Skip to content

Commit eb39346

Browse files
authored
Merge pull request #1744 from jbj/ast-field-flow-aggregate-init
C++: Field flow through ClassAggregateLiteral
2 parents eead7f6 + 1b4b352 commit eb39346

File tree

7 files changed

+88
-20
lines changed

7 files changed

+88
-20
lines changed

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ private import DataFlowDispatch
66
private Node getInstanceArgument(Call call) {
77
result.asExpr() = call.getQualifier()
88
or
9-
result.(PreConstructorCallNode).getConstructorCall() = call
9+
result.(PreObjectInitializerNode).getExpr().(ConstructorCall) = call
1010
// This does not include the implicit `this` argument on auto-generated
1111
// base class destructor calls as those do not have an AST element.
1212
}
@@ -169,6 +169,14 @@ private class ArrayContent extends Content, TArrayContent {
169169
* value of `node1`.
170170
*/
171171
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
172+
exists(ClassAggregateLiteral aggr, Field field |
173+
// The following line requires `node2` to be both an `ExprNode` and a
174+
// `PostUpdateNode`, which means it must be an `ObjectInitializerNode`.
175+
node2.asExpr() = aggr and
176+
f.(FieldContent).getField() = field and
177+
aggr.getFieldExpr(field) = node1.asExpr()
178+
)
179+
or
172180
exists(FieldAccess fa |
173181
exists(Assignment a |
174182
node1.asExpr() = a and

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ cached
1010
private newtype TNode =
1111
TExprNode(Expr e) or
1212
TPartialDefinitionNode(PartialDefinition pd) or
13-
TPreConstructorCallNode(ConstructorCall call) or
13+
TPreObjectInitializerNode(Expr e) {
14+
e instanceof ConstructorCall
15+
or
16+
e instanceof ClassAggregateLiteral
17+
} or
1418
TExplicitParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
1519
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
1620
TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
@@ -256,36 +260,44 @@ class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
256260
}
257261

258262
/**
259-
* A node representing the object that was just constructed and is identified
260-
* with the "return value" of the constructor call.
263+
* A node representing the temporary value of an object that was just
264+
* constructed by a constructor call or an aggregate initializer. This is only
265+
* for objects, not for pointers to objects.
266+
*
267+
* These expressions are their own post-update nodes but instead have synthetic
268+
* pre-update nodes.
261269
*/
262-
private class PostConstructorCallNode extends PostUpdateNode, TExprNode {
263-
PostConstructorCallNode() { this = TExprNode(any(ConstructorCall c)) }
270+
private class ObjectInitializerNode extends PostUpdateNode, TExprNode {
271+
PreObjectInitializerNode pre;
264272

265-
override PreConstructorCallNode getPreUpdateNode() {
266-
TExprNode(result.getConstructorCall()) = this
273+
ObjectInitializerNode() {
274+
// If a `Node` is associated with a `PreObjectInitializerNode`, then it's
275+
// an `ObjectInitializerNode`.
276+
pre.getExpr() = this.asExpr()
267277
}
268278

279+
override PreObjectInitializerNode getPreUpdateNode() { result = pre }
280+
269281
// No override of `toString` since these nodes already have a `toString` from
270282
// their overlap with `ExprNode`.
271283
}
272284

273285
/**
274286
* INTERNAL: do not use.
275287
*
276-
* A synthetic data-flow node that plays the role of the qualifier (or
277-
* `this`-argument) to a constructor call.
288+
* A synthetic data-flow node that plays the role of a temporary object that
289+
* has not yet been initialized.
278290
*/
279-
class PreConstructorCallNode extends Node, TPreConstructorCallNode {
280-
ConstructorCall getConstructorCall() { this = TPreConstructorCallNode(result) }
291+
class PreObjectInitializerNode extends Node, TPreObjectInitializerNode {
292+
Expr getExpr() { this = TPreObjectInitializerNode(result) }
281293

282-
override Function getFunction() { result = getConstructorCall().getEnclosingFunction() }
294+
override Function getFunction() { result = getExpr().getEnclosingFunction() }
283295

284-
override Type getType() { result = getConstructorCall().getType() }
296+
override Type getType() { result = getExpr().getType() }
285297

286-
override Location getLocation() { result = getConstructorCall().getLocation() }
298+
override Location getLocation() { result = getExpr().getLocation() }
287299

288-
override string toString() { result = getConstructorCall().toString() + " [pre constructor call]" }
300+
override string toString() { result = getExpr().toString() + " [pre init]" }
289301
}
290302

291303
/**

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,15 @@ void following_pointers(
142142
twoIntFields s = { source(), source() };
143143

144144

145-
sink(s.m2); // flow (AST dataflow misses this due to limitations of the analysis)
145+
sink(s.m2); // flow
146146

147147
twoIntFields sArray[1] = { { source(), source() } };
148148
// TODO: fix this like above
149149
sink(sArray[0].m2); // no flow (due to limitations of the analysis)
150150

151151
twoIntFields sSwapped = { .m2 = source(), .m1 = 0 };
152152

153-
sink(sSwapped.m2); // flow (AST dataflow misses this due to limitations of the analysis)
153+
sink(sSwapped.m2); // flow
154154

155155
sink(sourceFunctionPointer()); // no flow
156156

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
| test.cpp:137:27:137:28 | m1 | test.cpp:136:27:136:32 | call to source |
2020
| test.cpp:138:27:138:34 | call to getFirst | test.cpp:136:27:136:32 | call to source |
2121
| test.cpp:140:22:140:23 | m1 | test.cpp:136:27:136:32 | call to source |
22+
| test.cpp:145:10:145:11 | m2 | test.cpp:142:32:142:37 | call to source |
23+
| test.cpp:153:17:153:18 | m2 | test.cpp:151:35:151:40 | call to source |
2224
| test.cpp:188:8:188:8 | y | test.cpp:186:27:186:32 | call to source |
2325
| test.cpp:192:8:192:8 | s | test.cpp:199:33:199:38 | call to source |
2426
| test.cpp:200:8:200:8 | y | test.cpp:199:33:199:38 | call to source |

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
| test.cpp:136:27:136:32 | test.cpp:137:27:137:28 | AST only |
77
| test.cpp:136:27:136:32 | test.cpp:138:27:138:34 | AST only |
88
| test.cpp:136:27:136:32 | test.cpp:140:22:140:23 | AST only |
9-
| test.cpp:142:32:142:37 | test.cpp:145:10:145:11 | IR only |
10-
| test.cpp:151:35:151:40 | test.cpp:153:17:153:18 | IR only |
119
| test.cpp:395:17:395:22 | test.cpp:397:10:397:18 | AST only |
1210
| test.cpp:407:13:407:18 | test.cpp:413:10:413:14 | AST only |
1311
| test.cpp:421:13:421:18 | test.cpp:417:10:417:14 | AST only |

cpp/ql/test/library-tests/dataflow/fields/flow.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ edges
9898
| simple.cpp:48:9:48:9 | g [b_, ... (1)] | simple.cpp:26:15:26:15 | f [b_, ... (1)] |
9999
| simple.cpp:51:9:51:9 | h [a_, ... (1)] | simple.cpp:26:15:26:15 | f [a_, ... (1)] |
100100
| simple.cpp:51:9:51:9 | h [b_, ... (1)] | simple.cpp:26:15:26:15 | f [b_, ... (1)] |
101+
| struct_init.c:20:17:20:36 | {...} [a, ... (1)] | struct_init.c:22:8:22:9 | ab [a, ... (1)] |
102+
| struct_init.c:20:20:20:29 | call to user_input [void] | struct_init.c:20:17:20:36 | {...} [a, ... (1)] |
103+
| struct_init.c:22:8:22:9 | ab [a, ... (1)] | struct_init.c:22:11:22:11 | a |
104+
| struct_init.c:26:23:29:3 | {...} [nestedAB, ... (2)] | struct_init.c:31:8:31:12 | outer [nestedAB, ... (2)] |
105+
| struct_init.c:27:5:27:23 | {...} [a, ... (1)] | struct_init.c:26:23:29:3 | {...} [nestedAB, ... (2)] |
106+
| struct_init.c:27:7:27:16 | call to user_input [void] | struct_init.c:27:5:27:23 | {...} [a, ... (1)] |
107+
| struct_init.c:31:8:31:12 | outer [nestedAB, ... (2)] | struct_init.c:31:14:31:21 | nestedAB [a, ... (1)] |
108+
| struct_init.c:31:14:31:21 | nestedAB [a, ... (1)] | struct_init.c:31:23:31:23 | a |
101109
#select
102110
| A.cpp:43:10:43:12 | & ... | A.cpp:41:15:41:21 | new [void] | A.cpp:43:10:43:12 | & ... | & ... flows from $@ | A.cpp:41:15:41:21 | new [void] | new [void] |
103111
| A.cpp:49:13:49:13 | c | A.cpp:47:12:47:18 | new [void] | A.cpp:49:13:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new [void] | new [void] |
@@ -121,3 +129,5 @@ edges
121129
| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input [void] | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input [void] | call to user_input [void] |
122130
| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input [void] | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input [void] | call to user_input [void] |
123131
| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input [void] | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input [void] | call to user_input [void] |
132+
| struct_init.c:22:11:22:11 | a | struct_init.c:20:20:20:29 | call to user_input [void] | struct_init.c:22:11:22:11 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input [void] | call to user_input [void] |
133+
| struct_init.c:31:23:31:23 | a | struct_init.c:27:7:27:16 | call to user_input [void] | struct_init.c:31:23:31:23 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input [void] | call to user_input [void] |
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
void sink(void *o);
2+
void *user_input(void);
3+
4+
struct AB {
5+
void *a;
6+
void *b;
7+
};
8+
9+
struct Outer {
10+
struct AB nestedAB;
11+
struct AB *pointerAB;
12+
};
13+
14+
void absink(struct AB *ab) {
15+
sink(ab->a); // flow x3 [NOT DETECTED]
16+
sink(ab->b); // no flow
17+
}
18+
19+
int struct_init(void) {
20+
struct AB ab = { user_input(), 0 };
21+
22+
sink(ab.a); // flow
23+
sink(ab.b); // no flow
24+
absink(&ab);
25+
26+
struct Outer outer = {
27+
{ user_input(), 0 },
28+
&ab,
29+
};
30+
31+
sink(outer.nestedAB.a); // flow
32+
sink(outer.nestedAB.b); // no flow
33+
sink(outer.pointerAB->a); // flow [NOT DETECTED]
34+
sink(outer.pointerAB->b); // no flow
35+
36+
absink(&outer.nestedAB);
37+
absink(outer.pointerAB);
38+
}

0 commit comments

Comments
 (0)