Skip to content

Commit 57dbe57

Browse files
authored
Merge pull request github#3501 from jbj/dispatch-global-union
C++: Fix struct field conflation in IR data flow
2 parents bd6d2d8 + 76e194c commit 57dbe57

File tree

9 files changed

+166
-112
lines changed

9 files changed

+166
-112
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/defaulttainttracking.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
int atoi(const char *nptr);
2-
char *getenv(const char *name);
3-
char *strcat(char * s1, const char * s2);
1+
#include "shared.h"
2+
3+
4+
5+
6+
7+
8+
49

5-
char *strdup(const char *);
6-
char *_strdup(const char *);
7-
char *unmodeled_function(const char *);
810

9-
void sink(const char *);
10-
void sink(int);
1111

1212
int main(int argc, char *argv[]) {
1313

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "shared.h"
2+
3+
using SinkFunction = void (*)(int);
4+
5+
void notSink(int notSinkParam);
6+
7+
void callsSink(int sinkParam) {
8+
sink(sinkParam);
9+
}
10+
11+
struct {
12+
SinkFunction sinkPtr, notSinkPtr;
13+
} globalStruct;
14+
15+
union {
16+
SinkFunction sinkPtr, notSinkPtr;
17+
} globalUnion;
18+
19+
SinkFunction globalSinkPtr;
20+
21+
void assignGlobals() {
22+
globalStruct.sinkPtr = callsSink;
23+
globalUnion.sinkPtr = callsSink;
24+
globalSinkPtr = callsSink;
25+
};
26+
27+
void testStruct() {
28+
globalStruct.sinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam [NOT DETECTED]
29+
globalStruct.notSinkPtr(atoi(getenv("TAINTED"))); // shouldn't reach sinkParam
30+
31+
globalUnion.sinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam
32+
globalUnion.notSinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam
33+
34+
globalSinkPtr(atoi(getenv("TAINTED"))); // should reach sinkParam
35+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:2:17:2:25 | sinkParam | global1 |
1+
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:12:10:12:16 | (const char *)... | global1 |
22
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:12:10:12:16 | global1 | global1 |
3-
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:2:17:2:25 | sinkParam | global2 |
3+
| globals.cpp:13:15:13:20 | call to getenv | shared.h:5:23:5:31 | sinkparam | global1 |
4+
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:19:10:19:16 | (const char *)... | global2 |
45
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:19:10:19:16 | global2 | global2 |
6+
| globals.cpp:23:15:23:20 | call to getenv | shared.h:5:23:5:31 | sinkparam | global2 |

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
char * getenv(const char *);
2-
void sink(char *sinkParam);
1+
#include "shared.h"
2+
33

44
void throughLocal() {
55
char * local = getenv("VAR");
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Common declarations in this test dir should go in this file. Otherwise, some
2+
// declarations will have multiple locations, which leads to confusing test
3+
// output.
4+
5+
void sink(const char *sinkparam);
6+
void sink(int sinkparam);
7+
8+
int atoi(const char *nptr);
9+
char *getenv(const char *name);
10+
char *strcat(char * s1, const char * s2);
11+
12+
char *strdup(const char *string);
13+
char *_strdup(const char *string);
14+
char *unmodeled_function(const char *const_string);

0 commit comments

Comments
 (0)