🌐 AI搜索 & 代理 主页
Skip to content

Commit 10a0530

Browse files
committed
Go: Add support for MaD barriers and barrier guards.
1 parent 4b2e8c0 commit 10a0530

File tree

3 files changed

+128
-19
lines changed

3 files changed

+128
-19
lines changed

go/ql/lib/semmle/go/dataflow/ExternalFlow.qll

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ private predicate elementSpec(
320320
or
321321
sinkModel(package, type, subtypes, name, signature, ext, _, _, _, _)
322322
or
323+
barrierModel(package, type, subtypes, name, signature, ext, _, _, _, _)
324+
or
325+
barrierGuardModel(package, type, subtypes, name, signature, ext, _, _, _, _, _)
326+
or
323327
summaryModel(package, type, subtypes, name, signature, ext, _, _, _, _, _)
324328
or
325329
neutralModel(package, type, name, signature, _, _) and ext = "" and subtypes = false
@@ -455,6 +459,54 @@ private module Cached {
455459
isSinkNode(n, kind, model) and n.asNode() = node
456460
)
457461
}
462+
463+
private newtype TKindModelPair =
464+
TMkPair(string kind, string model) { isBarrierGuardNode(_, _, kind, model) }
465+
466+
private boolean convertAcceptingValue(Public::AcceptingValue av) {
467+
av.isTrue() and result = true
468+
or
469+
av.isFalse() and result = false
470+
// Remaining cases are not supported yet, they depend on the shared Guards library.
471+
// or
472+
// av.isNoException() and result.getDualValue().isThrowsException()
473+
// or
474+
// av.isZero() and result.asIntValue() = 0
475+
// or
476+
// av.isNotZero() and result.getDualValue().asIntValue() = 0
477+
// or
478+
// av.isNull() and result.isNullValue()
479+
// or
480+
// av.isNotNull() and result.isNonNullValue()
481+
}
482+
483+
private predicate barrierGuardChecks(DataFlow::Node g, Expr e, boolean gv, TKindModelPair kmp) {
484+
exists(
485+
SourceSinkInterpretationInput::InterpretNode n, Public::AcceptingValue acceptingvalue,
486+
string kind, string model
487+
|
488+
isBarrierGuardNode(n, acceptingvalue, kind, model) and
489+
n.asNode().asExpr() = e and
490+
kmp = TMkPair(kind, model) and
491+
gv = convertAcceptingValue(acceptingvalue)
492+
|
493+
g.asExpr().(CallExpr).getAnArgument() = e // TODO: qualifier?
494+
)
495+
}
496+
497+
/**
498+
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
499+
* model.
500+
*/
501+
cached
502+
predicate barrierNode(DataFlow::Node node, string kind, string model) {
503+
exists(SourceSinkInterpretationInput::InterpretNode n |
504+
isBarrierNode(n, kind, model) and n.asNode() = node
505+
)
506+
or
507+
DataFlow::ParameterizedBarrierGuard<TKindModelPair, barrierGuardChecks/4>::getABarrierNode(TMkPair(kind,
508+
model)) = node
509+
}
458510
}
459511

460512
import Cached
@@ -471,6 +523,12 @@ predicate sourceNode(DataFlow::Node node, string kind) { sourceNode(node, kind,
471523
*/
472524
predicate sinkNode(DataFlow::Node node, string kind) { sinkNode(node, kind, _) }
473525

526+
/**
527+
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
528+
* model.
529+
*/
530+
predicate barrierNode(DataFlow::Node node, string kind) { barrierNode(node, kind, _) }
531+
474532
// adapter class for converting Mad summaries to `SummarizedCallable`s
475533
private class SummarizedCallableAdapter extends Public::SummarizedCallable {
476534
SummarizedCallableAdapter() { summaryElement(this, _, _, _, _, _) }

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

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -339,29 +339,67 @@ class ContentSet instanceof TContentSet {
339339
*/
340340
signature predicate guardChecksSig(Node g, Expr e, boolean branch);
341341

342+
bindingset[this]
343+
private signature class ParamSig;
344+
345+
private module WithParam<ParamSig P> {
346+
/**
347+
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
348+
*
349+
* The expression `e` is expected to be a syntactic part of the guard `g`.
350+
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
351+
* the argument `x`.
352+
*/
353+
signature predicate guardChecksSig(Node g, Expr e, boolean branch, P param);
354+
}
355+
342356
/**
343357
* Provides a set of barrier nodes for a guard that validates an expression.
344358
*
345359
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
346360
* in data flow and taint tracking.
347361
*/
348362
module BarrierGuard<guardChecksSig/3 guardChecks> {
363+
private predicate guardChecks(Node g, Expr e, boolean branch, Unit param) {
364+
guardChecks(g, e, branch) and exists(param)
365+
}
366+
367+
private module B = ParameterizedBarrierGuard<Unit, guardChecks/4>;
368+
369+
/** Gets a node that is safely guarded by the given guard check. */
370+
Node getABarrierNode() { result = B::getABarrierNode(_) }
371+
372+
/**
373+
* Gets a node that is safely guarded by the given guard check.
374+
*/
375+
Node getABarrierNodeForGuard(Node guardCheck) {
376+
result = B::getABarrierNodeForGuard(guardCheck, _)
377+
}
378+
}
379+
380+
/**
381+
* Provides a set of barrier nodes for a guard that validates an expression.
382+
*
383+
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
384+
* in data flow and taint tracking.
385+
*/
386+
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
349387
/** Gets a node that is safely guarded by the given guard check. */
350-
Node getABarrierNode() {
388+
Node getABarrierNode(P param) {
351389
exists(ControlFlow::ConditionGuardNode guard, SsaWithFields var |
352390
result = pragma[only_bind_out](var).getAUse()
353391
|
354-
guards(_, guard, _, var) and
392+
guards(_, guard, _, var, param) and
355393
pragma[only_bind_out](guard).dominates(result.getBasicBlock())
356394
)
357395
}
358396

359397
/**
360398
* Gets a node that is safely guarded by the given guard check.
361399
*/
362-
Node getABarrierNodeForGuard(Node guardCheck) {
400+
Node getABarrierNodeForGuard(Node guardCheck, P param) {
363401
exists(ControlFlow::ConditionGuardNode guard, SsaWithFields var | result = var.getAUse() |
364-
guards(guardCheck, guard, _, var) and
402+
guards(guardCheck, guard, _, var, param) and
365403
guard.dominates(result.getBasicBlock())
366404
)
367405
}
@@ -373,22 +411,24 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
373411
* This predicate exists to enforce a good join order in `getAGuardedNode`.
374412
*/
375413
pragma[noinline]
376-
private predicate guards(Node g, ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields ap) {
377-
guards(g, guard, nd) and nd = ap.getAUse()
414+
private predicate guards(
415+
Node g, ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields ap, P param
416+
) {
417+
guards(g, guard, nd, param) and nd = ap.getAUse()
378418
}
379419

380420
/**
381421
* Holds if `guard` marks a point in the control-flow graph where `g`
382422
* is known to validate `nd`.
383423
*/
384-
private predicate guards(Node g, ControlFlow::ConditionGuardNode guard, Node nd) {
424+
private predicate guards(Node g, ControlFlow::ConditionGuardNode guard, Node nd, P param) {
385425
exists(boolean branch |
386-
guardChecks(g, nd.asExpr(), branch) and
426+
guardChecks(g, nd.asExpr(), branch, param) and
387427
guard.ensures(g, branch)
388428
)
389429
or
390430
exists(DataFlow::Property p, Node resNode, Node check, boolean outcome |
391-
guardingCall(g, _, _, _, p, _, nd, resNode) and
431+
guardingCall(g, _, _, _, p, _, nd, resNode, param) and
392432
p.checkOn(check, outcome, resNode) and
393433
guard.ensures(pragma[only_bind_into](check), outcome)
394434
)
@@ -405,9 +445,9 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
405445
pragma[noinline]
406446
private predicate guardingCall(
407447
Node g, Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p, CallNode c,
408-
Node nd, Node resNode
448+
Node nd, Node resNode, P param
409449
) {
410-
guardingFunction(g, f, inp, outp, p) and
450+
guardingFunction(g, f, inp, outp, p, param) and
411451
c = f.getACall() and
412452
nd = getInputNode(inp, c) and
413453
localFlow(getOutputNode(outp, c), resNode)
@@ -438,15 +478,15 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
438478
* `false`, `nil` or a non-`nil` value.)
439479
*/
440480
private predicate guardingFunction(
441-
Node g, Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p
481+
Node g, Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p, P param
442482
) {
443483
exists(FuncDecl fd, Node arg, Node ret |
444484
fd.getFunction() = f and
445485
localFlow(inp.getExitNode(fd), pragma[only_bind_out](arg)) and
446486
(
447487
// Case: a function like "if someBarrierGuard(arg) { return true } else { return false }"
448488
exists(ControlFlow::ConditionGuardNode guard |
449-
guards(g, pragma[only_bind_out](guard), arg) and
489+
guards(g, pragma[only_bind_out](guard), arg, param) and
450490
guard.dominates(pragma[only_bind_out](ret).getBasicBlock())
451491
|
452492
onlyPossibleReturnSatisfyingProperty(fd, outp, ret, p)
@@ -456,7 +496,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
456496
// or "return !someBarrierGuard(arg) && otherCond(...)"
457497
exists(boolean outcome |
458498
ret = getUniqueOutputNode(fd, outp) and
459-
guardChecks(g, arg.asExpr(), outcome) and
499+
guardChecks(g, arg.asExpr(), outcome, param) and
460500
// This predicate's contract is (p holds of ret ==> arg is checked),
461501
// (and we have (this has outcome ==> arg is checked))
462502
// but p.checkOn(ret, outcome, this) gives us (ret has outcome ==> p holds of this),
@@ -471,7 +511,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
471511
DataFlow::Property outpProp
472512
|
473513
ret = getUniqueOutputNode(fd, outp) and
474-
guardingFunction(g, f2, inp2, outp2, outpProp) and
514+
guardingFunction(g, f2, inp2, outp2, outpProp, param) and
475515
c = f2.getACall() and
476516
arg = inp2.getNode(c) and
477517
(

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,27 @@ module SourceSinkInterpretationInput implements
160160
}
161161

162162
predicate barrierElement(
163-
Element n, string output, string kind, Public::Provenance provenance, string model
163+
Element e, string output, string kind, Public::Provenance provenance, string model
164164
) {
165-
none()
165+
exists(
166+
string package, string type, boolean subtypes, string name, string signature, string ext
167+
|
168+
barrierModel(package, type, subtypes, name, signature, ext, output, kind, provenance, model) and
169+
e = interpretElement(package, type, subtypes, name, signature, ext)
170+
)
166171
}
167172

168173
predicate barrierGuardElement(
169-
Element n, string input, Public::AcceptingValue acceptingvalue, string kind,
174+
Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
170175
Public::Provenance provenance, string model
171176
) {
172-
none()
177+
exists(
178+
string package, string type, boolean subtypes, string name, string signature, string ext
179+
|
180+
barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingvalue, kind,
181+
provenance, model) and
182+
e = interpretElement(package, type, subtypes, name, signature, ext)
183+
)
173184
}
174185

175186
// Note that due to embedding, which is currently implemented via some

0 commit comments

Comments
 (0)