Skip to content

Commit 19d7fbf

Browse files
Claudeowen-mc
andcommitted
Go: migrate remaining ConditionGuardNode uses to the shared guards library
Agent-Logs-Url: https://github.com/github/codeql/sessions/d505ffcf-a693-4b67-9dc3-54fa92e27896 Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
1 parent ea619de commit 19d7fbf

6 files changed

Lines changed: 84 additions & 55 deletions

File tree

go/ql/lib/semmle/go/controlflow/Guards.qll

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,31 @@ module ValidationWrapper<GuardsLogic::guardChecksSig/3 guardChecks> {
317317
*/
318318
pragma[inline]
319319
predicate guardEnsures(Expr e, boolean b, BasicBlock bb) { e.(Guard).controls(bb, b) }
320+
321+
/** Holds if `guard` evaluating to `branch` ensures that `i = j` holds. */
322+
predicate guardEnsuresEq(Guard guard, boolean branch, DataFlow::Node i, DataFlow::Node j) {
323+
guard.isEquality(i.asExpr(), j.asExpr(), branch)
324+
}
325+
326+
/** Holds if `guard` evaluating to `branch` ensures that `i != j` holds. */
327+
predicate guardEnsuresNeq(Guard guard, boolean branch, DataFlow::Node i, DataFlow::Node j) {
328+
exists(boolean eqval |
329+
guard.isEquality(i.asExpr(), j.asExpr(), eqval) and
330+
branch = eqval.booleanNot()
331+
)
332+
}
333+
334+
/**
335+
* Holds if `guard` evaluating to `branch` ensures that `lesser <= greater + bias`
336+
* holds.
337+
*/
338+
predicate guardEnsuresLeq(
339+
Guard guard, boolean branch, DataFlow::Node lesser, DataFlow::Node greater, int bias
340+
) {
341+
exists(DataFlow::RelationalComparisonNode rel |
342+
guard = rel.asExpr() and
343+
rel.leq(branch, lesser, greater, bias)
344+
)
345+
or
346+
guardEnsuresEq(guard, branch, lesser, greater) and bias = 0
347+
}

go/ql/src/InconsistentCode/ConstantLengthComparison.ql

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
*/
1414

1515
import go
16+
private import semmle.go.controlflow.Guards
1617

1718
from
18-
ForStmt fs, Variable i, DataFlow::ElementReadNode idx, GVN a,
19-
ControlFlow::ConditionGuardNode cond, DataFlow::CallNode lenA
19+
ForStmt fs, Variable i, DataFlow::ElementReadNode idx, GVN a, Guard cond, boolean branch,
20+
DataFlow::CallNode lenA
2021
where
2122
// `i` is incremented in `fs`
2223
fs.getPost().(IncStmt).getOperand() = i.getAReference() and
@@ -27,11 +28,11 @@ where
2728
lenA.getArgument(0) = a.getANode() and
2829
// and is checked against a constant
2930
exists(DataFlow::Node const | exists(const.getIntValue()) |
30-
cond.ensuresNeq(lenA, const) or
31-
cond.ensuresLeq(const, lenA, _)
31+
guardEnsuresNeq(cond, branch, lenA, const) or
32+
guardEnsuresLeq(cond, branch, const, lenA, _)
3233
) and
33-
cond.dominates(idx.getBasicBlock()) and
34+
cond.controls(idx.getBasicBlock(), branch) and
3435
// and that check happens inside the loop body
35-
cond.getCondition().getParent+() = fs
36-
select cond.getCondition(), "This checks the length against a constant, but it $@.", idx,
36+
cond.(Expr).getParent+() = fs
37+
select cond, "This checks the length against a constant, but it $@.", idx,
3738
"is indexed using a variable"

go/ql/src/InconsistentCode/LengthComparisonOffByOne.ql

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414

1515
import go
16+
private import semmle.go.controlflow.Guards
1617

1718
newtype TIndex =
1819
VariableIndex(DataFlow::SsaNode v) { v.getAUse() = any(DataFlow::ElementReadNode e).getIndex() } or
@@ -41,21 +42,23 @@ DataFlow::CallNode arrayLen(DataFlow::SsaNode array) {
4142
}
4243

4344
/**
44-
* Gets a condition that checks that `index` is less than or equal to `array.length`.
45+
* Holds if `guard` evaluating to `branch` checks that `index` is less than or
46+
* equal to `array.length`.
4547
*/
46-
ControlFlow::ConditionGuardNode getLengthLEGuard(Index index, DataFlow::SsaNode array) {
47-
result.ensuresLeq(getAUse(index), arrayLen(array), 0)
48+
predicate lengthLeGuard(Guard guard, boolean branch, Index index, DataFlow::SsaNode array) {
49+
guardEnsuresLeq(guard, branch, getAUse(index), arrayLen(array), 0)
4850
or
4951
exists(int i, int bias | index = ConstantIndex(i) |
50-
result.ensuresLeq(getAUse(ConstantIndex(i + bias)), arrayLen(array), bias)
52+
guardEnsuresLeq(guard, branch, getAUse(ConstantIndex(i + bias)), arrayLen(array), bias)
5153
)
5254
}
5355

5456
/**
55-
* Gets a condition that checks that `index` is not equal to `array.length`.
57+
* Holds if `guard` evaluating to `branch` checks that `index` is not equal to
58+
* `array.length`.
5659
*/
57-
ControlFlow::ConditionGuardNode getLengthNEGuard(Index index, DataFlow::SsaNode array) {
58-
result.ensuresNeq(getAUse(index), arrayLen(array))
60+
predicate lengthNeGuard(Guard guard, boolean branch, Index index, DataFlow::SsaNode array) {
61+
guardEnsuresNeq(guard, branch, getAUse(index), arrayLen(array))
5962
}
6063

6164
/**
@@ -78,23 +81,24 @@ predicate isRegexpMethodCall(DataFlow::MethodCallNode c) {
7881
}
7982

8083
from
81-
ControlFlow::ConditionGuardNode cond, DataFlow::SsaNode array, Index index,
82-
DataFlow::ElementReadNode ea, BasicBlock bb
84+
Guard cond, boolean branch, DataFlow::SsaNode array, Index index, DataFlow::ElementReadNode ea,
85+
BasicBlock bb
8386
where
8487
// there is a comparison `index <= len(array)`
85-
cond = getLengthLEGuard(index, array) and
88+
lengthLeGuard(cond, branch, index, array) and
8689
// there is a read from `array[index]`
8790
elementRead(ea, array, index, bb) and
8891
// and the read is guarded by the comparison
89-
cond.dominates(bb) and
92+
cond.controls(bb, branch) and
9093
// but the read is not guarded by another check that `index != len(array)`
91-
not getLengthNEGuard(index, array).dominates(bb) and
94+
not exists(Guard ne, boolean neBranch |
95+
lengthNeGuard(ne, neBranch, index, array) and ne.controls(bb, neBranch)
96+
) and
9297
// and it is not additionally guarded by a stronger index check
93-
not exists(Index index2, int i, int i2 |
98+
not exists(Index index2, int i, int i2, Guard g2, boolean b2 |
9499
index = ConstantIndex(i) and index2 = ConstantIndex(i2) and i < i2
95100
|
96-
getLengthLEGuard(index2, array).dominates(bb)
101+
lengthLeGuard(g2, b2, index2, array) and g2.controls(bb, b2)
97102
) and
98103
not isRegexpMethodCall(array.getInit())
99-
select cond.getCondition(),
100-
"Off-by-one index comparison against length may lead to out-of-bounds $@.", ea, "read"
104+
select cond, "Off-by-one index comparison against length may lead to out-of-bounds $@.", ea, "read"

go/ql/src/experimental/CWE-807/SensitiveConditionBypass.ql

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,18 @@
1414

1515
import go
1616
import SensitiveConditionBypass
17+
private import semmle.go.controlflow.Guards
1718

1819
from
19-
ControlFlow::ConditionGuardNode guard, DataFlow::Node sensitiveSink,
20-
SensitiveExpr::Classification classification, DataFlow::Node source, DataFlow::Node operand,
21-
ComparisonExpr comp
20+
DataFlow::Node sensitiveSink, SensitiveExpr::Classification classification, DataFlow::Node source,
21+
DataFlow::Node operand, ComparisonExpr comp
2222
where
2323
// there should be a flow between source and the operand sink
2424
Flow::flow(source, operand) and
2525
// both the operand should belong to the same comparison expression
2626
operand.asExpr() = comp.getAnOperand() and
27-
// get the ConditionGuardNode corresponding to the comparison expr.
28-
guard.getCondition() = comp and
2927
// the sink `sensitiveSink` should be sensitive,
3028
isSensitive(sensitiveSink, classification) and
31-
// the guard should control the sink
32-
guard.dominates(sensitiveSink.getBasicBlock())
29+
// the comparison should control the sink
30+
comp.(Guard).controls(sensitiveSink.getBasicBlock(), _)
3331
select comp, "This sensitive comparision check can potentially be bypassed."

go/ql/src/experimental/CWE-942/CorsMisconfiguration.ql

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import go
1616
import semmle.go.security.InsecureFeatureFlag::InsecureFeatureFlag
17+
private import semmle.go.controlflow.Guards
1718

1819
/**
1920
* A flag indicating a check for satisfied permissions or test configuration.
@@ -59,10 +60,8 @@ module UntrustedToAllowOriginHeaderConfig implements DataFlow::ConfigSig {
5960
}
6061

6162
predicate isBarrier(DataFlow::Node node) {
62-
exists(ControlFlow::ConditionGuardNode cgn |
63-
cgn.ensures(any(AllowedFlag f).getAFlag().getANode(), _)
64-
|
65-
cgn.dominates(node.getBasicBlock())
63+
exists(Guard g | g = any(AllowedFlag f).getAFlag().getANode().asExpr() |
64+
g.controls(node.getBasicBlock(), _)
6665
)
6766
}
6867

@@ -173,7 +172,7 @@ module FromUntrustedConfig implements DataFlow::ConfigSig {
173172

174173
predicate isSink(DataFlow::Node sink) { isSinkCgn(sink, _) }
175174

176-
additional predicate isSinkCgn(DataFlow::Node sink, ControlFlow::ConditionGuardNode cgn) {
175+
additional predicate isSinkCgn(DataFlow::Node sink, Guard guard) {
177176
exists(IfStmt ifs |
178177
exists(Expr operand |
179178
operand = ifs.getCond().getAChildExpr*() and
@@ -202,7 +201,7 @@ module FromUntrustedConfig implements DataFlow::ConfigSig {
202201
)
203202
)
204203
|
205-
cgn.getCondition() = ifs.getCond()
204+
guard = ifs.getCond()
206205
)
207206
}
208207
}
@@ -217,10 +216,10 @@ module FromUntrustedFlow = TaintTracking::Global<FromUntrustedConfig>;
217216
* Holds if the provided `allowOriginHW` is also destination of a `ActiveThreatModelSource`.
218217
*/
219218
predicate flowsToGuardedByCheckOnUntrusted(DataFlow::ExprNode allowOriginHW) {
220-
exists(DataFlow::Node sink, ControlFlow::ConditionGuardNode cgn |
221-
FromUntrustedFlow::flowTo(sink) and FromUntrustedConfig::isSinkCgn(sink, cgn)
219+
exists(DataFlow::Node sink, Guard guard |
220+
FromUntrustedFlow::flowTo(sink) and FromUntrustedConfig::isSinkCgn(sink, guard)
222221
|
223-
cgn.dominates(allowOriginHW.getBasicBlock())
222+
guard.controls(allowOriginHW.getBasicBlock(), _)
224223
)
225224
}
226225

@@ -233,9 +232,7 @@ where
233232
allowOriginIsNull(allowOriginHW, message)
234233
) and
235234
not flowsToGuardedByCheckOnUntrusted(allowOriginHW) and
236-
not exists(ControlFlow::ConditionGuardNode cgn |
237-
cgn.ensures(any(AllowedFlag f).getAFlag().getANode(), _)
238-
|
239-
cgn.dominates(allowOriginHW.getBasicBlock())
235+
not exists(Guard g | g = any(AllowedFlag f).getAFlag().getANode().asExpr() |
236+
g.controls(allowOriginHW.getBasicBlock(), _)
240237
)
241238
select allowOriginHW, message

go/ql/src/experimental/IntegerOverflow/RangeAnalysis.qll

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import go
2+
private import semmle.go.controlflow.Guards
23

34
Expr getAUse(SsaDefinition def) {
45
result = def.getVariable().getAUse().(IR::EvalInstruction).getExpr()
@@ -46,12 +47,12 @@ float getAnUpperBound(Expr expr) {
4647
if
4748
//if a condition expression exists before and one of the operand happens to be the identifier, we use this condition expression to narrow down the range.
4849
exists(
49-
ControlFlow::ConditionGuardNode n, DataFlow::Node lesser, DataFlow::Node greater,
50+
Guard n, boolean branch, DataFlow::Node lesser, DataFlow::Node greater,
5051
ReachableBasicBlock bb
5152
|
52-
n.ensuresLeq(lesser, greater, _) and
53+
guardEnsuresLeq(n, branch, lesser, greater, _) and
5354
IR::evalExprInstruction(lesser.asExpr()) = v.getAUse() and
54-
n.dominates(bb) and
55+
n.controls(bb, branch) and
5556
bb.getANode() = IR::evalExprInstruction(identifier) and
5657
not exists(Expr e |
5758
e = v.getAUse().(IR::EvalInstruction).getExpr() and
@@ -60,12 +61,12 @@ float getAnUpperBound(Expr expr) {
6061
)
6162
then
6263
exists(
63-
ControlFlow::ConditionGuardNode n, ReachableBasicBlock bb, DataFlow::Node lesser,
64+
Guard n, boolean branch, ReachableBasicBlock bb, DataFlow::Node lesser,
6465
DataFlow::Node greater, int bias
6566
|
66-
n.dominates(bb) and
67+
n.controls(bb, branch) and
6768
bb.getANode() = IR::evalExprInstruction(identifier) and
68-
n.ensuresLeq(lesser, greater, bias) and
69+
guardEnsuresLeq(n, branch, lesser, greater, bias) and
6970
v.getAUse() = IR::evalExprInstruction(lesser.asExpr()) and
7071
not exists(Expr e |
7172
e = v.getAUse().(IR::EvalInstruction).getExpr() and
@@ -202,12 +203,12 @@ float getALowerBound(Expr expr) {
202203
//if exists a condition expression before this identifier
203204
if
204205
exists(
205-
ControlFlow::ConditionGuardNode n, DataFlow::Node greater, DataFlow::Node lesser,
206+
Guard n, boolean branch, DataFlow::Node greater, DataFlow::Node lesser,
206207
ReachableBasicBlock bb
207208
|
208-
n.ensuresLeq(lesser, greater, _) and
209+
guardEnsuresLeq(n, branch, lesser, greater, _) and
209210
IR::evalExprInstruction(greater.asExpr()) = v.getAUse() and
210-
n.dominates(bb) and
211+
n.controls(bb, branch) and
211212
bb.getANode() = IR::evalExprInstruction(identifier) and
212213
not exists(Expr e |
213214
e = v.getAUse().(IR::EvalInstruction).getExpr() and
@@ -216,12 +217,12 @@ float getALowerBound(Expr expr) {
216217
)
217218
then
218219
exists(
219-
ControlFlow::ConditionGuardNode n, ReachableBasicBlock bb, DataFlow::Node lesser,
220+
Guard n, boolean branch, ReachableBasicBlock bb, DataFlow::Node lesser,
220221
DataFlow::Node greater, int bias, float lbs
221222
|
222-
n.dominates(bb) and
223+
n.controls(bb, branch) and
223224
bb.getANode() = IR::evalExprInstruction(identifier) and
224-
n.ensuresLeq(lesser, greater, bias) and
225+
guardEnsuresLeq(n, branch, lesser, greater, bias) and
225226
v.getAUse() = IR::evalExprInstruction(greater.asExpr()) and
226227
not exists(Expr e |
227228
e = v.getAUse().(IR::EvalInstruction).getExpr() and

0 commit comments

Comments
 (0)