Skip to content

Commit d9e02e8

Browse files
authored
Merge pull request #6825 from MathiasVP/use-shared-ssa-in-ir-dataflow
C++: Redesign IR dataflow using the shared SSA library
2 parents 1bacce4 + 8e496f7 commit d9e02e8

File tree

66 files changed

+7425
-1927
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+7425
-1927
lines changed

config/identical-files.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,8 @@
449449
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
450450
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
451451
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
452-
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll"
452+
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
453+
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
453454
],
454455
"CryptoAlgorithms Python/JS": [
455456
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@ private module VirtualDispatch {
6363
|
6464
// Call argument
6565
exists(DataFlowCall call, int i |
66-
other.(DataFlow::ParameterNode).isParameterOf(call.getStaticCallTarget(), i) and
67-
src.(ArgumentNode).argumentOf(call, i)
66+
other
67+
.(DataFlow::ParameterNode)
68+
.isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
69+
src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i)))
6870
) and
6971
allowOtherFromArg = true and
7072
allowFromArg = true
@@ -128,6 +130,7 @@ private module VirtualDispatch {
128130
*
129131
* Used to fix a join ordering issue in flowsFrom.
130132
*/
133+
pragma[noinline]
131134
private predicate returnNodeWithKindAndEnclosingCallable(
132135
ReturnNode node, ReturnKind kind, DataFlowCallable callable
133136
) {

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

Lines changed: 13 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,9 @@ class ReturnNode extends InstructionNode {
112112
Instruction primary;
113113

114114
ReturnNode() {
115-
exists(ReturnValueInstruction ret | instr = ret.getReturnValue() and primary = ret)
115+
exists(ReturnValueInstruction ret | instr = ret and primary = ret)
116116
or
117-
exists(ReturnIndirectionInstruction rii |
118-
instr = rii.getSideEffectOperand().getAnyDef() and primary = rii
119-
)
117+
exists(ReturnIndirectionInstruction rii | instr = rii and primary = rii)
120118
}
121119

122120
/** Gets the kind of this returned value. */
@@ -190,108 +188,16 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
190188
*/
191189
predicate jumpStep(Node n1, Node n2) { none() }
192190

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

@@ -300,122 +206,14 @@ private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
300206
* Thus, `node1` references an object with a field `f` whose value ends up in
301207
* `node2`.
302208
*/
303-
private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
304-
exists(LoadOperand operand |
305-
node2.asOperand() = operand and
306-
node1.asInstruction() = operand.getAnyDef() and
307-
exists(Class c |
308-
c = operand.getAnyDef().getResultType() and
309-
exists(int startBit, int endBit |
310-
operand.getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
311-
f.hasOffset(c, startBit, endBit)
312-
)
313-
or
314-
getLoadedField(operand.getUse(), f.getAField(), c) and
315-
f.hasOffset(c, _, _)
316-
)
209+
predicate readStep(ReadNode node1, FieldContent f, ReadNode node2) {
210+
exists(FieldAddressInstruction fai |
211+
node1.getInstruction() = fai.getObjectAddress() and
212+
node2.getInstruction() = fai and
213+
f.getField() = fai.getField()
317214
)
318215
}
319216

320-
/**
321-
* When a store step happens in a function that looks like an array write such as:
322-
* ```cpp
323-
* void f(int* pa) {
324-
* pa = source();
325-
* }
326-
* ```
327-
* it can be a write to an array, but it can also happen that `f` is called as `f(&a.x)`. If that is
328-
* the case, the `ArrayContent` that was written by the call to `f` should be popped off the access
329-
* path, and a `FieldContent` containing `x` should be pushed instead.
330-
* So this case pops `ArrayContent` off the access path, and the `fieldStoreStepAfterArraySuppression`
331-
* predicate in `storeStep` ensures that we push the right `FieldContent` onto the access path.
332-
*/
333-
predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
334-
exists(a) and
335-
exists(WriteSideEffectInstruction write, ChiInstruction chi |
336-
node1.asInstruction() = write and
337-
node2.asInstruction() = chi and
338-
chi.getPartial() = write and
339-
getWrittenField(write, _, _)
340-
)
341-
}
342-
343-
private class ArrayToPointerConvertInstruction extends ConvertInstruction {
344-
ArrayToPointerConvertInstruction() {
345-
this.getUnary().getResultType() instanceof ArrayType and
346-
this.getResultType() instanceof PointerType
347-
}
348-
}
349-
350-
private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) {
351-
copy.getUnary() = result and not result instanceof CopyValueInstruction
352-
or
353-
result = skipOneCopyValueInstructionRec(copy.getUnary())
354-
}
355-
356-
private Instruction skipCopyValueInstructions(Operand op) {
357-
not result instanceof CopyValueInstruction and result = op.getDef()
358-
or
359-
result = skipOneCopyValueInstructionRec(op.getDef())
360-
}
361-
362-
private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
363-
exists(a) and
364-
// Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
365-
exists(LoadOperand operand, Instruction address |
366-
operand.isDefinitionInexact() and
367-
node1.asInstruction() = operand.getAnyDef() and
368-
operand = node2.asOperand() and
369-
address = skipCopyValueInstructions(operand.getAddressOperand()) and
370-
(
371-
address instanceof LoadInstruction or
372-
address instanceof ArrayToPointerConvertInstruction or
373-
address instanceof PointerOffsetInstruction
374-
)
375-
)
376-
}
377-
378-
/**
379-
* In cases such as:
380-
* ```cpp
381-
* void f(int* pa) {
382-
* *pa = source();
383-
* }
384-
* ...
385-
* int x;
386-
* f(&x);
387-
* use(x);
388-
* ```
389-
* the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition
390-
* is a `WriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
391-
* from the access path.
392-
*/
393-
private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
394-
exists(a) and
395-
exists(WriteSideEffectInstruction write, ChiInstruction chi |
396-
not chi.isResultConflated() and
397-
chi.getPartial() = write and
398-
node1.asInstruction() = write and
399-
node2.asInstruction() = chi and
400-
// To distinquish this case from the `arrayReadStep` case we require that the entire variable was
401-
// overwritten by the `WriteSideEffectInstruction` (i.e., there is a load that reads the
402-
// entire variable).
403-
exists(LoadInstruction load | load.getSourceValue() = chi)
404-
)
405-
}
406-
407-
/**
408-
* Holds if data can flow from `node1` to `node2` via a read of `f`.
409-
* Thus, `node1` references an object with a field `f` whose value ends up in
410-
* `node2`.
411-
*/
412-
predicate readStep(Node node1, Content f, Node node2) {
413-
fieldReadStep(node1, f, node2) or
414-
arrayReadStep(node1, f, node2) or
415-
exactReadStep(node1, f, node2) or
416-
suppressArrayRead(node1, f, node2)
417-
}
418-
419217
/**
420218
* Holds if values stored inside content `c` are cleared at node `n`.
421219
*/
@@ -447,7 +245,7 @@ private predicate suppressUnusedNode(Node n) { any() }
447245
// Java QL library compatibility wrappers
448246
//////////////////////////////////////////////////////////////////////////////
449247
/** A node that performs a type cast. */
450-
class CastNode extends InstructionNode {
248+
class CastNode extends Node {
451249
CastNode() { none() } // stub implementation
452250
}
453251

0 commit comments

Comments
 (0)