Skip to content

Commit ea619de

Browse files
Claudeowen-mc
andcommitted
Go: migrate simple 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 7b2e075 commit ea619de

7 files changed

Lines changed: 54 additions & 47 deletions

File tree

go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ overlay[local?]
22
module;
33

44
private import go
5+
private import semmle.go.controlflow.Guards
56
private import DataFlowUtil
67
private import DataFlowImplCommon
78
private import ContainerFlow
@@ -390,19 +391,21 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode {
390391
}
391392

392393
/**
393-
* Returns a guard that will certainly not hold in calling context `call`.
394+
* Holds if `guard` evaluating to `branch` will certainly not happen in calling
395+
* context `call`.
394396
*
395397
* In particular it does not hold because it checks that `param` has value `b`, but
396398
* in context `call` it is known to have value `!b`. Note this is `noinline`d in order
397399
* to avoid a bad join order in `isUnreachableInCall`.
398400
*/
399401
pragma[noinline]
400-
private ControlFlow::ConditionGuardNode getAFalsifiedGuard(DataFlowCall call) {
402+
private predicate falsifiedGuard(DataFlowCall call, Guard guard, boolean branch) {
401403
exists(SsaParameterNode param, ConstantBooleanArgumentNode arg |
402404
// get constant bool argument and parameter for this call
403405
viableParamArg(call, pragma[only_bind_into](param), pragma[only_bind_into](arg)) and
404406
// which is used in a guard controlling `n` with the opposite value of `arg`
405-
result.ensures(param.getAUse(), arg.getBooleanValue().booleanNot())
407+
guard = param.getAUse().asExpr() and
408+
branch = arg.getBooleanValue().booleanNot()
406409
)
407410
}
408411

@@ -416,7 +419,10 @@ class NodeRegion instanceof BasicBlock {
416419
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
417420
*/
418421
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) {
419-
getAFalsifiedGuard(call).dominates(nr)
422+
exists(Guard guard, boolean branch |
423+
falsifiedGuard(call, guard, branch) and
424+
guard.controls(nr, branch)
425+
)
420426
}
421427

422428
/**

go/ql/lib/semmle/go/dataflow/internal/DataFlowUtil.qll

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ overlay[local?]
55
module;
66

77
private import go
8+
private import semmle.go.controlflow.Guards
89
private import semmle.go.dataflow.FunctionInputsAndOutputs
910
private import semmle.go.dataflow.ExternalFlow
1011
private import DataFlowPrivate
@@ -388,51 +389,47 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
388389
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
389390
/** Gets a node that is safely guarded by the given guard check. */
390391
Node getABarrierNode(P param) {
391-
exists(ControlFlow::ConditionGuardNode guard, SsaWithFields var |
392+
exists(Guard guard, boolean branch, SsaWithFields var |
392393
result = pragma[only_bind_out](var).getAUse()
393394
|
394-
guards(_, guard, _, var, param) and
395-
pragma[only_bind_out](guard).dominates(result.getBasicBlock())
395+
guards(_, guard, branch, _, var, param) and
396+
pragma[only_bind_out](guard).controls(result.getBasicBlock(), branch)
396397
)
397398
}
398399

399400
/**
400401
* Gets a node that is safely guarded by the given guard check.
401402
*/
402403
Node getABarrierNodeForGuard(Node guardCheck, P param) {
403-
exists(ControlFlow::ConditionGuardNode guard, SsaWithFields var | result = var.getAUse() |
404-
guards(guardCheck, guard, _, var, param) and
405-
guard.dominates(result.getBasicBlock())
404+
exists(Guard guard, boolean branch, SsaWithFields var | result = var.getAUse() |
405+
guards(guardCheck, guard, branch, _, var, param) and
406+
guard.controls(result.getBasicBlock(), branch)
406407
)
407408
}
408409

409410
/**
410-
* Holds if `guard` marks a point in the control-flow graph where `g`
411-
* is known to validate `nd`, which is represented by `ap`.
411+
* Holds if `guard` evaluating to `branch` marks a point in the control-flow
412+
* graph where `g` is known to validate `nd`, which is represented by `ap`.
412413
*
413414
* This predicate exists to enforce a good join order in `getAGuardedNode`.
414415
*/
415416
pragma[noinline]
416-
private predicate guards(
417-
Node g, ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields ap, P param
418-
) {
419-
guards(g, guard, nd, param) and nd = ap.getAUse()
417+
private predicate guards(Node g, Guard guard, boolean branch, Node nd, SsaWithFields ap, P param) {
418+
guards(g, guard, branch, nd, param) and nd = ap.getAUse()
420419
}
421420

422421
/**
423-
* Holds if `guard` marks a point in the control-flow graph where `g`
424-
* is known to validate `nd`.
422+
* Holds if `guard` evaluating to `branch` marks a point in the control-flow
423+
* graph where `g` is known to validate `nd`.
425424
*/
426-
private predicate guards(Node g, ControlFlow::ConditionGuardNode guard, Node nd, P param) {
427-
exists(boolean branch |
428-
guardChecks(g, nd.asExpr(), branch, param) and
429-
guard.ensures(g, branch)
430-
)
425+
private predicate guards(Node g, Guard guard, boolean branch, Node nd, P param) {
426+
guardChecks(g, nd.asExpr(), branch, param) and
427+
guard = g.asExpr()
431428
or
432-
exists(DataFlow::Property p, Node resNode, Node check, boolean outcome |
429+
exists(DataFlow::Property p, Node resNode, Node check |
433430
guardingCall(g, _, _, _, p, _, nd, resNode, param) and
434-
p.checkOn(check, outcome, resNode) and
435-
guard.ensures(pragma[only_bind_into](check), outcome)
431+
p.checkOn(check, branch, resNode) and
432+
guard = pragma[only_bind_into](check).asExpr()
436433
)
437434
}
438435

@@ -487,9 +484,9 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
487484
localFlow(inp.getExitNode(fd), pragma[only_bind_out](arg)) and
488485
(
489486
// Case: a function like "if someBarrierGuard(arg) { return true } else { return false }"
490-
exists(ControlFlow::ConditionGuardNode guard |
491-
guards(g, pragma[only_bind_out](guard), arg, param) and
492-
guard.dominates(pragma[only_bind_out](ret).getBasicBlock())
487+
exists(Guard guard, boolean branch |
488+
guards(g, pragma[only_bind_out](guard), branch, arg, param) and
489+
guard.controls(pragma[only_bind_out](ret).getBasicBlock(), branch)
493490
|
494491
onlyPossibleReturnSatisfyingProperty(fd, outp, ret, p)
495492
)

go/ql/lib/semmle/go/security/InsecureFeatureFlag.qll

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import go
6+
private import semmle.go.controlflow.Guards
67

78
/**
89
* Provides classes and predicates relating to flags that may indicate security expectations.
@@ -114,9 +115,9 @@ module InsecureFeatureFlag {
114115
}
115116

116117
/**
117-
* Gets a control-flow node that represents a (likely) security feature-flag check
118+
* Gets a guard that represents a (likely) security feature-flag check.
118119
*/
119-
ControlFlow::ConditionGuardNode getASecurityFeatureFlagCheck() {
120-
result.ensures(any(SecurityFeatureFlag f).getAFlag().getANode(), _)
120+
Guard getASecurityFeatureFlagCheck() {
121+
result = any(SecurityFeatureFlag f).getAFlag().getANode().asExpr()
121122
}
122123
}

go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.ql

Lines changed: 4 additions & 3 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
/**
1819
* Holds if `pattern` is a regular expression pattern for URLs with a host matched by `hostPart`,
@@ -70,12 +71,12 @@ predicate regexpGuardsHandler(RegexpPattern regexp, Http::RequestHandler handler
7071

7172
/** Holds if `regexp` guards an HTTP error write. */
7273
predicate regexpGuardsError(RegexpPattern regexp) {
73-
exists(ControlFlow::ConditionGuardNode cond, RegexpMatchFunction match, DataFlow::CallNode call |
74+
exists(Guard cond, RegexpMatchFunction match, DataFlow::CallNode call |
7475
call.getTarget() = match and
7576
match.getRegexp(call) = regexp
7677
|
77-
cond.ensures(match.getResult().getNode(call).getASuccessor*(), true) and
78-
cond.dominates(any(ReachableBasicBlock b | writesHttpError(b)))
78+
cond = match.getResult().getNode(call).getASuccessor*().asExpr() and
79+
cond.controls(any(ReachableBasicBlock b | writesHttpError(b)), true)
7980
)
8081
}
8182

go/ql/src/Security/CWE-209/StackTraceExposure.ql

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import go
1717
import semmle.go.security.InsecureFeatureFlag::InsecureFeatureFlag
18+
private import semmle.go.controlflow.Guards
1819

1920
/**
2021
* A flag indicating the program is in debug or development mode, or that stack
@@ -56,10 +57,8 @@ module StackTraceExposureConfig implements DataFlow::ConfigSig {
5657
// Sanitize everything controlled by an is-debug-mode check.
5758
// Imprecision: I don't try to guess which arm of a branch is intended
5859
// to mean debug mode, and which is production mode.
59-
exists(ControlFlow::ConditionGuardNode cgn |
60-
cgn.ensures(any(DebugModeFlag f).getAFlag().getANode(), _)
61-
|
62-
cgn.dominates(node.getBasicBlock())
60+
exists(Guard g | g = any(DebugModeFlag f).getAFlag().getANode().asExpr() |
61+
g.controls(node.getBasicBlock(), _)
6362
)
6463
}
6564

go/ql/src/Security/CWE-295/DisabledCertificateCheck.ql

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import go
2626
import semmle.go.security.InsecureFeatureFlag::InsecureFeatureFlag
27+
private import semmle.go.controlflow.Guards
2728

2829
/**
2930
* Holds if `part` becomes a part of `whole`, either by (local) data flow or by being incorporated
@@ -50,10 +51,10 @@ class InsecureCertificateFlag extends FlagKind {
5051
}
5152

5253
/**
53-
* Gets a control-flow node that represents a (likely) flag controlling an insecure certificate setup.
54+
* Gets a guard that represents a (likely) flag controlling an insecure certificate setup.
5455
*/
55-
ControlFlow::ConditionGuardNode getAnInsecureCertificateCheck() {
56-
result.ensures(any(InsecureCertificateFlag f).getAFlag().getANode(), _)
56+
Guard getAnInsecureCertificateCheck() {
57+
result = any(InsecureCertificateFlag f).getAFlag().getANode().asExpr()
5758
}
5859

5960
/**
@@ -80,7 +81,8 @@ where
8081
f.hasQualifiedName("crypto/tls", "Config", "InsecureSkipVerify") and
8182
rhs.getBoolValue() = true and
8283
// exclude writes guarded by a feature flag
83-
not [getASecurityFeatureFlagCheck(), getAnInsecureCertificateCheck()].dominatesNode(w) and
84+
not [getASecurityFeatureFlagCheck(), getAnInsecureCertificateCheck()]
85+
.controls(w.getBasicBlock(), _) and
8486
// exclude results in functions whose name documents the insecurity
8587
not exists(FuncDef fn | fn = w.getRoot() |
8688
isSecurityOrCertificateConfigFlag(fn.getEnclosingFunction*().getName())

go/ql/src/Security/CWE-327/InsecureTLS.ql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import go
1515
import semmle.go.security.InsecureFeatureFlag::InsecureFeatureFlag
16+
private import semmle.go.controlflow.Guards
1617

1718
/**
1819
* Holds if it is insecure to assign TLS version `val` named `name` to `tls.Config` field `fieldName`.
@@ -247,10 +248,10 @@ class LegacyTlsVersionFlag extends FlagKind {
247248
}
248249

249250
/**
250-
* Gets a control-flow node that represents a (likely) flag controlling TLS version selection.
251+
* Gets a guard that represents a (likely) flag controlling TLS version selection.
251252
*/
252-
ControlFlow::ConditionGuardNode getALegacyTlsVersionCheck() {
253-
result.ensures(any(LegacyTlsVersionFlag f).getAFlag().getANode(), _)
253+
Guard getALegacyTlsVersionCheck() {
254+
result = any(LegacyTlsVersionFlag f).getAFlag().getANode().asExpr()
254255
}
255256

256257
/**
@@ -276,7 +277,7 @@ where
276277
) and
277278
// Exclude sources or sinks guarded by a feature or legacy flag
278279
not [getASecurityFeatureFlagCheck(), getALegacyTlsVersionCheck()]
279-
.dominatesNode([source, sink].getNode().asInstruction()) and
280+
.controls([source, sink].getNode().getBasicBlock(), _) and
280281
// Exclude sources or sinks that occur lexically within a block related to a feature or legacy flag
281282
not astNodeIsFlag([source, sink].getNode().asExpr().getParent*(), securityOrTlsVersionFlag()) and
282283
// Exclude results in functions whose name documents insecurity

0 commit comments

Comments
 (0)