Skip to content

Commit c48cfb7

Browse files
committed
C++: Change the behavior of 'isUserInput' so that the 'argv' paramter (and not all of the uses of 'argv') is user input. This removes the duplicate paths.
1 parent 353ef9f commit c48cfb7

File tree

39 files changed

+1079
-537
lines changed

39 files changed

+1079
-537
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,24 @@ predicate predictableOnlyFlow(string name) {
4747
]
4848
}
4949

50-
private DataFlow::Node getNodeForSource(Expr source) {
51-
isUserInput(source, _) and
52-
result = getNodeForExpr(source)
50+
private DataFlow::Node getNodeForSource(Element source) {
51+
(
52+
// It is user input
53+
isUserInput(source, _) and
54+
// But `isUserInput` also marks all uses of `argv` as user input, and we don't want those.
55+
not argv(source.(VariableAccess).getTarget())
56+
or
57+
// Instead, we want the actual parameter as user input.
58+
argv(source)
59+
) and
60+
result = getNodeForElement(source)
5361
}
5462

55-
private DataFlow::Node getNodeForExpr(Expr node) {
63+
private DataFlow::Node getNodeForElement(Element node) {
5664
result = DataFlow::exprNode(node)
5765
or
66+
result.asParameter() = node
67+
or
5868
// Some of the sources in `isUserInput` are intended to match the value of
5969
// an expression, while others (those modeled below) are intended to match
6070
// the taint that propagates out of an argument, like the `char *` argument
@@ -194,13 +204,7 @@ private module Cached {
194204
cached
195205
predicate nodeIsBarrierIn(DataFlow::Node node) {
196206
// don't use dataflow into taint sources, as this leads to duplicate results.
197-
exists(Expr source | isUserInput(source, _) |
198-
node = DataFlow::exprNode(source)
199-
or
200-
// This case goes together with the similar (but not identical) rule in
201-
// `getNodeForSource`.
202-
node = DataFlow::definitionByReferenceNodeFromArgument(source)
203-
)
207+
node = getNodeForSource(_)
204208
or
205209
// don't use dataflow into binary instructions if both operands are unpredictable
206210
exists(BinaryInstruction iTo |
@@ -303,7 +307,7 @@ private import Cached
303307
* If you need that you must call `taintedIncludingGlobalVars`.
304308
*/
305309
cached
306-
predicate tainted(Expr source, Element tainted) {
310+
predicate tainted(Element source, Element tainted) {
307311
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
308312
cfg.hasFlow(getNodeForSource(source), sink) and
309313
tainted = adjustedSink(sink)
@@ -326,7 +330,7 @@ predicate tainted(Expr source, Element tainted) {
326330
* through a global variable, then `globalVar = ""`.
327331
*/
328332
cached
329-
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
333+
predicate taintedIncludingGlobalVars(Element source, Element tainted, string globalVar) {
330334
tainted(source, tainted) and
331335
globalVar = ""
332336
or
@@ -376,13 +380,13 @@ module TaintedWithPath {
376380
*/
377381
class TaintTrackingConfiguration extends TSingleton {
378382
/** Override this to specify which elements are sources in this configuration. */
379-
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
383+
predicate isSource(Element source) { exists(getNodeForSource(source)) }
380384

381385
/** Override this to specify which elements are sinks in this configuration. */
382386
abstract predicate isSink(Element e);
383387

384388
/** Override this to specify which expressions are barriers in this configuration. */
385-
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
389+
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForElement(e)) }
386390

387391
/**
388392
* Override this predicate to `any()` to allow taint to flow through global
@@ -398,8 +402,8 @@ module TaintedWithPath {
398402
AdjustedConfiguration() { this = "AdjustedConfiguration" }
399403

400404
override predicate isSource(DataFlow::Node source) {
401-
exists(TaintTrackingConfiguration cfg, Expr e |
402-
cfg.isSource(e) and source = getNodeForExpr(e)
405+
exists(TaintTrackingConfiguration cfg, Element e |
406+
cfg.isSource(e) and source = getNodeForElement(e)
403407
)
404408
}
405409

@@ -419,7 +423,9 @@ module TaintedWithPath {
419423
}
420424

421425
override predicate isSanitizer(DataFlow::Node node) {
422-
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
426+
exists(TaintTrackingConfiguration cfg, Element e |
427+
cfg.isBarrier(e) and node = getNodeForElement(e)
428+
)
423429
}
424430

425431
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
@@ -447,7 +453,7 @@ module TaintedWithPath {
447453
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
448454
cfg.hasFlow(sourceNode, sinkNode)
449455
|
450-
sourceNode = getNodeForExpr(e) and
456+
sourceNode = getNodeForElement(e) and
451457
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
452458
or
453459
e = adjustedSink(sinkNode) and
@@ -506,7 +512,7 @@ module TaintedWithPath {
506512
}
507513

508514
private class EndpointPathNode extends PathNode, TEndpointPathNode {
509-
Expr inner() { this = TEndpointPathNode(result) }
515+
Element inner() { this = TEndpointPathNode(result) }
510516

511517
override string toString() { result = this.inner().toString() }
512518

@@ -543,14 +549,14 @@ module TaintedWithPath {
543549
// Same for the first node
544550
exists(WrapPathNode sourceNode |
545551
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
546-
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
552+
sourceNode.inner().getNode() = getNodeForElement(a.(InitialPathNode).inner())
547553
)
548554
or
549555
// Finally, handle the case where the path goes directly from a source to a
550556
// sink, meaning that they both need to be translated.
551557
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
552558
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
553-
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
559+
sourceNode.inner().getNode() = getNodeForElement(a.(InitialPathNode).inner()) and
554560
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
555561
)
556562
}
@@ -575,15 +581,15 @@ module TaintedWithPath {
575581
exists(WrapPathNode sourceNode |
576582
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
577583
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
578-
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
584+
sourceNode.inner().getNode() = getNodeForElement(arg.(InitialPathNode).inner())
579585
)
580586
or
581587
// Finally, handle the case where the path goes directly from a source to a
582588
// sink, meaning that they both need to be translated.
583589
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
584590
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
585591
ret.(WrapPathNode).inner(), sinkNode.inner()) and
586-
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
592+
sourceNode.inner().getNode() = getNodeForElement(arg.(InitialPathNode).inner()) and
587593
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
588594
)
589595
}
@@ -603,10 +609,10 @@ module TaintedWithPath {
603609
* user input in a way that users can probably control the exact output of
604610
* the computation.
605611
*/
606-
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
612+
predicate taintedWithPath(Element source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
607613
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
608614
source = sourceNode.(InitialPathNode).inner() and
609-
flowSource = getNodeForExpr(source) and
615+
flowSource = getNodeForElement(source) and
610616
cfg.hasFlow(flowSource, flowSink) and
611617
tainted = adjustedSink(flowSink) and
612618
tainted = sinkNode.(FinalPathNode).inner()

cpp/ql/lib/semmle/code/cpp/security/Security.qll

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,22 +117,22 @@ class SecurityOptions extends string {
117117
* computed from user input. Such expressions are treated as
118118
* sources of taint.
119119
*/
120-
predicate isUserInput(Expr expr, string cause) {
120+
predicate isUserInput(Element e, string cause) {
121121
exists(FunctionCall fc, int i |
122122
this.userInputArgument(fc, i) and
123-
expr = fc.getArgument(i) and
123+
e = fc.getArgument(i) and
124124
cause = fc.getTarget().getName()
125125
)
126126
or
127127
exists(FunctionCall fc |
128128
this.userInputReturned(fc) and
129-
expr = fc and
129+
e = fc and
130130
cause = fc.getTarget().getName()
131131
)
132132
or
133-
commandLineArg(expr) and cause = "argv"
133+
argv(e) and cause = "argv"
134134
or
135-
expr.(EnvironmentRead).getSourceDescription() = cause
135+
e.(EnvironmentRead).getSourceDescription() = cause
136136
}
137137

138138
/**
@@ -188,8 +188,8 @@ predicate userInputReturned(FunctionCall functionCall) {
188188
}
189189

190190
/** Convenience accessor for SecurityOptions.isUserInput */
191-
predicate isUserInput(Expr expr, string cause) {
192-
exists(SecurityOptions opts | opts.isUserInput(expr, cause))
191+
predicate isUserInput(Element e, string cause) {
192+
exists(SecurityOptions opts | opts.isUserInput(e, cause))
193193
}
194194

195195
/** Convenience accessor for SecurityOptions.isProcessOperationArgument */

cpp/ql/lib/semmle/code/cpp/security/TaintTrackingImpl.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ private predicate taintedWithArgsAndGlobalVars(
395395
* This doesn't include data flow through global variables.
396396
* If you need that you must call taintedIncludingGlobalVars.
397397
*/
398-
predicate tainted(Expr source, Element tainted) {
398+
predicate tainted(Element source, Element tainted) {
399399
taintedWithArgsAndGlobalVars(source, tainted, _, "")
400400
}
401401

cpp/ql/src/Critical/OverflowDestination.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ predicate sourceSized(FunctionCall fc, Expr src) {
4545
)
4646
}
4747

48-
from FunctionCall fc, Expr vuln, Expr taintSource
48+
from FunctionCall fc, Expr vuln
4949
where
5050
sourceSized(fc, vuln) and
51-
tainted(taintSource, vuln)
51+
tainted(_, vuln)
5252
select fc,
5353
"To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size."

cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class TaintedPathConfiguration extends TaintTrackingConfiguration {
5353
}
5454

5555
from
56-
FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
56+
FileFunction fileFunction, Expr taintedArg, Element taintSource, PathNode sourceNode,
5757
PathNode sinkNode, string taintCause, string callChain
5858
where
5959
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and

cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class QueryString extends EnvironmentRead {
3030
}
3131

3232
class Configuration extends TaintTrackingConfiguration {
33-
override predicate isSource(Expr source) { source instanceof QueryString }
33+
override predicate isSource(Element source) { source instanceof QueryString }
3434

3535
override predicate isSink(Element tainted) {
3636
exists(PrintStdoutCall call | call.getAnArgument() = tainted)

cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ class Configuration extends TaintTrackingConfiguration {
4343
}
4444

4545
from
46-
SQLLikeFunction runSql, Expr taintedArg, Expr taintSource, PathNode sourceNode, PathNode sinkNode,
47-
string taintCause, string callChain
46+
SQLLikeFunction runSql, Expr taintedArg, Element taintSource, PathNode sourceNode,
47+
PathNode sinkNode, string taintCause, string callChain
4848
where
4949
runSql.outermostWrapperFunctionCall(taintedArg, callChain) and
5050
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and

cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Configuration extends TaintTrackingConfiguration {
2929
override predicate isSink(Element arg) { isProcessOperationExplanation(arg, _) }
3030
}
3131

32-
from string processOperation, Expr arg, Expr source, PathNode sourceNode, PathNode sinkNode
32+
from string processOperation, Expr arg, Element source, PathNode sourceNode, PathNode sinkNode
3333
where
3434
isProcessOperationExplanation(arg, processOperation) and
3535
taintedWithPath(source, arg, sourceNode, sinkNode)

cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class Configuration extends TaintTrackingConfiguration {
9494
* an argument. Thus, we get the desired alerts.
9595
*/
9696

97-
from BufferWrite bw, Expr inputSource, Expr tainted, PathNode sourceNode, PathNode sinkNode
97+
from BufferWrite bw, Element inputSource, Expr tainted, PathNode sourceNode, PathNode sinkNode
9898
where
9999
taintedWithPath(inputSource, tainted, sourceNode, sinkNode) and
100100
unboundedWriteSource(tainted, bw)

cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ predicate linearBoundControls(BasicBlock controlled, SsaDefinition def, StackVar
3232
)
3333
}
3434

35-
from Expr origin, ArrayExpr arrayExpr, VariableAccess offsetExpr
35+
from Element origin, ArrayExpr arrayExpr, VariableAccess offsetExpr
3636
where
3737
tainted(origin, offsetExpr) and
3838
offsetExpr = arrayExpr.getArrayOffset() and

0 commit comments

Comments
 (0)