Skip to content

Commit e7d3ae3

Browse files
committed
Shared CFG: fix nested and self-looping goto targets
1 parent 88df73a commit e7d3ae3

10 files changed

Lines changed: 152 additions & 63 deletions

File tree

‎csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ module Ast implements AstSig<Location> {
162162

163163
class Stmt = CS::Stmt;
164164

165+
class LabeledStmt extends Stmt {
166+
LabeledStmt() { none() }
167+
168+
Stmt getStmt() { none() }
169+
}
170+
165171
class Expr = CS::Expr;
166172

167173
class BlockStmt = CS::BlockStmt;

‎go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll‎

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ module CfgImpl {
186186

187187
class Stmt = Go::Stmt;
188188

189+
class LabeledStmt = Go::LabeledStmt;
190+
189191
class Expr = Go::Expr;
190192

191193
class BlockStmt extends Go::BlockStmt {
@@ -434,29 +436,23 @@ module CfgImpl {
434436
}
435437

436438
predicate hasLabel(Ast::AstNode n, Label l) {
437-
// A statement carries the label of every `LabeledStmt` that wraps it.
438-
// This is recursive because Go allows stacked labels (`L1: L2: stmt`),
439-
// which the extractor represents as nested `LabeledStmt`s, so a single
440-
// statement may have several labels.
441-
exists(Go::LabeledStmt ls | n = ls.getStmt() | l = ls.getLabel() or hasLabel(ls, l))
442-
or
443-
// The `LabeledStmt` wrapper itself also carries its label. Blocks contain
444-
// the wrapper (not the inner statement) as a direct child, so the shared
445-
// library's block-level `goto` target resolution -- which looks for a
446-
// labelled statement that is a direct child of a block -- matches on the
447-
// wrapper.
448439
l = n.(Go::LabeledStmt).getLabel()
449440
or
450441
l = n.(Go::BreakStmt).getLabel()
451442
or
452443
l = n.(Go::ContinueStmt).getLabel()
453444
or
454-
// A `goto` statement carries its target label, so that the shared
455-
// library's `beginAbruptCompletion` produces a *labelled* goto completion
456-
// (matching the target label) rather than an unlabelled one.
457445
l = n.(Go::GotoStmt).getLabel()
458446
}
459447

448+
private predicate hasLabelOrEnclosingLabel(Ast::AstNode n, Label l) {
449+
hasLabel(n, l)
450+
or
451+
exists(Go::LabeledStmt labeled |
452+
labeled.getStmt() = n and hasLabelOrEnclosingLabel(labeled, l)
453+
)
454+
}
455+
460456
predicate preOrderExpr(Ast::Expr e) {
461457
// The call of a `defer` statement is not invoked at the statement
462458
// itself; its callee expression and arguments are evaluated in place,
@@ -799,13 +795,6 @@ module CfgImpl {
799795
n.isAdditional(ast, "catch-return") and
800796
c.getSuccessorType() instanceof ReturnSuccessor
801797
or
802-
exists(Go::LabeledStmt lbl |
803-
ast = lbl.getStmt() and
804-
n.isAfter(lbl) and
805-
c.getSuccessorType() instanceof BreakSuccessor and
806-
c.hasLabel(lbl.getLabel())
807-
)
808-
or
809798
// A `break` in a communication clause body terminates the enclosing
810799
// `select` statement, continuing after it. This mirrors the shared
811800
// library's handling of `break` in a `switch` case body, but `select` is
@@ -823,7 +812,7 @@ module CfgImpl {
823812
|
824813
not c.hasLabel(_)
825814
or
826-
exists(Label l | c.hasLabel(l) and hasLabel(sel, l))
815+
exists(Label l | c.hasLabel(l) and hasLabelOrEnclosingLabel(sel, l))
827816
)
828817
or
829818
exists(Go::FuncDef fd |
@@ -835,18 +824,6 @@ module CfgImpl {
835824
exists(fd.getResultVar(0)) and
836825
n.isAdditional(fd.getBody(), "result-read:0")
837826
)
838-
or
839-
// Function bodies are excluded from `Ast::BlockStmt`, so handle goto
840-
// targets among their top-level statements here.
841-
exists(Go::FuncDef fd, Go::Stmt target, Label l |
842-
ast = fd.getBody() and
843-
target = fd.getBody().getAStmt() and
844-
not target instanceof Go::GotoStmt and
845-
hasLabel(target, l) and
846-
n.isBefore(target) and
847-
c.getSuccessorType() instanceof GotoSuccessor and
848-
c.hasLabel(l)
849-
)
850827
}
851828

852829
/** Holds if `ast` or one of its CFG children may panic. */

‎go/ql/test/library-tests/semmle/go/controlflow/GotoTarget/GotoTarget.expected‎

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import go
2+
import utils.test.InlineExpectationsTest
3+
4+
module GotoTargetTest implements TestSig {
5+
string getARelevantTag() { result = "gotoTarget" }
6+
7+
predicate hasActualResult(Location location, string element, string tag, string value) {
8+
exists(GotoStmt jump, LabeledStmt target, ControlFlow::Node source |
9+
jump.getLocation() = location and
10+
source.getAstNode() = jump and
11+
source.getASuccessor().getAstNode() = target and
12+
element = jump.toString() and
13+
tag = "gotoTarget" and
14+
value = target.getLabel()
15+
)
16+
}
17+
}
18+
19+
import MakeTest<GotoTargetTest>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package main
2+
3+
func gotoStackedSiblingTarget(flag bool) {
4+
if flag {
5+
goto inner // $ gotoTarget=inner
6+
}
7+
outer:
8+
inner:
9+
flag = false
10+
goto outer // $ gotoTarget=outer
11+
}
12+
13+
func gotoNestedSiblingTarget(flag bool) {
14+
if flag {
15+
goto inner // $ gotoTarget=inner
16+
} else {
17+
goto outer // $ gotoTarget=outer
18+
}
19+
outer:
20+
inner:
21+
{
22+
flag = false
23+
}
24+
}
25+
26+
func gotoSelfLoop(flag bool) {
27+
self:
28+
if flag {
29+
goto self // $ gotoTarget=self
30+
}
31+
}
32+
33+
func gotoDirectSelfLoop() {
34+
self:
35+
goto self // $ gotoTarget=self
36+
}
37+
38+
func gotoEnclosingStackedLabel(flag bool) {
39+
outer:
40+
inner:
41+
if flag {
42+
goto inner // $ gotoTarget=inner
43+
} else {
44+
goto outer // $ gotoTarget=outer
45+
}
46+
}

‎java/ql/lib/semmle/code/java/ControlFlowGraph.qll‎

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ private module Ast implements AstSig<Location> {
7070

7171
class Stmt = J::Stmt;
7272

73+
class LabeledStmt = J::LabeledStmt;
74+
7375
class Expr = J::Expr;
7476

7577
class BlockStmt = J::BlockStmt;
@@ -543,15 +545,8 @@ private module Input implements InputSig1, InputSig2 {
543545
}
544546
}
545547

546-
private Label getLabelOfLoop(Stmt s) {
547-
exists(LabeledStmt l | s = l.getStmt() |
548-
result = TJavaLabel(l.getLabel()) or
549-
result = getLabelOfLoop(l)
550-
)
551-
}
552-
553548
predicate hasLabel(Ast::AstNode n, Label l) {
554-
l = getLabelOfLoop(n)
549+
l = TJavaLabel(n.(LabeledStmt).getLabel())
555550
or
556551
l = TJavaLabel(n.(BreakStmt).getLabel())
557552
or
@@ -616,12 +611,7 @@ private module Input implements InputSig1, InputSig2 {
616611
* flow continuing at `n`.
617612
*/
618613
predicate endAbruptCompletion(Ast::AstNode ast, PreControlFlowNode n, AbruptCompletion c) {
619-
exists(LabeledStmt lbl |
620-
ast = lbl.getStmt() and
621-
n.isAfter(lbl) and
622-
c.getSuccessorType() instanceof BreakSuccessor and
623-
c.hasLabel(TJavaLabel(lbl.getLabel()))
624-
)
614+
none()
625615
}
626616

627617
/** Holds if there is a local non-abrupt step from `n1` to `n2`. */

‎python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ module Ast implements AstSig<Py::Location> {
231231
override Callable getEnclosingCallable() { result.asScope() = this.asStmt().getScope() }
232232
}
233233

234+
class LabeledStmt extends Stmt {
235+
LabeledStmt() { none() }
236+
237+
Stmt getStmt() { none() }
238+
}
239+
234240
/** An expression. */
235241
class Expr extends AstNodeImpl, TExpr {
236242
// For `TPyExpr` instances, delegate to the wrapped Python expression.

‎ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,12 @@ private module Ast implements AstSig<Location> {
308308

309309
class ContinueStmt extends Stmt instanceof R::Ast::NextStmt { }
310310

311+
class LabeledStmt extends Stmt {
312+
LabeledStmt() { none() }
313+
314+
Stmt getStmt() { none() }
315+
}
316+
311317
class GotoStmt extends Stmt {
312318
GotoStmt() { none() }
313319
}

‎shared/controlflow/codeql/controlflow/ControlFlowGraph.qll‎

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ signature module AstSig<LocationSig Location> {
7171
/** A statement. */
7272
class Stmt extends AstNode;
7373

74+
/** A labeled statement. */
75+
class LabeledStmt extends Stmt {
76+
/** Gets the statement carrying the label. */
77+
Stmt getStmt();
78+
}
79+
7480
/** An expression. */
7581
class Expr extends AstNode;
7682

@@ -439,10 +445,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
439445
string toString();
440446
}
441447

442-
/**
443-
* Holds if the node `n` has the label `l`. For example, a label in a goto
444-
* statement or a goto target.
445-
*/
448+
/** Holds if the node `n` directly has the label `l`. */
446449
default predicate hasLabel(AstNode n, Label l) { none() }
447450

448451
/**
@@ -1282,9 +1285,23 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
12821285
)
12831286
}
12841287

1285-
private Stmt getAStmtInBlock(AstNode block) {
1286-
result = block.(BlockStmt).getStmt(_) or
1287-
result = block.(Switch).getStmt(_)
1288+
/** Holds if `n` has `l`, possibly through enclosing labeled statements. */
1289+
private predicate hasLabel(AstNode n, Input1::Label l) {
1290+
Input1::hasLabel(n, l)
1291+
or
1292+
exists(LabeledStmt labeled | labeled.getStmt() = n and hasLabel(labeled, l))
1293+
}
1294+
1295+
/**
1296+
* Holds if `target` is a labeled statement at the start of `root`,
1297+
* possibly nested under other labeled statements.
1298+
*/
1299+
private predicate labeledTargetInRoot(Stmt root, LabeledStmt target) {
1300+
root = target
1301+
or
1302+
exists(LabeledStmt labeled |
1303+
root = labeled and labeledTargetInRoot(labeled.getStmt(), target)
1304+
)
12881305
}
12891306

12901307
private predicate callableHasParamDefault(Callable c, Expr defaultValue) {
@@ -1325,7 +1342,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
13251342
or
13261343
exists(Input1::Label l |
13271344
c.hasLabel(l) and
1328-
Input1::hasLabel(loop, l)
1345+
hasLabel(loop, l)
13291346
)
13301347
)
13311348
)
@@ -1365,16 +1382,32 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
13651382
or
13661383
exists(Input1::Label l |
13671384
c.hasLabel(l) and
1368-
Input1::hasLabel(switch, l)
1385+
hasLabel(switch, l)
13691386
)
13701387
)
13711388
or
1372-
exists(AstNode block, Input1::Label l, Stmt lblstmt |
1373-
ast = getAStmtInBlock(block) and
1374-
lblstmt = getAStmtInBlock(block) and
1375-
not lblstmt instanceof GotoStmt and
1376-
Input1::hasLabel(pragma[only_bind_into](lblstmt), l) and
1377-
n.isBefore(lblstmt) and
1389+
exists(LabeledStmt target, Input1::Label l |
1390+
ast = target.getStmt() and
1391+
Input1::hasLabel(target, l) and
1392+
n.isAfter(target) and
1393+
c.getSuccessorType() instanceof BreakSuccessor and
1394+
c.hasLabel(l)
1395+
)
1396+
or
1397+
exists(AstNode parent, Stmt root, LabeledStmt target, Input1::Label l |
1398+
ast = getChild(parent, _) and
1399+
root = getChild(parent, _) and
1400+
labeledTargetInRoot(root, target) and
1401+
Input1::hasLabel(target, l) and
1402+
n.isBefore(target) and
1403+
c.getSuccessorType() instanceof GotoSuccessor and
1404+
c.hasLabel(l)
1405+
)
1406+
or
1407+
exists(LabeledStmt target, Input1::Label l |
1408+
ast = target.getStmt() and
1409+
Input1::hasLabel(target, l) and
1410+
n.isBefore(target) and
13781411
c.getSuccessorType() instanceof GotoSuccessor and
13791412
c.hasLabel(l)
13801413
)

‎unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ module Ast implements AstSig<Location> {
130130

131131
class ContinueStmt = U::ContinueExpr;
132132

133+
class LabeledStmt extends Stmt {
134+
LabeledStmt() { none() }
135+
136+
Stmt getStmt() { none() }
137+
}
138+
133139
class GotoStmt extends Stmt {
134140
GotoStmt() { none() }
135141
}

0 commit comments

Comments
 (0)