Skip to content

Commit b3f8d13

Browse files
committed
C++: Pull in the latest version of TaintedPath.ql from CodeQL
1 parent f6ca0ce commit b3f8d13

File tree

2 files changed

+86
-34
lines changed

2 files changed

+86
-34
lines changed

c/cert/src/rules/FIO32-C/DoNotPerformFileOperationsOnDevices.ql

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
*/
1313

1414
import cpp
15-
import codingstandards.c.cert
1615
import semmle.code.cpp.security.FunctionWithWrappers
1716
import semmle.code.cpp.security.Security
18-
import semmle.code.cpp.security.TaintTracking
19-
import TaintedWithPath
17+
import semmle.code.cpp.ir.IR
18+
import semmle.code.cpp.ir.dataflow.TaintTracking
19+
import DataFlow::PathGraph
2020

2121
// Query TaintedPath.ql from the CodeQL standard library
2222
/**
@@ -45,20 +45,92 @@ class FileFunction extends FunctionWithWrappers {
4545
override predicate interestingArg(int arg) { arg = 0 }
4646
}
4747

48-
class TaintedPathConfiguration extends TaintTrackingConfiguration {
49-
override predicate isSink(Element tainted) {
50-
exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
48+
Expr asSourceExpr(DataFlow::Node node) {
49+
result = node.asConvertedExpr()
50+
or
51+
result = node.asDefiningArgument()
52+
}
53+
54+
Expr asSinkExpr(DataFlow::Node node) {
55+
result =
56+
node.asOperand()
57+
.(SideEffectOperand)
58+
.getUse()
59+
.(ReadSideEffectInstruction)
60+
.getArgumentDef()
61+
.getUnconvertedResultExpression()
62+
}
63+
64+
/**
65+
* Holds for a variable that has any kind of upper-bound check anywhere in the program.
66+
* This is biased towards being inclusive and being a coarse overapproximation because
67+
* there are a lot of valid ways of doing an upper bounds checks if we don't consider
68+
* where it occurs, for example:
69+
* ```cpp
70+
* if (x < 10) { sink(x); }
71+
*
72+
* if (10 > y) { sink(y); }
73+
*
74+
* if (z > 10) { z = 10; }
75+
* sink(z);
76+
* ```
77+
*/
78+
predicate hasUpperBoundsCheck(Variable var) {
79+
exists(RelationalOperation oper, VariableAccess access |
80+
oper.getAnOperand() = access and
81+
access.getTarget() = var and
82+
// Comparing to 0 is not an upper bound check
83+
not oper.getAnOperand().getValue() = "0"
84+
)
85+
}
86+
87+
class TaintedPathConfiguration extends TaintTracking::Configuration {
88+
TaintedPathConfiguration() { this = "TaintedPathConfiguration" }
89+
90+
override predicate isSource(DataFlow::Node node) { isUserInput(asSourceExpr(node), _) }
91+
92+
override predicate isSink(DataFlow::Node node) {
93+
exists(FileFunction fileFunction |
94+
fileFunction.outermostWrapperFunctionCall(asSinkExpr(node), _)
95+
)
96+
}
97+
98+
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
99+
100+
override predicate isSanitizer(DataFlow::Node node) {
101+
node.asExpr().(Call).getTarget().getUnspecifiedType() instanceof ArithmeticType
102+
or
103+
exists(LoadInstruction load, Variable checkedVar |
104+
load = node.asInstruction() and
105+
checkedVar = load.getSourceAddress().(VariableAddressInstruction).getAstVariable() and
106+
hasUpperBoundsCheck(checkedVar)
107+
)
108+
}
109+
110+
predicate hasFilteredFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
111+
this.hasFlowPath(source, sink) and
112+
// The use of `isUserInput` in `isSink` in combination with `asSourceExpr` causes
113+
// duplicate results. Filter these duplicates. The proper solution is to switch to
114+
// using `LocalFlowSource` and `RemoteFlowSource`, but this currently only supports
115+
// a subset of the cases supported by `isUserInput`.
116+
not exists(DataFlow::PathNode source2 |
117+
this.hasFlowPath(source2, sink) and
118+
asSourceExpr(source.getNode()) = asSourceExpr(source2.getNode())
119+
|
120+
not exists(source.getNode().asConvertedExpr()) and exists(source2.getNode().asConvertedExpr())
121+
)
51122
}
52123
}
53124

54125
from
55-
FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
56-
PathNode sinkNode, string taintCause, string callChain
126+
FileFunction fileFunction, Expr taintedArg, Expr taintSource, TaintedPathConfiguration cfg,
127+
DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, string taintCause, string callChain
57128
where
58-
not isExcluded(taintedArg, IO3Package::doNotPerformFileOperationsOnDevicesQuery()) and
129+
taintedArg = asSinkExpr(sinkNode.getNode()) and
59130
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
60-
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
131+
cfg.hasFilteredFlowPath(sourceNode, sinkNode) and
132+
taintSource = asSourceExpr(sourceNode.getNode()) and
61133
isUserInput(taintSource, taintCause)
62134
select taintedArg, sourceNode, sinkNode,
63-
"This argument to a file access function is derived from $@ and then passed to " + callChain,
135+
"This argument to a file access function is derived from $@ and then passed to " + callChain + ".",
64136
taintSource, "user input (" + taintCause + ")"
Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,16 @@
11
edges
2-
| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | (const char *)... |
3-
| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name |
4-
| test.c:20:15:20:23 | array to pointer conversion | test.c:21:8:21:16 | file_name indirection |
5-
| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | (const char *)... |
6-
| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name |
72
| test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name indirection |
8-
| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | (const char *)... |
9-
| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name |
103
| test.c:20:15:20:23 | scanf output argument | test.c:21:8:21:16 | file_name indirection |
11-
| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | (LPCTSTR)... |
12-
| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name |
13-
| test.c:45:15:45:23 | array to pointer conversion | test.c:46:29:46:37 | file_name indirection |
14-
| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | (LPCTSTR)... |
15-
| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name |
164
| test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name indirection |
17-
| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | (LPCTSTR)... |
18-
| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name |
195
| test.c:45:15:45:23 | scanf output argument | test.c:46:29:46:37 | file_name indirection |
20-
subpaths
216
nodes
22-
| test.c:20:15:20:23 | array to pointer conversion | semmle.label | array to pointer conversion |
237
| test.c:20:15:20:23 | file_name | semmle.label | file_name |
248
| test.c:20:15:20:23 | scanf output argument | semmle.label | scanf output argument |
25-
| test.c:21:8:21:16 | (const char *)... | semmle.label | (const char *)... |
26-
| test.c:21:8:21:16 | file_name | semmle.label | file_name |
279
| test.c:21:8:21:16 | file_name indirection | semmle.label | file_name indirection |
28-
| test.c:45:15:45:23 | array to pointer conversion | semmle.label | array to pointer conversion |
2910
| test.c:45:15:45:23 | file_name | semmle.label | file_name |
3011
| test.c:45:15:45:23 | scanf output argument | semmle.label | scanf output argument |
31-
| test.c:46:29:46:37 | (LPCTSTR)... | semmle.label | (LPCTSTR)... |
32-
| test.c:46:29:46:37 | file_name | semmle.label | file_name |
3312
| test.c:46:29:46:37 | file_name indirection | semmle.label | file_name indirection |
13+
subpaths
3414
#select
35-
| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen((unnamed parameter 0)) | test.c:20:15:20:23 | file_name | user input (scanf) |
36-
| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName) | test.c:45:15:45:23 | file_name | user input (scanf) |
15+
| test.c:21:8:21:16 | file_name | test.c:20:15:20:23 | file_name | test.c:21:8:21:16 | file_name indirection | This argument to a file access function is derived from $@ and then passed to func(file_name), which calls fopen((unnamed parameter 0)). | test.c:20:15:20:23 | file_name | user input (scanf) |
16+
| test.c:46:29:46:37 | file_name | test.c:45:15:45:23 | file_name | test.c:46:29:46:37 | file_name indirection | This argument to a file access function is derived from $@ and then passed to CreateFile(lpFileName). | test.c:45:15:45:23 | file_name | user input (scanf) |

0 commit comments

Comments
 (0)