C++: Field flow through ConstructorFieldInit

This allows a member initializer list to be seen as a sequence of field
assignments. For example, the constructor

    C() : a(taint()) { }

now has data flow similar to

    C() { this.a = taint(); }
This commit is contained in:
Jonas Jensen
2019-08-15 08:56:05 +02:00
parent 1be2380511
commit 45eefdb218
8 changed files with 90 additions and 4 deletions

View File

@@ -178,6 +178,12 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
node2.getPreUpdateNode().asExpr() = fa.getQualifier() and
f.(FieldContent).getField() = fa.getTarget()
)
or
exists(ConstructorFieldInit cfi |
node2.getPreUpdateNode().(PreConstructorInitThis).getConstructorFieldInit() = cfi and
f.(FieldContent).getField() = cfi.getTarget() and
node1.asExpr() = cfi.getExpr()
)
}
/**

View File

@@ -13,6 +13,8 @@ private newtype TNode =
TPreConstructorCallNode(ConstructorCall call) or
TExplicitParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
TPreConstructorInitThis(ConstructorFieldInit cfi) or
TPostConstructorInitThis(ConstructorFieldInit cfi) or
TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
/**
@@ -288,6 +290,44 @@ class PreConstructorCallNode extends Node, TPreConstructorCallNode {
override string toString() { result = getConstructorCall().toString() + " [pre constructor call]" }
}
/**
* A synthetic data-flow node that plays the role of the post-update `this`
* pointer in a `ConstructorFieldInit`. For example, the `x(1)` in
* `C() : x(1) { }` is roughly equivalent to `this.x = 1`, and this node is
* equivalent to the `this` _after_ the field has been assigned.
*/
private class PostConstructorInitThis extends PostUpdateNode, TPostConstructorInitThis {
override PreConstructorInitThis getPreUpdateNode() {
this = TPostConstructorInitThis(result.getConstructorFieldInit())
}
override string toString() {
result = getPreUpdateNode().getConstructorFieldInit().toString() + " [post-this]"
}
}
/**
* INTERNAL: do not use.
*
* A synthetic data-flow node that plays the role of the pre-update `this`
* pointer in a `ConstructorFieldInit`. For example, the `x(1)` in
* `C() : x(1) { }` is roughly equivalent to `this.x = 1`, and this node is
* equivalent to the `this` _before_ the field has been assigned.
*/
class PreConstructorInitThis extends Node, TPreConstructorInitThis {
ConstructorFieldInit getConstructorFieldInit() { this = TPreConstructorInitThis(result) }
override Constructor getFunction() { result = getConstructorFieldInit().getEnclosingFunction() }
override PointerType getType() {
result.getBaseType() = getConstructorFieldInit().getEnclosingFunction().getDeclaringType()
}
override Location getLocation() { result = getConstructorFieldInit().getLocation() }
override string toString() { result = getConstructorFieldInit().toString() + " [pre-this]" }
}
/**
* Gets the `Node` corresponding to `e`.
*/
@@ -313,6 +353,12 @@ DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
private module ThisFlow {
/**
* Gets the 0-based index of `thisNode` in `b`, where `thisNode` is an access
* to `this` that may or may not have an associated `PostUpdateNode`. To make
* room for synthetic nodes that access `this`, the index may not correspond
* to an actual `ControlFlowNode`.
*/
private int basicBlockThisIndex(BasicBlock b, Node thisNode) {
// The implicit `this` parameter node is given a very negative offset to
// make space for any `ConstructorFieldInit`s there may be between it and
@@ -320,6 +366,16 @@ private module ThisFlow {
thisNode.(ImplicitParameterNode).getFunction().getBlock() = b and
result = -1000
or
// Place the synthetic `this` node for a `ConstructorFieldInit` at a
// negative offset in the first basic block, between the
// `ImplicitParameterNode` and the first statement.
exists(Constructor constructor, int i |
thisNode.(PreConstructorInitThis).getConstructorFieldInit() =
constructor.getInitializer(i) and
result = -999 + i and
b = thisNode.getFunction().getBlock()
)
or
b.getNode(result) = thisNode.asExpr().(ThisExpr)
}