Skip to content

Commit 76e194c

Browse files
committed
C++: Fix struct field conflation in IR data flow
The virtual-dispatch code for globals was missing any relationship between the union field access and the global variable, which meant it propagated function-pointer flow between any two fields of a global struct. This resulted in false positives from `cpp/tainted-format-string` on projects using SDL, such as WohlSoft/PGE-Project. In addition to fixing that bug, this commit also brings the code up to date with the new style of modeling flow through global variables: `DataFlow::Node.asVariable()`.
1 parent f2402c5 commit 76e194c

File tree

4 files changed

+41
-54
lines changed

4 files changed

+41
-54
lines changed

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -77,49 +77,48 @@ private module VirtualDispatch {
7777
// Local flow
7878
DataFlow::localFlowStep(src, other) and
7979
allowFromArg = allowOtherFromArg
80-
)
81-
or
82-
// Flow through global variable
83-
exists(StoreInstruction store |
84-
store = src.asInstruction() and
85-
(
86-
exists(Variable var |
87-
var = store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() and
88-
this.flowsFromGlobal(var)
80+
or
81+
// Flow from global variable to load.
82+
exists(LoadInstruction load, GlobalOrNamespaceVariable var |
83+
var = src.asVariable() and
84+
other.asInstruction() = load and
85+
// The `allowFromArg` concept doesn't play a role when `src` is a
86+
// global variable, so we just set it to a single arbitrary value for
87+
// performance.
88+
allowFromArg = true
89+
|
90+
// Load directly from the global variable
91+
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
92+
or
93+
// Load from a field on a global union
94+
exists(FieldAddressInstruction fa |
95+
fa = load.getSourceAddress() and
96+
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
97+
fa.getField().getDeclaringType() instanceof Union
8998
)
99+
)
100+
or
101+
// Flow from store to global variable. These cases are similar to the
102+
// above but have `StoreInstruction` instead of `LoadInstruction` and
103+
// have the roles swapped between `other` and `src`.
104+
exists(StoreInstruction store, GlobalOrNamespaceVariable var |
105+
var = other.asVariable() and
106+
store = src.asInstruction() and
107+
// Setting `allowFromArg` to `true` like in the base case means we
108+
// treat a store to a global variable like the dispatch itself: flow
109+
// may come from anywhere.
110+
allowFromArg = true
111+
|
112+
// Store directly to the global variable
113+
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
90114
or
91-
exists(Variable var, FieldAccess a |
92-
var =
93-
store
94-
.getDestinationAddress()
95-
.(FieldAddressInstruction)
96-
.getObjectAddress()
97-
.(VariableAddressInstruction)
98-
.getASTVariable() and
99-
this.flowsFromGlobalUnionField(var, a)
115+
// Store to a field on a global union
116+
exists(FieldAddressInstruction fa |
117+
fa = store.getDestinationAddress() and
118+
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
119+
fa.getField().getDeclaringType() instanceof Union
100120
)
101-
) and
102-
allowFromArg = true
103-
)
104-
}
105-
106-
private predicate flowsFromGlobal(GlobalOrNamespaceVariable var) {
107-
exists(LoadInstruction load |
108-
this.flowsFrom(DataFlow::instructionNode(load), _) and
109-
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
110-
)
111-
}
112-
113-
private predicate flowsFromGlobalUnionField(Variable var, FieldAccess a) {
114-
a.getTarget().getDeclaringType() instanceof Union and
115-
exists(LoadInstruction load |
116-
this.flowsFrom(DataFlow::instructionNode(load), _) and
117-
load
118-
.getSourceAddress()
119-
.(FieldAddressInstruction)
120-
.getObjectAddress()
121-
.(VariableAddressInstruction)
122-
.getASTVariable() = var
121+
)
123122
)
124123
}
125124
}

cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/dispatch.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ void assignGlobals() {
2525
};
2626

2727
void testStruct() {
28-
globalStruct.sinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam [NOT DETECTED in AST]
29-
globalStruct.notSinkPtr(atoi(getenv("TAINTED"))); // shouldn't reach sinkParam [FALSE POSITIVE in IR]
28+
globalStruct.sinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam [NOT DETECTED]
29+
globalStruct.notSinkPtr(atoi(getenv("TAINTED"))); // shouldn't reach sinkParam
3030

3131
globalUnion.sinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam
3232
globalUnion.notSinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam

cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,13 @@
9898
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... |
9999
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 |
100100
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | shared.h:5:23:5:31 | sinkparam |
101-
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam |
102-
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
103101
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:24:28:27 | call to atoi |
104102
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:34 | call to getenv |
105103
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:45 | (const char *)... |
106-
| dispatch.cpp:28:29:28:34 | call to getenv | shared.h:6:15:6:23 | sinkparam |
107104
| dispatch.cpp:28:29:28:34 | call to getenv | shared.h:8:22:8:25 | nptr |
108-
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam |
109-
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |
110105
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:27:29:30 | call to atoi |
111106
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:32:29:37 | call to getenv |
112107
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:29:32:29:48 | (const char *)... |
113-
| dispatch.cpp:29:32:29:37 | call to getenv | shared.h:6:15:6:23 | sinkparam |
114108
| dispatch.cpp:29:32:29:37 | call to getenv | shared.h:8:22:8:25 | nptr |
115109
| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam |
116110
| dispatch.cpp:31:28:31:33 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam |

cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,6 @@
2020
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... | IR only |
2121
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 | IR only |
2222
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | shared.h:5:23:5:31 | sinkparam | IR only |
23-
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam | IR only |
24-
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam | IR only |
25-
| dispatch.cpp:28:29:28:34 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
26-
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:7:20:7:28 | sinkParam | IR only |
27-
| dispatch.cpp:29:32:29:37 | call to getenv | dispatch.cpp:8:8:8:16 | sinkParam | IR only |
28-
| dispatch.cpp:29:32:29:37 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
2923
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
3024
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
3125
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |

0 commit comments

Comments
 (0)