Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5ebefe2

Browse files
committed
C++: Throw away the old way of doing store steps using memory edges. Instead, we introduce a StoreNode IPA branch that does store steps and instead use the shared SSA library to transfer flow into these nodes before a store step, and out of them following a sequence of store steps.
1 parent 1842fed commit 5ebefe2

3 files changed

Lines changed: 268 additions & 129 deletions

File tree

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 6 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -184,108 +184,17 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
184184
*/
185185
predicate jumpStep(Node n1, Node n2) { none() }
186186

187-
private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
188-
exists(StoreInstruction store, Class c |
189-
store = node2.asInstruction() and
190-
store.getSourceValueOperand() = node1.asOperand() and
191-
getWrittenField(store, f.(FieldContent).getAField(), c) and
192-
f.hasOffset(c, _, _)
193-
)
194-
}
195-
196-
private FieldAddressInstruction getFieldInstruction(Instruction instr) {
197-
result = instr or
198-
result = instr.(CopyValueInstruction).getUnary()
199-
}
200-
201-
pragma[noinline]
202-
private predicate getWrittenField(Instruction instr, Field f, Class c) {
203-
exists(FieldAddressInstruction fa |
204-
fa =
205-
getFieldInstruction([
206-
instr.(StoreInstruction).getDestinationAddress(),
207-
instr.(WriteSideEffectInstruction).getDestinationAddress()
208-
]) and
209-
f = fa.getField() and
210-
c = f.getDeclaringType()
211-
)
212-
}
213-
214-
private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) {
215-
exists(ChiPartialOperand operand, ChiInstruction chi |
216-
chi.getPartialOperand() = operand and
217-
node1.asOperand() = operand and
218-
node2.asInstruction() = chi and
219-
exists(Class c |
220-
c = chi.getResultType() and
221-
exists(int startBit, int endBit |
222-
chi.getUpdatedInterval(startBit, endBit) and
223-
f.hasOffset(c, startBit, endBit)
224-
)
225-
or
226-
getWrittenField(operand.getDef(), f.getAField(), c) and
227-
f.hasOffset(c, _, _)
228-
)
229-
)
230-
}
231-
232-
private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
233-
exists(a) and
234-
exists(ChiPartialOperand operand, ChiInstruction chi, StoreInstruction store |
235-
chi.getPartialOperand() = operand and
236-
store = operand.getDef() and
237-
node1.asOperand() = operand and
238-
// This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
239-
// and `PointerStoreNode` require it in their characteristic predicates.
240-
node2.asInstruction() = chi and
241-
(
242-
// `x[i] = taint()`
243-
// This matches the characteristic predicate in `ArrayStoreNode`.
244-
store.getDestinationAddress() instanceof PointerAddInstruction
245-
or
246-
// `*p = taint()`
247-
// This matches the characteristic predicate in `PointerStoreNode`.
248-
store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
249-
)
250-
)
251-
}
252-
253187
/**
254188
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
255189
* Thus, `node2` references an object with a field `f` that contains the
256190
* value of `node1`.
257191
*/
258-
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
259-
fieldStoreStepNoChi(node1, f, node2) or
260-
fieldStoreStepChi(node1, f, node2) or
261-
arrayStoreStepChi(node1, f, node2) or
262-
fieldStoreStepAfterArraySuppression(node1, f, node2)
263-
}
264-
265-
// This predicate pushes the correct `FieldContent` onto the access path when the
266-
// `suppressArrayRead` predicate has popped off an `ArrayContent`.
267-
private predicate fieldStoreStepAfterArraySuppression(
268-
Node node1, FieldContent f, PostUpdateNode node2
269-
) {
270-
exists(WriteSideEffectInstruction write, ChiInstruction chi, Class c |
271-
not chi.isResultConflated() and
272-
node1.asInstruction() = chi and
273-
node2.asInstruction() = chi and
274-
chi.getPartial() = write and
275-
getWrittenField(write, f.getAField(), c) and
276-
f.hasOffset(c, _, _)
277-
)
278-
}
279-
280-
bindingset[result, i]
281-
private int unbindInt(int i) { i <= result and i >= result }
282-
283-
pragma[noinline]
284-
private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
285-
exists(FieldAddressInstruction fa |
286-
fa = load.getSourceAddress() and
287-
f = fa.getField() and
288-
c = f.getDeclaringType()
192+
predicate storeStep(StoreNode node1, FieldContent f, StoreNode node2) {
193+
exists(FieldAddressInstruction fai |
194+
not fai.getObjectAddress().getResultType().stripType() instanceof Union and
195+
node1.getInstruction() = fai and
196+
node2.getInstruction() = fai.getObjectAddress() and
197+
f.getField() = fai.getField()
289198
)
290199
}
291200

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 151 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,37 @@ private import semmle.code.cpp.ir.ValueNumbering
1010
private import semmle.code.cpp.ir.IR
1111
private import semmle.code.cpp.controlflow.IRGuards
1212
private import semmle.code.cpp.models.interfaces.DataFlow
13+
private import DataFlowPrivate
14+
private import Ssa as Ssa
1315

1416
cached
1517
private module Cached {
1618
cached
1719
newtype TIRDataFlowNode =
1820
TInstructionNode(Instruction i) or
1921
TOperandNode(Operand op) or
20-
TVariableNode(Variable var)
22+
TVariableNode(Variable var) or
23+
TStoreNodeInstr(Instruction i) { Ssa::explicitWrite(_, _, i) } or
24+
TStoreNodeOperand(ArgumentOperand op) { Ssa::explicitWrite(_, _, op.getDef()) }
2125

2226
cached
2327
predicate localFlowStepCached(Node nodeFrom, Node nodeTo) {
2428
simpleLocalFlowStep(nodeFrom, nodeTo)
2529
}
30+
31+
private predicate needsPostReadNode(Instruction iFrom) {
32+
// If the instruction generates an address that flows to a load.
33+
Ssa::addressFlowTC(iFrom, Ssa::getSourceAddress(_)) and
34+
(
35+
// And it is either a field address
36+
iFrom instanceof FieldAddressInstruction
37+
or
38+
// Or it is instruction that either uses or is used for an address that needs a post read node.
39+
exists(Instruction mid | needsPostReadNode(mid) |
40+
Ssa::addressFlow(mid, iFrom) or Ssa::addressFlow(iFrom, mid)
41+
)
42+
)
43+
}
2644
}
2745

2846
private import Cached
@@ -180,6 +198,99 @@ class OperandNode extends Node, TOperandNode {
180198
override string toString() { result = this.getOperand().toString() }
181199
}
182200

201+
/**
202+
* INTERNAL: do not use.
203+
*
204+
* A `StoreNode` is a node that has been (or is about to be) the
205+
* source or target of a `storeStep`.
206+
*/
207+
abstract class StoreNode extends Node {
208+
/** Gets the underlying instruction, if any. */
209+
Instruction getInstruction() { none() }
210+
211+
/** Gets the underlying operand, if any. */
212+
Operand getOperand() { none() }
213+
214+
/** Holds if this node should receive flow from `addr`. */
215+
abstract predicate flowInto(Instruction addr);
216+
217+
override Declaration getEnclosingCallable() { result = this.getFunction() }
218+
219+
/** Holds if this `StoreNode` is the root of the address computation used by a store operation. */
220+
predicate isTerminal() {
221+
not exists(this.getAPredecessor()) and
222+
not storeStep(this, _, _)
223+
}
224+
225+
/** Gets the store operation that uses the address computed by this `StoreNode`. */
226+
abstract Instruction getStoreInstruction();
227+
228+
/** Holds if the store operation associated with this `StoreNode` overwrites the entire variable. */
229+
final predicate isCertain() { Ssa::explicitWrite(true, this.getStoreInstruction(), _) }
230+
231+
/**
232+
* Gets the `StoreNode` that computes the address used by this `StoreNode`.
233+
* The boolean `readEffect` is `true` if the predecessor is accessed through the
234+
* address of a `ReadSideEffectInstruction`.
235+
*/
236+
abstract StoreNode getAPredecessor();
237+
238+
/** The inverse of `StoreNode.getAPredecessor`. */
239+
final StoreNode getASuccessor() { result.getAPredecessor() = this }
240+
}
241+
242+
private class StoreNodeInstr extends StoreNode, TStoreNodeInstr {
243+
Instruction instr;
244+
245+
StoreNodeInstr() { this = TStoreNodeInstr(instr) }
246+
247+
override predicate flowInto(Instruction addr) { this.getInstruction() = addr }
248+
249+
override Instruction getInstruction() { result = instr }
250+
251+
override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
252+
253+
override IRType getType() { result = this.getInstruction().getResultIRType() }
254+
255+
override Location getLocation() { result = this.getInstruction().getLocation() }
256+
257+
override string toString() {
258+
result = instructionNode(this.getInstruction()).toString() + " [store]"
259+
}
260+
261+
override Instruction getStoreInstruction() {
262+
Ssa::explicitWrite(_, result, this.getInstruction())
263+
}
264+
265+
override StoreNode getAPredecessor() {
266+
Ssa::addressFlow(result.getInstruction(), this.getInstruction())
267+
}
268+
}
269+
270+
private class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
271+
ArgumentOperand operand;
272+
273+
StoreNodeOperand() { this = TStoreNodeOperand(operand) }
274+
275+
override predicate flowInto(Instruction addr) { this.getOperand().getDef() = addr }
276+
277+
override Operand getOperand() { result = operand }
278+
279+
override Function getFunction() { result = operand.getDef().getEnclosingFunction() }
280+
281+
override IRType getType() { result = operand.getIRType() }
282+
283+
override Location getLocation() { result = operand.getLocation() }
284+
285+
override string toString() { result = operandNode(this.getOperand()).toString() + " [store]" }
286+
287+
override WriteSideEffectInstruction getStoreInstruction() {
288+
Ssa::explicitWrite(_, result, operand.getDef())
289+
}
290+
291+
override StoreNode getAPredecessor() { operand.getDef() = result.getInstruction() }
292+
}
293+
183294
/**
184295
* An expression, viewed as a node in a data flow graph.
185296
*/
@@ -313,15 +424,14 @@ deprecated class UninitializedNode extends Node {
313424
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
314425
* to the value before the update with the exception of `ClassInstanceExpr`,
315426
* which represents the value after the constructor has run.
316-
*
317-
* This class exists to match the interface used by Java. There are currently no non-abstract
318-
* classes that extend it. When we implement field flow, we can revisit this.
319427
*/
320-
abstract class PostUpdateNode extends InstructionNode {
428+
abstract class PostUpdateNode extends Node {
321429
/**
322430
* Gets the node before the state update.
323431
*/
324432
abstract Node getPreUpdateNode();
433+
434+
override string toString() { result = this.getPreUpdateNode() + " [post update]" }
325435
}
326436

327437
/**
@@ -614,6 +724,13 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
614724
or
615725
// Instruction -> Operand flow
616726
simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand())
727+
or
728+
// Flow into, through, and out of store nodes
729+
StoreNodeFlow::flowInto(nodeFrom, nodeTo)
730+
or
731+
StoreNodeFlow::flowThrough(nodeFrom, nodeTo)
732+
or
733+
StoreNodeFlow::flowOutOf(nodeFrom, nodeTo)
617734
}
618735

619736
pragma[noinline]
@@ -631,6 +748,28 @@ private predicate isSingleFieldClass(Type type, Operand op) {
631748
c.getSize() = size and
632749
getFieldSizeOfClass(c, type, size)
633750
)
751+
private module StoreNodeFlow {
752+
/** Holds if the store node `nodeTo` should receive flow from `nodeFrom`. */
753+
predicate flowInto(Node nodeFrom, StoreNode nodeTo) {
754+
nodeTo.flowInto(Ssa::getDestinationAddress(nodeFrom.asInstruction()))
755+
}
756+
757+
/** Holds if the store node `nodeTo` should receive flow from `nodeFom`. */
758+
predicate flowThrough(StoreNode nFrom, StoreNode nodeTo) {
759+
// Flow through a post update node that doesn't need a store step.
760+
not storeStep(nFrom, _, _) and
761+
nodeTo.getASuccessor() = nFrom
762+
}
763+
764+
/**
765+
* Holds if flow should leave the store node `nodeFrom` and enter the node `nodeTo`.
766+
* This happens because we have traversed an entire chain of field dereferences
767+
* after a store operation.
768+
*/
769+
predicate flowOutOf(StoreNode nFrom, Node nodeTo) {
770+
nFrom.isTerminal() and
771+
Ssa::ssaFlow(nFrom, nodeTo)
772+
}
634773
}
635774

636775
private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
@@ -788,25 +927,10 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
788927
*/
789928
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
790929

791-
/**
792-
* Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any.
793-
*/
794-
private Field getAField(Class c, int startBit, int endBit) {
795-
result.getDeclaringType() = c and
796-
startBit = 8 * result.getByteOffset() and
797-
endBit = 8 * result.getType().getSize() + startBit
798-
or
799-
exists(Field f, Class cInner |
800-
f = c.getAField() and
801-
cInner = f.getUnderlyingType() and
802-
result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset())
803-
)
804-
}
805-
806930
private newtype TContent =
807-
TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or
808-
TCollectionContent() or
809-
TArrayContent()
931+
TFieldContent(Field f) or
932+
TCollectionContent() or // Not used in C/C++
933+
TArrayContent() // Not used in C/C++.
810934

811935
/**
812936
* A description of the way data may be stored inside an object. Examples
@@ -824,18 +948,13 @@ class Content extends TContent {
824948

825949
/** A reference through an instance field. */
826950
class FieldContent extends Content, TFieldContent {
827-
Class c;
828-
int startBit;
829-
int endBit;
830-
831-
FieldContent() { this = TFieldContent(c, startBit, endBit) }
951+
Field f;
832952

833-
// Ensure that there's just 1 result for `toString`.
834-
override string toString() { result = min(Field f | f = getAField() | f.toString()) }
953+
FieldContent() { this = TFieldContent(f) }
835954

836-
predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit }
955+
override string toString() { result = f.toString() }
837956

838-
Field getAField() { result = getAField(c, startBit, endBit) }
957+
Field getField() { result = f }
839958
}
840959

841960
/** A reference through an array. */

0 commit comments

Comments
 (0)