Skip to content

Commit fe57876

Browse files
authored
Merge pull request github#5643 from dbartol/smart-pointers/side-effect-refactor
C++: Refactor some side effect generation code
2 parents 56ba0f0 + b29f35f commit fe57876

15 files changed

+422
-330
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,15 +362,22 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
362362

363363
/**
364364
* Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
365-
* For instance, an update to a field of a struct containing only one field. For these cases we
366-
* attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case
367-
* (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`.
365+
* For instance, an update to a field of a struct containing only one field. Even if the store does
366+
* have a chi instruction, a subsequent use of the result of the store may be linked directly to the
367+
* result of the store as an inexact definition if the store totally overlaps the use. For these
368+
* cases we attach the PostUpdateNode to the store instruction. There's no obvious pre update node
369+
* for this case (as the entire memory is updated), so `getPreUpdateNode` is implemented as
370+
* `none()`.
368371
*/
369372
private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
370373
override StoreInstruction instr;
371374

372375
ExplicitSingleFieldStoreQualifierNode() {
373-
not exists(ChiInstruction chi | chi.getPartial() = instr) and
376+
(
377+
instr.getAUse().isDefinitionInexact()
378+
or
379+
not exists(ChiInstruction chi | chi.getPartial() = instr)
380+
) and
374381
// Without this condition any store would create a `PostUpdateNode`.
375382
instr.getDestinationAddress() instanceof FieldAddressInstruction
376383
}

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,7 @@ private import semmle.code.cpp.ir.ValueNumbering
66
private import semmle.code.cpp.ir.IR
77
private import semmle.code.cpp.ir.dataflow.DataFlow
88
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
9-
10-
/**
11-
* Gets a short ID for an IR dataflow node.
12-
* - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`).
13-
* - For `Operand`s, this is the label of the operand, prefixed with the result ID of the
14-
* instruction and a dot (e.g. `m128.left`).
15-
* - For `Variable`s, this is the qualified name of the variable.
16-
*/
17-
private string nodeId(DataFlow::Node node, int order1, int order2) {
18-
exists(Instruction instruction | instruction = node.asInstruction() |
19-
result = instruction.getResultId() and
20-
order1 = instruction.getBlock().getDisplayIndex() and
21-
order2 = instruction.getDisplayIndexInBlock()
22-
)
23-
or
24-
exists(Operand operand, Instruction instruction |
25-
operand = node.asOperand() and
26-
instruction = operand.getUse()
27-
|
28-
result = instruction.getResultId() + "." + operand.getDumpId() and
29-
order1 = instruction.getBlock().getDisplayIndex() and
30-
order2 = instruction.getDisplayIndexInBlock()
31-
)
32-
or
33-
result = "var(" + node.asVariable().getQualifiedName() + ")" and
34-
order1 = 1000000 and
35-
order2 = 0
36-
}
9+
private import PrintIRUtilities
3710

3811
/**
3912
* Gets the local dataflow from other nodes in the same function to this node.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Print the dataflow local store steps in IR dumps.
3+
*/
4+
5+
private import cpp
6+
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
7+
// that the cached IR gets the same checksum here as it does in queries that use
8+
// `ValueNumbering` without `DataFlow`.
9+
private import semmle.code.cpp.ir.ValueNumbering
10+
private import semmle.code.cpp.ir.IR
11+
private import semmle.code.cpp.ir.dataflow.DataFlow
12+
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
13+
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
14+
private import PrintIRUtilities
15+
16+
/**
17+
* Property provider for local IR dataflow store steps.
18+
*/
19+
class LocalFlowPropertyProvider extends IRPropertyProvider {
20+
override string getInstructionProperty(Instruction instruction, string key) {
21+
exists(DataFlow::Node objectNode, Content content |
22+
key = "content[" + content.toString() + "]" and
23+
instruction = objectNode.asInstruction() and
24+
result =
25+
strictconcat(string element, DataFlow::Node fieldNode |
26+
storeStep(fieldNode, content, objectNode) and
27+
element = nodeId(fieldNode, _, _)
28+
|
29+
element, ", "
30+
)
31+
)
32+
}
33+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Shared utilities used when printing dataflow annotations in IR dumps.
3+
*/
4+
5+
private import cpp
6+
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
7+
// that the cached IR gets the same checksum here as it does in queries that use
8+
// `ValueNumbering` without `DataFlow`.
9+
private import semmle.code.cpp.ir.ValueNumbering
10+
private import semmle.code.cpp.ir.IR
11+
private import semmle.code.cpp.ir.dataflow.DataFlow
12+
13+
/**
14+
* Gets a short ID for an IR dataflow node.
15+
* - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`).
16+
* - For `Operand`s, this is the label of the operand, prefixed with the result ID of the
17+
* instruction and a dot (e.g. `m128.left`).
18+
* - For `Variable`s, this is the qualified name of the variable.
19+
*/
20+
string nodeId(DataFlow::Node node, int order1, int order2) {
21+
exists(Instruction instruction | instruction = node.asInstruction() |
22+
result = instruction.getResultId() and
23+
order1 = instruction.getBlock().getDisplayIndex() and
24+
order2 = instruction.getDisplayIndexInBlock()
25+
)
26+
or
27+
exists(Operand operand, Instruction instruction |
28+
operand = node.asOperand() and
29+
instruction = operand.getUse()
30+
|
31+
result = instruction.getResultId() + "." + operand.getDumpId() and
32+
order1 = instruction.getBlock().getDisplayIndex() and
33+
order2 = instruction.getDisplayIndexInBlock()
34+
)
35+
or
36+
result = "var(" + node.asVariable().getQualifiedName() + ")" and
37+
order1 = 1000000 and
38+
order2 = 0
39+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* Predicates to compute the modeled side effects of calls during IR construction.
3+
*
4+
* These are used in `TranslatedElement.qll` to generate the `TTranslatedSideEffect` instances, and
5+
* also in `TranslatedCall.qll` to inject the actual side effect instructions.
6+
*/
7+
8+
private import cpp
9+
private import semmle.code.cpp.ir.implementation.Opcode
10+
private import semmle.code.cpp.models.interfaces.SideEffect
11+
12+
/**
13+
* Holds if the specified call has a side effect that does not come from a `SideEffectFunction`
14+
* model.
15+
*/
16+
private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buffer, boolean isWrite) {
17+
not call.getTarget() instanceof SideEffectFunction and
18+
(
19+
exists(MemberFunction mfunc |
20+
// A non-static member function, including a constructor or destructor, may write to `*this`,
21+
// and may also read from `*this` if it is not a constructor.
22+
i = -1 and
23+
mfunc = call.getTarget() and
24+
not mfunc.isStatic() and
25+
buffer = false and
26+
(
27+
isWrite = false and not mfunc instanceof Constructor
28+
or
29+
isWrite = true and not mfunc instanceof ConstMemberFunction
30+
)
31+
)
32+
or
33+
exists(Expr expr |
34+
// A pointer-like argument is assumed to read from the pointed-to buffer, and may write to the
35+
// buffer as well unless the pointer points to a `const` value.
36+
i >= 0 and
37+
buffer = true and
38+
expr = call.getArgument(i).getFullyConverted() and
39+
exists(Type t | t = expr.getUnspecifiedType() |
40+
t instanceof ArrayType or
41+
t instanceof PointerType or
42+
t instanceof ReferenceType
43+
) and
44+
(
45+
isWrite = true and
46+
not call.getTarget().getParameter(i).getType().isDeeplyConstBelow()
47+
or
48+
isWrite = false
49+
)
50+
)
51+
)
52+
}
53+
54+
/**
55+
* Returns a side effect opcode for parameter index `i` of the specified call.
56+
*
57+
* This predicate will return at most two results: one read side effect, and one write side effect.
58+
*/
59+
Opcode getASideEffectOpcode(Call call, ParameterIndex i) {
60+
exists(boolean buffer |
61+
(
62+
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(i, buffer)
63+
or
64+
not call.getTarget() instanceof SideEffectFunction and
65+
hasDefaultSideEffect(call, i, buffer, false)
66+
) and
67+
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i))
68+
then (
69+
buffer = true and
70+
result instanceof Opcode::SizedBufferReadSideEffect
71+
) else (
72+
buffer = false and result instanceof Opcode::IndirectReadSideEffect
73+
or
74+
buffer = true and result instanceof Opcode::BufferReadSideEffect
75+
)
76+
)
77+
or
78+
exists(boolean buffer, boolean mustWrite |
79+
(
80+
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(i, buffer, mustWrite)
81+
or
82+
not call.getTarget() instanceof SideEffectFunction and
83+
hasDefaultSideEffect(call, i, buffer, true) and
84+
mustWrite = false
85+
) and
86+
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i))
87+
then (
88+
buffer = true and
89+
mustWrite = false and
90+
result instanceof Opcode::SizedBufferMayWriteSideEffect
91+
or
92+
buffer = true and
93+
mustWrite = true and
94+
result instanceof Opcode::SizedBufferMustWriteSideEffect
95+
) else (
96+
buffer = false and
97+
mustWrite = false and
98+
result instanceof Opcode::IndirectMayWriteSideEffect
99+
or
100+
buffer = false and
101+
mustWrite = true and
102+
result instanceof Opcode::IndirectMustWriteSideEffect
103+
or
104+
buffer = true and mustWrite = false and result instanceof Opcode::BufferMayWriteSideEffect
105+
or
106+
buffer = true and mustWrite = true and result instanceof Opcode::BufferMustWriteSideEffect
107+
)
108+
)
109+
}

0 commit comments

Comments
 (0)