mirror of
https://github.com/github/codeql.git
synced 2026-03-30 20:28:15 +02:00
Merge pull request #21404 from hvitved/dataflow/no-enclosing-stack-flow-feature
Data flow: Add `FeatureEscapesSourceCallContext(OrEqualSourceSinkCallContext)` flow feature
This commit is contained in:
@@ -50,6 +50,10 @@ module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
|
||||
result = [target.getLocation(), source.getLocation()]
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
module AccessAfterLifetimeFlow = TaintTracking::Global<AccessAfterLifetimeConfig>;
|
||||
@@ -64,53 +68,22 @@ predicate sinkBlock(Sink s, BlockExpr be) {
|
||||
be = s.asExpr().getEnclosingBlock()
|
||||
}
|
||||
|
||||
private predicate tcStep(BlockExpr a, BlockExpr b) {
|
||||
// propagate through function calls
|
||||
exists(Call call |
|
||||
a = call.getEnclosingBlock() and
|
||||
call.getARuntimeTarget() = b.getEnclosingCallable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isTcSource(BlockExpr be) { sourceBlock(_, _, be) }
|
||||
|
||||
private predicate isTcSink(BlockExpr be) { sinkBlock(_, be) }
|
||||
|
||||
/**
|
||||
* Holds if block `a` contains block `b`, in the sense that a stack allocated variable in
|
||||
* `a` may still be on the stack during execution of `b`. This is interprocedural,
|
||||
* but is an overapproximation that doesn't accurately track call contexts
|
||||
* (for example if `f` and `g` both call `b`, then depending on the
|
||||
* caller a variable in `f` or `g` may or may-not be on the stack during `b`).
|
||||
*/
|
||||
private predicate mayEncloseOnStack(BlockExpr a, BlockExpr b) =
|
||||
doublyBoundedFastTC(tcStep/2, isTcSource/1, isTcSink/1)(a, b)
|
||||
|
||||
/**
|
||||
* Holds if the pair `(source, sink)`, that represents a flow from a
|
||||
* pointer or reference to a dereference, has its dereference outside the
|
||||
* lifetime of the target variable `target`.
|
||||
*/
|
||||
predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) {
|
||||
AccessAfterLifetimeFlow::flow(source, sink) and
|
||||
sourceValueScope(source, target, _) and
|
||||
not exists(BlockExpr beSource, BlockExpr beSink |
|
||||
sourceBlock(source, target, beSource) and
|
||||
sinkBlock(sink, beSink)
|
||||
|
|
||||
beSource = beSink
|
||||
or
|
||||
mayEncloseOnStack(beSource, beSink)
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
AccessAfterLifetimeFlow::PathNode sourceNode, AccessAfterLifetimeFlow::PathNode sinkNode,
|
||||
Variable target
|
||||
Source source, Sink sink, Variable target
|
||||
where
|
||||
// flow from a pointer or reference to the dereference
|
||||
AccessAfterLifetimeFlow::flowPath(sourceNode, sinkNode) and
|
||||
// check that the dereference is outside the lifetime of the target
|
||||
dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target)
|
||||
source = sourceNode.getNode() and
|
||||
sink = sinkNode.getNode() and
|
||||
sourceValueScope(source, target, _) and
|
||||
// check that the dereference is outside the lifetime of the target, when the source
|
||||
// and the sink are in the same callable
|
||||
// (`FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext` handles the case when
|
||||
// they are not)
|
||||
not exists(BlockExpr be |
|
||||
sourceBlock(source, target, be) and
|
||||
sinkBlock(sink, be)
|
||||
)
|
||||
select sinkNode.getNode(), sourceNode, sinkNode,
|
||||
"Access of a pointer to $@ after its lifetime has ended.", target, target.toString()
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
| lifetime.rs:76:4:76:5 | p2 | lifetime.rs:27:9:27:22 | &mut my_local2 | lifetime.rs:76:4:76:5 | p2 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:25:10:25:18 | my_local2 | my_local2 |
|
||||
| lifetime.rs:77:4:77:5 | p4 | lifetime.rs:39:9:39:26 | &raw mut my_local4 | lifetime.rs:77:4:77:5 | p4 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:37:10:37:18 | my_local4 | my_local4 |
|
||||
| lifetime.rs:172:13:172:15 | ptr | lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:172:13:172:15 | ptr | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:186:6:186:14 | my_local1 | my_local1 |
|
||||
| lifetime.rs:180:13:180:15 | ptr | lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:180:13:180:15 | ptr | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:186:6:186:14 | my_local1 | my_local1 |
|
||||
| lifetime.rs:255:14:255:17 | prev | lifetime.rs:251:10:251:19 | &my_local2 | lifetime.rs:255:14:255:17 | prev | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:242:7:242:15 | my_local2 | my_local2 |
|
||||
| lifetime.rs:310:31:310:32 | e1 | lifetime.rs:272:30:272:32 | &e1 | lifetime.rs:310:31:310:32 | e1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:271:6:271:7 | e1 | e1 |
|
||||
| lifetime.rs:314:23:314:24 | p2 | lifetime.rs:279:28:279:30 | &v2 | lifetime.rs:314:23:314:24 | p2 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:278:6:278:7 | v2 | v2 |
|
||||
@@ -55,39 +56,23 @@ edges
|
||||
| lifetime.rs:59:11:59:36 | get_local_field_dangling(...) | lifetime.rs:59:6:59:7 | p6 | provenance | |
|
||||
| lifetime.rs:63:3:63:4 | p7 | lifetime.rs:75:13:75:14 | p7 | provenance | |
|
||||
| lifetime.rs:63:8:63:27 | &raw const my_local7 | lifetime.rs:63:3:63:4 | p7 | provenance | |
|
||||
| lifetime.rs:91:17:91:30 | ...: ... | lifetime.rs:101:14:101:15 | p1 | provenance | |
|
||||
| lifetime.rs:91:33:91:44 | ...: ... | lifetime.rs:102:14:102:15 | p2 | provenance | |
|
||||
| lifetime.rs:91:33:91:44 | ...: ... | lifetime.rs:110:5:110:6 | p2 | provenance | |
|
||||
| lifetime.rs:94:2:94:3 | p3 | lifetime.rs:103:14:103:15 | p3 | provenance | |
|
||||
| lifetime.rs:94:7:94:16 | &my_local1 | lifetime.rs:94:2:94:3 | p3 | provenance | |
|
||||
| lifetime.rs:119:15:119:24 | &my_local3 | lifetime.rs:91:17:91:30 | ...: ... | provenance | |
|
||||
| lifetime.rs:119:27:119:44 | &mut my_local_mut4 | lifetime.rs:91:33:91:44 | ...: ... | provenance | |
|
||||
| lifetime.rs:161:17:161:31 | ...: ... | lifetime.rs:164:13:164:15 | ptr | provenance | |
|
||||
| lifetime.rs:169:17:169:31 | ...: ... | lifetime.rs:172:13:172:15 | ptr | provenance | |
|
||||
| lifetime.rs:177:17:177:31 | ...: ... | lifetime.rs:180:13:180:15 | ptr | provenance | |
|
||||
| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:189:15:189:17 | ptr | provenance | |
|
||||
| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:190:15:190:17 | ptr | provenance | |
|
||||
| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:192:2:192:11 | return ptr | provenance | |
|
||||
| lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:187:6:187:8 | ptr | provenance | |
|
||||
| lifetime.rs:189:15:189:17 | ptr | lifetime.rs:161:17:161:31 | ...: ... | provenance | |
|
||||
| lifetime.rs:190:15:190:17 | ptr | lifetime.rs:177:17:177:31 | ...: ... | provenance | |
|
||||
| lifetime.rs:192:2:192:11 | return ptr | lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | provenance | |
|
||||
| lifetime.rs:196:6:196:8 | ptr | lifetime.rs:200:15:200:17 | ptr | provenance | |
|
||||
| lifetime.rs:196:6:196:8 | ptr | lifetime.rs:201:15:201:17 | ptr | provenance | |
|
||||
| lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | lifetime.rs:196:6:196:8 | ptr | provenance | |
|
||||
| lifetime.rs:200:15:200:17 | ptr | lifetime.rs:169:17:169:31 | ...: ... | provenance | |
|
||||
| lifetime.rs:201:15:201:17 | ptr | lifetime.rs:177:17:177:31 | ...: ... | provenance | |
|
||||
| lifetime.rs:206:19:206:36 | ...: ... | lifetime.rs:216:16:216:21 | ptr_up | provenance | |
|
||||
| lifetime.rs:208:6:208:13 | ptr_ours | lifetime.rs:211:33:211:40 | ptr_ours | provenance | |
|
||||
| lifetime.rs:208:6:208:13 | ptr_ours | lifetime.rs:225:2:225:16 | return ptr_ours | provenance | |
|
||||
| lifetime.rs:208:17:208:29 | &my_local_rec | lifetime.rs:208:6:208:13 | ptr_ours | provenance | |
|
||||
| lifetime.rs:211:7:211:14 | ptr_down | lifetime.rs:218:18:218:25 | ptr_down | provenance | |
|
||||
| lifetime.rs:211:18:211:52 | access_ptr_rec(...) | lifetime.rs:211:7:211:14 | ptr_down | provenance | |
|
||||
| lifetime.rs:211:33:211:40 | ptr_ours | lifetime.rs:206:19:206:36 | ...: ... | provenance | |
|
||||
| lifetime.rs:225:2:225:16 | return ptr_ours | lifetime.rs:211:18:211:52 | access_ptr_rec(...) | provenance | |
|
||||
| lifetime.rs:230:6:230:14 | ptr_start | lifetime.rs:232:21:232:29 | ptr_start | provenance | |
|
||||
| lifetime.rs:230:18:230:31 | &my_local_rec2 | lifetime.rs:230:6:230:14 | ptr_start | provenance | |
|
||||
| lifetime.rs:232:21:232:29 | ptr_start | lifetime.rs:206:19:206:36 | ...: ... | provenance | |
|
||||
| lifetime.rs:239:6:239:13 | mut prev | lifetime.rs:247:15:247:18 | prev | provenance | |
|
||||
| lifetime.rs:239:6:239:13 | mut prev | lifetime.rs:255:14:255:17 | prev | provenance | |
|
||||
| lifetime.rs:239:34:239:43 | &my_local1 | lifetime.rs:239:6:239:13 | mut prev | provenance | |
|
||||
@@ -215,43 +200,26 @@ nodes
|
||||
| lifetime.rs:75:13:75:14 | p7 | semmle.label | p7 |
|
||||
| lifetime.rs:76:4:76:5 | p2 | semmle.label | p2 |
|
||||
| lifetime.rs:77:4:77:5 | p4 | semmle.label | p4 |
|
||||
| lifetime.rs:91:17:91:30 | ...: ... | semmle.label | ...: ... |
|
||||
| lifetime.rs:91:33:91:44 | ...: ... | semmle.label | ...: ... |
|
||||
| lifetime.rs:94:2:94:3 | p3 | semmle.label | p3 |
|
||||
| lifetime.rs:94:7:94:16 | &my_local1 | semmle.label | &my_local1 |
|
||||
| lifetime.rs:101:14:101:15 | p1 | semmle.label | p1 |
|
||||
| lifetime.rs:102:14:102:15 | p2 | semmle.label | p2 |
|
||||
| lifetime.rs:103:14:103:15 | p3 | semmle.label | p3 |
|
||||
| lifetime.rs:110:5:110:6 | p2 | semmle.label | p2 |
|
||||
| lifetime.rs:119:15:119:24 | &my_local3 | semmle.label | &my_local3 |
|
||||
| lifetime.rs:119:27:119:44 | &mut my_local_mut4 | semmle.label | &mut my_local_mut4 |
|
||||
| lifetime.rs:161:17:161:31 | ...: ... | semmle.label | ...: ... |
|
||||
| lifetime.rs:164:13:164:15 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:169:17:169:31 | ...: ... | semmle.label | ...: ... |
|
||||
| lifetime.rs:172:13:172:15 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:177:17:177:31 | ...: ... | semmle.label | ...: ... |
|
||||
| lifetime.rs:180:13:180:15 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:187:6:187:8 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:187:12:187:21 | &my_local1 | semmle.label | &my_local1 |
|
||||
| lifetime.rs:189:15:189:17 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:190:15:190:17 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:192:2:192:11 | return ptr | semmle.label | return ptr |
|
||||
| lifetime.rs:196:6:196:8 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | semmle.label | access_and_get_dangling(...) |
|
||||
| lifetime.rs:200:15:200:17 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:201:15:201:17 | ptr | semmle.label | ptr |
|
||||
| lifetime.rs:206:19:206:36 | ...: ... | semmle.label | ...: ... |
|
||||
| lifetime.rs:208:6:208:13 | ptr_ours | semmle.label | ptr_ours |
|
||||
| lifetime.rs:208:17:208:29 | &my_local_rec | semmle.label | &my_local_rec |
|
||||
| lifetime.rs:211:7:211:14 | ptr_down | semmle.label | ptr_down |
|
||||
| lifetime.rs:211:18:211:52 | access_ptr_rec(...) | semmle.label | access_ptr_rec(...) |
|
||||
| lifetime.rs:211:33:211:40 | ptr_ours | semmle.label | ptr_ours |
|
||||
| lifetime.rs:216:16:216:21 | ptr_up | semmle.label | ptr_up |
|
||||
| lifetime.rs:218:18:218:25 | ptr_down | semmle.label | ptr_down |
|
||||
| lifetime.rs:225:2:225:16 | return ptr_ours | semmle.label | return ptr_ours |
|
||||
| lifetime.rs:230:6:230:14 | ptr_start | semmle.label | ptr_start |
|
||||
| lifetime.rs:230:18:230:31 | &my_local_rec2 | semmle.label | &my_local_rec2 |
|
||||
| lifetime.rs:232:21:232:29 | ptr_start | semmle.label | ptr_start |
|
||||
| lifetime.rs:239:6:239:13 | mut prev | semmle.label | mut prev |
|
||||
| lifetime.rs:239:34:239:43 | &my_local1 | semmle.label | &my_local1 |
|
||||
| lifetime.rs:247:15:247:18 | prev | semmle.label | prev |
|
||||
|
||||
@@ -177,7 +177,7 @@ fn access_ptr_2(ptr: *const i64) {
|
||||
fn access_ptr_3(ptr: *const i64) {
|
||||
// called from contexts with `ptr` safe and dangling
|
||||
unsafe {
|
||||
let v3 = *ptr; // $ MISSING: Alert
|
||||
let v3 = *ptr; // $ Alert[rust/access-after-lifetime-ended]=local1
|
||||
println!(" v3 = {v3} (!)"); // corrupt in practice (in one context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Two new flow features `FeatureEscapesSourceCallContext` and `FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext` have been added. The former implies that the sink must be reached from the source by escaping the source call context, that is, flow must either return from the callable containing the source or use a jump-step before reaching the sink. The latter is the disjunction of the former and the existing `FeatureEqualSourceSinkCallContext` flow feature.
|
||||
@@ -567,7 +567,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
) {
|
||||
sourceNode(node) and
|
||||
(if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and
|
||||
summaryCtx = TSummaryCtxNone() and
|
||||
summaryCtx.isSourceCtx() and
|
||||
t = getNodeTyp(node) and
|
||||
ap instanceof ApNil and
|
||||
apa = getApprox(ap) and
|
||||
@@ -602,18 +602,19 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
apa = getApprox(ap)
|
||||
or
|
||||
// flow into a callable without summary context
|
||||
fwdFlowInNoFlowThrough(node, cc, t, ap, stored) and
|
||||
fwdFlowInNoFlowThrough(node, cc, summaryCtx, t, ap, stored) and
|
||||
apa = getApprox(ap) and
|
||||
summaryCtx = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
or
|
||||
// flow into a callable with summary context (non-linear recursion)
|
||||
fwdFlowInFlowThrough(node, cc, t, ap, stored) and
|
||||
apa = getApprox(ap) and
|
||||
summaryCtx = TSummaryCtxSome(node, t, ap, stored)
|
||||
exists(boolean mustReturn |
|
||||
fwdFlowInFlowThrough(node, cc, t, ap, stored, mustReturn) and
|
||||
apa = getApprox(ap) and
|
||||
summaryCtx = TSummaryCtxSome(node, t, ap, stored, mustReturn)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, _, node, cc, summaryCtx, t, ap, stored) and
|
||||
@@ -630,9 +631,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) {
|
||||
fwdFlowInFlowThrough(p, _, t, ap, stored)
|
||||
}
|
||||
TSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored, boolean mustReturn) {
|
||||
fwdFlowInFlowThrough(p, _, t, ap, stored, mustReturn)
|
||||
} or
|
||||
TSummaryCtxSource(Boolean mustEscape)
|
||||
|
||||
/**
|
||||
* A context for generating flow summaries. This represents flow entry through
|
||||
@@ -644,6 +646,69 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
abstract string toString();
|
||||
|
||||
abstract Location getLocation();
|
||||
|
||||
/**
|
||||
* Holds if this context is the unique context used at flow sources.
|
||||
*/
|
||||
predicate isSourceCtx() {
|
||||
exists(boolean strict |
|
||||
Stage1::hasFeatureEscapesSourceCallContext(strict) and
|
||||
this = TSummaryCtxSource(strict)
|
||||
)
|
||||
or
|
||||
not Stage1::hasFeatureEscapesSourceCallContext(_) and
|
||||
this = TSummaryCtxNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isSome(boolean mustReturn) {
|
||||
this = TSummaryCtxSome(_, _, _, _, mustReturn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this context is valid as a flow-in context when no flow-through is possible,
|
||||
* in which case `innerSummaryCtx` is the summary context to be used when entering the
|
||||
* callable.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate isValidForFlowInNoThrough(SummaryCtx innerSummaryCtx) {
|
||||
this = TSummaryCtxNone() and
|
||||
innerSummaryCtx = TSummaryCtxNone()
|
||||
or
|
||||
this.isSome(false) and
|
||||
innerSummaryCtx = TSummaryCtxNone()
|
||||
or
|
||||
// Even if we must escape the source call context, we can still allow for flow-in
|
||||
// without flow-through, as long as the inner context is escaped (which must then
|
||||
// necessarily be via a jump-step -- possibly after even more flow-in
|
||||
// without-flow-through steps).
|
||||
this = TSummaryCtxSource(_) and
|
||||
innerSummaryCtx = TSummaryCtxSource(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this context is valid as a flow-in context when flow-through is possible.
|
||||
*
|
||||
* The boolean `mustReturn` indicates whether flow must return.
|
||||
*/
|
||||
predicate isValidForFlowThrough(boolean mustReturn) {
|
||||
this = TSummaryCtxSource(_) and
|
||||
mustReturn = true
|
||||
or
|
||||
this = TSummaryCtxNone() and
|
||||
mustReturn = false
|
||||
or
|
||||
this.isSome(mustReturn)
|
||||
}
|
||||
|
||||
/** Holds if this context is valid as a sink context. */
|
||||
predicate isASinkCtx() {
|
||||
this = TSummaryCtxNone()
|
||||
or
|
||||
this.isSome(false)
|
||||
or
|
||||
this = TSummaryCtxSource(false)
|
||||
}
|
||||
}
|
||||
|
||||
/** A summary context from which no flow summary can be generated. */
|
||||
@@ -659,20 +724,43 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
private Typ t;
|
||||
private Ap ap;
|
||||
private TypOption stored;
|
||||
private boolean mustReturn;
|
||||
|
||||
SummaryCtxSome() { this = TSummaryCtxSome(p, t, ap, stored) }
|
||||
SummaryCtxSome() { this = TSummaryCtxSome(p, t, ap, stored, mustReturn) }
|
||||
|
||||
ParamNd getParamNode() { result = p }
|
||||
|
||||
private string ppTyp() { result = t.toString() and result != "" }
|
||||
|
||||
private string ppMustReturn() {
|
||||
if mustReturn = true then result = " <mustReturn>" else result = ""
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored)
|
||||
result =
|
||||
p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored) + this.ppMustReturn()
|
||||
}
|
||||
|
||||
override Location getLocation() { result = p.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A special summary context that is used when the flow feature
|
||||
* `FeatureEscapesSourceCallContext(OrEqualSourceSinkCallContext)`
|
||||
* is enabled.
|
||||
*/
|
||||
private class SummaryCtxSource extends SummaryCtx, TSummaryCtxSource {
|
||||
private boolean mustEscape;
|
||||
|
||||
SummaryCtxSource() { this = TSummaryCtxSource(mustEscape) }
|
||||
|
||||
override string toString() {
|
||||
if mustEscape = true then result = "<source-must-escape>" else result = "<source>"
|
||||
}
|
||||
|
||||
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
|
||||
}
|
||||
|
||||
private predicate fwdFlowJump(Nd node, Typ t, Ap ap, TypOption stored) {
|
||||
exists(Nd mid |
|
||||
fwdFlow(mid, _, _, t, ap, stored) and
|
||||
@@ -915,9 +1003,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInNoFlowThrough(
|
||||
ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored
|
||||
ParamNd p, CcCall innercc, SummaryCtx innerSummaryCtx, Typ t, Ap ap, TypOption stored
|
||||
) {
|
||||
FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _)
|
||||
exists(SummaryCtx summaryCtx |
|
||||
FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, _, innercc, summaryCtx, t, ap, stored, _) and
|
||||
summaryCtx.isValidForFlowInNoThrough(innerSummaryCtx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate top() { any() }
|
||||
@@ -926,9 +1017,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInFlowThrough(
|
||||
ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored
|
||||
ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored, boolean mustReturn
|
||||
) {
|
||||
FwdFlowInThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _)
|
||||
exists(SummaryCtx summaryCtx |
|
||||
FwdFlowInThrough::fwdFlowIn(_, _, _, p, _, innercc, summaryCtx, t, ap, stored, _) and
|
||||
summaryCtx.isValidForFlowThrough(mustReturn)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -999,7 +1093,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
TypOption stored
|
||||
) {
|
||||
exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow |
|
||||
fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and
|
||||
fwdFlowIntoRet(ret, innercc, _, t, ap, stored) and
|
||||
summaryCtx = TSummaryCtxNone() and
|
||||
fwdFlowOutValidEdge(call, ret, innercc, inner, out, outercc, allowsFieldFlow) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
@@ -1090,7 +1185,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
instanceofCcCall(ccc) and
|
||||
fwdFlow(pragma[only_bind_into](ret), ccc, summaryCtx, t, ap, stored) and
|
||||
summaryCtx =
|
||||
TSummaryCtxSome(pragma[only_bind_into](p), _, pragma[only_bind_into](argAp), _) and
|
||||
TSummaryCtxSome(pragma[only_bind_into](p), _, pragma[only_bind_into](argAp), _, _) and
|
||||
kind = ret.getKind() and
|
||||
Stage1::parameterFlowThroughAllowed(p, kind) and
|
||||
PrevStage::returnMayFlowThrough(ret, kind)
|
||||
@@ -1116,9 +1211,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered0(
|
||||
Call call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, ParamNd p, Typ t,
|
||||
Ap ap, TypOption stored
|
||||
Ap ap, TypOption stored, boolean mustReturn
|
||||
) {
|
||||
FwdFlowInThrough::fwdFlowIn(call, arg, _, p, cc, innerCc, summaryCtx, t, ap, stored, _)
|
||||
FwdFlowInThrough::fwdFlowIn(call, arg, _, p, cc, innerCc, summaryCtx, t, ap, stored, _) and
|
||||
summaryCtx.isValidForFlowThrough(mustReturn)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1130,9 +1226,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
Call call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx,
|
||||
SummaryCtxSome innerSummaryCtx
|
||||
) {
|
||||
exists(ParamNd p, Typ t, Ap ap, TypOption stored |
|
||||
fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, t, ap, stored) and
|
||||
innerSummaryCtx = TSummaryCtxSome(p, t, ap, stored)
|
||||
exists(ParamNd p, Typ t, Ap ap, TypOption stored, boolean mustReturn |
|
||||
fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, t, ap, stored, mustReturn) and
|
||||
innerSummaryCtx = TSummaryCtxSome(p, t, ap, stored, mustReturn)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1160,7 +1256,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
TypOption argStored, Ap ap
|
||||
) {
|
||||
exists(Call call, boolean allowsFieldFlow |
|
||||
returnFlowsThrough0(call, ccc, ap, ret, TSummaryCtxSome(p, argT, argAp, argStored)) and
|
||||
returnFlowsThrough0(call, ccc, ap, ret, TSummaryCtxSome(p, argT, argAp, argStored, _)) and
|
||||
flowThroughOutOfCall(call, ret, _, allowsFieldFlow) and
|
||||
pos = ret.getReturnPosition() and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1216,7 +1312,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(Nd node, ReturnCtx returnCtx, ApOption returnAp, Ap ap) {
|
||||
fwdFlow(node, _, _, _, ap, _) and
|
||||
fwdFlow(node, _, any(SummaryCtx sinkCtx | sinkCtx.isASinkCtx()), _, ap, _) and
|
||||
sinkNode(node) and
|
||||
(
|
||||
if hasSinkCallCtx()
|
||||
@@ -1490,7 +1586,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
exists(Ap ap0 |
|
||||
parameterMayFlowThrough(p, _) and
|
||||
revFlow(n, TReturnCtxMaybeFlowThrough(_), _, ap0) and
|
||||
fwdFlow(n, any(CcCall ccc), TSummaryCtxSome(p, _, ap, _), _, ap0, _)
|
||||
fwdFlow(n, any(CcCall ccc), TSummaryCtxSome(p, _, ap, _, _), _, ap0, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1962,6 +2058,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
private string ppSummaryCtx() {
|
||||
summaryCtx instanceof SummaryCtxNone and result = ""
|
||||
or
|
||||
result = " " + summaryCtx.(SummaryCtxSource)
|
||||
or
|
||||
summaryCtx instanceof SummaryCtxSome and
|
||||
result = " <" + summaryCtx + ">"
|
||||
}
|
||||
@@ -1983,7 +2081,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
override predicate isSource() {
|
||||
sourceNode(node) and
|
||||
(if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and
|
||||
summaryCtx = TSummaryCtxNone() and
|
||||
summaryCtx.isSourceCtx() and
|
||||
t = getNodeTyp(node) and
|
||||
ap instanceof ApNil
|
||||
}
|
||||
@@ -1991,6 +2089,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
predicate isAtSink() {
|
||||
sinkNode(node) and
|
||||
ap instanceof ApNil and
|
||||
summaryCtx.isASinkCtx() and
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call
|
||||
@@ -2042,10 +2141,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
override predicate isSource() { sourceNode(node) }
|
||||
}
|
||||
|
||||
bindingset[p, t, ap, stored]
|
||||
bindingset[p, t, ap, stored, mustReturn]
|
||||
pragma[inline_late]
|
||||
private SummaryCtxSome mkSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) {
|
||||
result = TSummaryCtxSome(p, t, ap, stored)
|
||||
private SummaryCtxSome mkSummaryCtxSome(
|
||||
ParamNd p, Typ t, Ap ap, TypOption stored, boolean mustReturn
|
||||
) {
|
||||
result = TSummaryCtxSome(p, t, ap, stored, mustReturn)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2055,11 +2156,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
) {
|
||||
FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap,
|
||||
stored, _) and
|
||||
innerSummaryCtx = TSummaryCtxNone()
|
||||
outerSummaryCtx.isValidForFlowInNoThrough(innerSummaryCtx)
|
||||
or
|
||||
FwdFlowInThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap,
|
||||
stored, _) and
|
||||
innerSummaryCtx = mkSummaryCtxSome(p, t, ap, stored)
|
||||
exists(boolean mustReturn |
|
||||
FwdFlowInThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap,
|
||||
stored, _) and
|
||||
outerSummaryCtx.isValidForFlowThrough(mustReturn) and
|
||||
innerSummaryCtx = mkSummaryCtxSome(p, t, ap, stored, mustReturn)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2098,7 +2202,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
|
||||
fwdFlowThroughStep0(call, arg, cc, ccc, summaryCtx, t, ap, stored, ret,
|
||||
innerSummaryCtx) and
|
||||
innerSummaryCtx = TSummaryCtxSome(p, innerArgT, innerArgAp, innerArgStored) and
|
||||
innerSummaryCtx = TSummaryCtxSome(p, innerArgT, innerArgAp, innerArgStored, _) and
|
||||
pn1 = mkPathNode(arg, cc, summaryCtx, innerArgT, innerArgAp, innerArgStored) and
|
||||
pn2 =
|
||||
typeStrengthenToPathNode(p, ccc, innerSummaryCtx, innerArgT, innerArgAp,
|
||||
@@ -2212,11 +2316,14 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow |
|
||||
pn1 = TPathNodeMid(ret, innercc, summaryCtx, t, ap, stored) and
|
||||
fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and
|
||||
exists(
|
||||
RetNd ret, CcNoCall innercc, SummaryCtx innerSummaryCtx, boolean allowsFieldFlow
|
||||
|
|
||||
pn1 = TPathNodeMid(ret, innercc, innerSummaryCtx, t, ap, stored) and
|
||||
fwdFlowIntoRet(ret, innercc, innerSummaryCtx, t, ap, stored) and
|
||||
fwdFlowOutValidEdge(_, ret, innercc, _, node, cc, allowsFieldFlow) and
|
||||
label = "" and
|
||||
summaryCtx = TSummaryCtxNone() and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ module;
|
||||
|
||||
private import codeql.dataflow.DataFlow
|
||||
private import codeql.typetracking.TypeTracking as Tt
|
||||
private import codeql.util.Boolean
|
||||
private import codeql.util.Location
|
||||
private import codeql.util.Option
|
||||
private import codeql.util.Unit
|
||||
@@ -49,7 +50,9 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
private newtype TFlowFeature =
|
||||
TFeatureHasSourceCallContext() or
|
||||
TFeatureHasSinkCallContext() or
|
||||
TFeatureEqualSourceSinkCallContext()
|
||||
TFeatureEqualSourceSinkCallContext() or
|
||||
TFeatureEscapesSourceCallContext() or
|
||||
TFeatureEscapesSourceCallContextOrEqualSourceSinkCallContext()
|
||||
|
||||
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
|
||||
class FlowFeature extends TFlowFeature {
|
||||
@@ -80,6 +83,28 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
|
||||
override string toString() { result = "FeatureEqualSourceSinkCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that the sink must be reached from
|
||||
* the source by escaping the source call context, that is, flow must either
|
||||
* return from the callable containing the source or use a jump-step before reaching
|
||||
* the sink.
|
||||
*/
|
||||
class FeatureEscapesSourceCallContext extends FlowFeature, TFeatureEscapesSourceCallContext {
|
||||
override string toString() { result = "FeatureEscapesSourceCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that is the disjunction of `FeatureEscapesSourceCallContext`
|
||||
* and `FeatureEqualSourceSinkCallContext`.
|
||||
*/
|
||||
class FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext extends FlowFeature,
|
||||
TFeatureEscapesSourceCallContextOrEqualSourceSinkCallContext
|
||||
{
|
||||
override string toString() {
|
||||
result = "FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
|
||||
@@ -64,6 +64,8 @@ module MakeImplStage1<LocationSig Location, InputSig<Location> Lang> {
|
||||
|
||||
predicate hasSourceCallCtx();
|
||||
|
||||
predicate hasFeatureEscapesSourceCallContext(boolean strict);
|
||||
|
||||
predicate hasSinkCallCtx();
|
||||
|
||||
predicate jumpStepEx(Nd node1, Nd node2);
|
||||
@@ -1016,6 +1018,13 @@ module MakeImplStage1<LocationSig Location, InputSig<Location> Lang> {
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasFeatureEscapesSourceCallContext(boolean strict) {
|
||||
Config::getAFeature() instanceof FeatureEscapesSourceCallContext and strict = true
|
||||
or
|
||||
Config::getAFeature() instanceof FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext and
|
||||
strict = false
|
||||
}
|
||||
|
||||
predicate hasSinkCallCtx() {
|
||||
exists(FlowFeature feature | feature = Config::getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
|
||||
Reference in New Issue
Block a user