mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #1744 from jbj/ast-field-flow-aggregate-init
C++: Field flow through ClassAggregateLiteral
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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]" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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] |
|
||||
|
||||
38
cpp/ql/test/library-tests/dataflow/fields/struct_init.c
Normal file
38
cpp/ql/test/library-tests/dataflow/fields/struct_init.c
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user