Merge pull request #1744 from jbj/ast-field-flow-aggregate-init

C++: Field flow through ClassAggregateLiteral
This commit is contained in:
Geoffrey White
2019-08-16 09:56:11 +01:00
committed by GitHub
7 changed files with 88 additions and 20 deletions

View File

@@ -6,7 +6,7 @@ private import DataFlowDispatch
private Node getInstanceArgument(Call call) {
result.asExpr() = call.getQualifier()
or
result.(PreConstructorCallNode).getConstructorCall() = call
result.(PreObjectInitializerNode).getExpr().(ConstructorCall) = call
// This does not include the implicit `this` argument on auto-generated
// base class destructor calls as those do not have an AST element.
}
@@ -169,6 +169,14 @@ private class ArrayContent extends Content, TArrayContent {
* value of `node1`.
*/
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
exists(ClassAggregateLiteral aggr, Field field |
// The following line requires `node2` to be both an `ExprNode` and a
// `PostUpdateNode`, which means it must be an `ObjectInitializerNode`.
node2.asExpr() = aggr and
f.(FieldContent).getField() = field and
aggr.getFieldExpr(field) = node1.asExpr()
)
or
exists(FieldAccess fa |
exists(Assignment a |
node1.asExpr() = a and

View File

@@ -10,7 +10,11 @@ cached
private newtype TNode =
TExprNode(Expr e) or
TPartialDefinitionNode(PartialDefinition pd) or
TPreConstructorCallNode(ConstructorCall call) or
TPreObjectInitializerNode(Expr e) {
e instanceof ConstructorCall
or
e instanceof ClassAggregateLiteral
} or
TExplicitParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
@@ -256,16 +260,24 @@ class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
}
/**
* A node representing the object that was just constructed and is identified
* with the "return value" of the constructor call.
* A node representing the temporary value of an object that was just
* constructed by a constructor call or an aggregate initializer. This is only
* for objects, not for pointers to objects.
*
* These expressions are their own post-update nodes but instead have synthetic
* pre-update nodes.
*/
private class PostConstructorCallNode extends PostUpdateNode, TExprNode {
PostConstructorCallNode() { this = TExprNode(any(ConstructorCall c)) }
private class ObjectInitializerNode extends PostUpdateNode, TExprNode {
PreObjectInitializerNode pre;
override PreConstructorCallNode getPreUpdateNode() {
TExprNode(result.getConstructorCall()) = this
ObjectInitializerNode() {
// If a `Node` is associated with a `PreObjectInitializerNode`, then it's
// an `ObjectInitializerNode`.
pre.getExpr() = this.asExpr()
}
override PreObjectInitializerNode getPreUpdateNode() { result = pre }
// No override of `toString` since these nodes already have a `toString` from
// their overlap with `ExprNode`.
}
@@ -273,19 +285,19 @@ private class PostConstructorCallNode extends PostUpdateNode, TExprNode {
/**
* INTERNAL: do not use.
*
* A synthetic data-flow node that plays the role of the qualifier (or
* `this`-argument) to a constructor call.
* A synthetic data-flow node that plays the role of a temporary object that
* has not yet been initialized.
*/
class PreConstructorCallNode extends Node, TPreConstructorCallNode {
ConstructorCall getConstructorCall() { this = TPreConstructorCallNode(result) }
class PreObjectInitializerNode extends Node, TPreObjectInitializerNode {
Expr getExpr() { this = TPreObjectInitializerNode(result) }
override Function getFunction() { result = getConstructorCall().getEnclosingFunction() }
override Function getFunction() { result = getExpr().getEnclosingFunction() }
override Type getType() { result = getConstructorCall().getType() }
override Type getType() { result = getExpr().getType() }
override Location getLocation() { result = getConstructorCall().getLocation() }
override Location getLocation() { result = getExpr().getLocation() }
override string toString() { result = getConstructorCall().toString() + " [pre constructor call]" }
override string toString() { result = getExpr().toString() + " [pre init]" }
}
/**

View File

@@ -142,7 +142,7 @@ void following_pointers(
twoIntFields s = { source(), source() };
sink(s.m2); // flow (AST dataflow misses this due to limitations of the analysis)
sink(s.m2); // flow
twoIntFields sArray[1] = { { source(), source() } };
// TODO: fix this like above
@@ -150,7 +150,7 @@ void following_pointers(
twoIntFields sSwapped = { .m2 = source(), .m1 = 0 };
sink(sSwapped.m2); // flow (AST dataflow misses this due to limitations of the analysis)
sink(sSwapped.m2); // flow
sink(sourceFunctionPointer()); // no flow

View File

@@ -19,6 +19,8 @@
| test.cpp:137:27:137:28 | m1 | test.cpp:136:27:136:32 | call to source |
| test.cpp:138:27:138:34 | call to getFirst | test.cpp:136:27:136:32 | call to source |
| test.cpp:140:22:140:23 | m1 | test.cpp:136:27:136:32 | call to source |
| test.cpp:145:10:145:11 | m2 | test.cpp:142:32:142:37 | call to source |
| test.cpp:153:17:153:18 | m2 | test.cpp:151:35:151:40 | call to source |
| test.cpp:188:8:188:8 | y | test.cpp:186:27:186:32 | call to source |
| test.cpp:192:8:192:8 | s | test.cpp:199:33:199:38 | call to source |
| test.cpp:200:8:200:8 | y | test.cpp:199:33:199:38 | call to source |

View File

@@ -6,8 +6,6 @@
| test.cpp:136:27:136:32 | test.cpp:137:27:137:28 | AST only |
| test.cpp:136:27:136:32 | test.cpp:138:27:138:34 | AST only |
| test.cpp:136:27:136:32 | test.cpp:140:22:140:23 | AST only |
| test.cpp:142:32:142:37 | test.cpp:145:10:145:11 | IR only |
| test.cpp:151:35:151:40 | test.cpp:153:17:153:18 | IR only |
| test.cpp:395:17:395:22 | test.cpp:397:10:397:18 | AST only |
| test.cpp:407:13:407:18 | test.cpp:413:10:413:14 | AST only |
| test.cpp:421:13:421:18 | test.cpp:417:10:417:14 | AST only |

View File

@@ -98,6 +98,14 @@ edges
| simple.cpp:48:9:48:9 | g [b_, ... (1)] | simple.cpp:26:15:26:15 | f [b_, ... (1)] |
| simple.cpp:51:9:51:9 | h [a_, ... (1)] | simple.cpp:26:15:26:15 | f [a_, ... (1)] |
| simple.cpp:51:9:51:9 | h [b_, ... (1)] | simple.cpp:26:15:26:15 | f [b_, ... (1)] |
| struct_init.c:20:17:20:36 | {...} [a, ... (1)] | struct_init.c:22:8:22:9 | ab [a, ... (1)] |
| struct_init.c:20:20:20:29 | call to user_input [void] | struct_init.c:20:17:20:36 | {...} [a, ... (1)] |
| struct_init.c:22:8:22:9 | ab [a, ... (1)] | struct_init.c:22:11:22:11 | a |
| struct_init.c:26:23:29:3 | {...} [nestedAB, ... (2)] | struct_init.c:31:8:31:12 | outer [nestedAB, ... (2)] |
| struct_init.c:27:5:27:23 | {...} [a, ... (1)] | struct_init.c:26:23:29:3 | {...} [nestedAB, ... (2)] |
| struct_init.c:27:7:27:16 | call to user_input [void] | struct_init.c:27:5:27:23 | {...} [a, ... (1)] |
| struct_init.c:31:8:31:12 | outer [nestedAB, ... (2)] | struct_init.c:31:14:31:21 | nestedAB [a, ... (1)] |
| struct_init.c:31:14:31:21 | nestedAB [a, ... (1)] | struct_init.c:31:23:31:23 | a |
#select
| 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] |
| 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
| 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] |
| 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] |
| 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] |
| 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] |
| 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] |

View File

@@ -0,0 +1,38 @@
void sink(void *o);
void *user_input(void);
struct AB {
void *a;
void *b;
};
struct Outer {
struct AB nestedAB;
struct AB *pointerAB;
};
void absink(struct AB *ab) {
sink(ab->a); // flow x3 [NOT DETECTED]
sink(ab->b); // no flow
}
int struct_init(void) {
struct AB ab = { user_input(), 0 };
sink(ab.a); // flow
sink(ab.b); // no flow
absink(&ab);
struct Outer outer = {
{ user_input(), 0 },
&ab,
};
sink(outer.nestedAB.a); // flow
sink(outer.nestedAB.b); // no flow
sink(outer.pointerAB->a); // flow [NOT DETECTED]
sink(outer.pointerAB->b); // no flow
absink(&outer.nestedAB);
absink(outer.pointerAB);
}