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

Skip to content

Commit 309b703

Browse files
C++: Models for side-effect-free functions
This commit adds a new model interface that describes the known side effects (or lack thereof) of a library function. Does it read memory, does it write memory, and do any of its parameters escape? Initially, we have models for just two Standard Library functions: `std::move` and `std::forward`, which neither read nor write memory, and do not escape their parameter. IR construction has been updated to insert the correct side effect instruction (or no side effect instruction) based on the model.
1 parent af44356 commit 309b703

4 files changed

Lines changed: 141 additions & 3 deletions

File tree

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import cpp
22
private import semmle.code.cpp.ir.implementation.Opcode
33
private import semmle.code.cpp.ir.internal.OperandTag
4+
private import semmle.code.cpp.models.interfaces.SideEffectFunction
45
private import InstructionTag
56
private import TranslatedElement
67
private import TranslatedExpr
@@ -37,9 +38,18 @@ abstract class TranslatedCall extends TranslatedExpr {
3738
isGLValue = false
3839
) or
3940
(
41+
hasSideEffect() and
4042
tag = CallSideEffectTag() and
41-
opcode instanceof Opcode::CallSideEffect and
42-
resultType instanceof UnknownType and
43+
(
44+
if hasWriteSideEffect() then (
45+
opcode instanceof Opcode::CallSideEffect and
46+
resultType instanceof UnknownType
47+
)
48+
else (
49+
opcode instanceof Opcode::CallReadSideEffect and
50+
resultType instanceof VoidType
51+
)
52+
) and
4353
isGLValue = false
4454
)
4555
}
@@ -68,9 +78,13 @@ abstract class TranslatedCall extends TranslatedExpr {
6878
(
6979
(
7080
tag = CallTag() and
71-
result = getInstruction(CallSideEffectTag())
81+
if hasSideEffect() then
82+
result = getInstruction(CallSideEffectTag())
83+
else
84+
result = getParent().getChildSuccessor(this)
7285
) or
7386
(
87+
hasSideEffect() and
7488
tag = CallSideEffectTag() and
7589
result = getParent().getChildSuccessor(this)
7690
)
@@ -183,6 +197,18 @@ abstract class TranslatedCall extends TranslatedExpr {
183197
* Holds if the call has any arguments, not counting the `this` argument.
184198
*/
185199
abstract predicate hasArguments();
200+
201+
predicate hasReadSideEffect() {
202+
any()
203+
}
204+
205+
predicate hasWriteSideEffect() {
206+
any()
207+
}
208+
209+
private predicate hasSideEffect() {
210+
hasReadSideEffect() or hasWriteSideEffect()
211+
}
186212
}
187213

188214
/**
@@ -280,6 +306,14 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
280306
override Function getInstructionFunction(InstructionTag tag) {
281307
tag = CallTargetTag() and result = funcCall.getTarget()
282308
}
309+
310+
override predicate hasReadSideEffect() {
311+
functionReadsMemory(funcCall.getTarget())
312+
}
313+
314+
override predicate hasWriteSideEffect() {
315+
functionWritesMemory(funcCall.getTarget())
316+
}
283317
}
284318

285319
/**

cpp/ql/src/semmle/code/cpp/models/Models.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
private import implementations.IdentityFunction
12
private import implementations.Inet
23
private import implementations.Memcpy
34
private import implementations.Printf
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import semmle.code.cpp.Function
2+
import semmle.code.cpp.models.interfaces.DataFlow
3+
import semmle.code.cpp.models.interfaces.SideEffectFunction
4+
5+
/**
6+
* The standard function templates `std::move` and `std::identity`
7+
*/
8+
class IdentityFunction extends DataFlowFunction, SideEffectFunction {
9+
IdentityFunction() {
10+
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
11+
this.getNamespace().getName() = "std" and
12+
(
13+
this.getName() = "move" or
14+
this.getName() = "forward"
15+
)
16+
}
17+
18+
override predicate readsMemory() {
19+
none()
20+
}
21+
22+
override predicate writesMemory() {
23+
none()
24+
}
25+
26+
override predicate parameterEscapes(int index) {
27+
// Note that returning the value of the parameter does not count as escaping.
28+
none()
29+
}
30+
31+
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
32+
// These functions simply return the argument value.
33+
input.isInParameter(0) and output.isOutReturnValue()
34+
}
35+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Provides an abstract class for accurate dataflow modeling of library
3+
* functions when source code is not available. To use this QL library,
4+
* create a QL class extending `SideEffectFunction` with a characteristic
5+
* predicate that selects the function or set of functions you are modeling.
6+
* Within that class, override the predicates provided by `SideEffectFunction`
7+
* to match the flow within that function.
8+
*/
9+
10+
import semmle.code.cpp.Function
11+
import semmle.code.cpp.models.Models
12+
13+
/**
14+
* Models the side effects of a library function.
15+
*/
16+
abstract class SideEffectFunction extends Function {
17+
/**
18+
* Holds if the function may read from memory that was defined before entry to the function. This
19+
* memory could be from global variables, or from other memory that was reachable from a pointer
20+
* that was passed into the function.
21+
*/
22+
abstract predicate readsMemory();
23+
24+
/**
25+
* Holds if the function may write to memory that remains allocated after the function returns.
26+
* This memory could be from global variables, or from other memory that was reachable from a
27+
* pointer that was passed into the function.
28+
*/
29+
abstract predicate writesMemory();
30+
31+
/**
32+
* Holds if any address passed to the parameter at the specified index is retained after the
33+
* function returns.
34+
*/
35+
abstract predicate parameterEscapes(int index);
36+
}
37+
38+
/**
39+
* Holds if the function `f` may read from memory that was defined before entry to the function.
40+
* This memory could be from global variables, or from other memory that was reachable from a
41+
* pointer that was passed into the function.
42+
*/
43+
predicate functionReadsMemory(Function f) {
44+
not exists(SideEffectFunction sideEffect |
45+
sideEffect = f and not sideEffect.readsMemory()
46+
)
47+
}
48+
49+
/**
50+
* Holds if the function `f` may write to memory that remains allocated after the function returns.
51+
* This memory could be from global variables, or from other memory that was reachable from a
52+
* pointer that was passed into the function.
53+
*/
54+
predicate functionWritesMemory(Function f) {
55+
not exists(SideEffectFunction sideEffect |
56+
sideEffect = f and not sideEffect.writesMemory()
57+
)
58+
}
59+
60+
/**
61+
* Holds if any address passed to the parameter at the specified index is retained after the
62+
* function returns.
63+
*/
64+
predicate functionParameterEscapes(Function f, int index) {
65+
not exists(SideEffectFunction sideEffect |
66+
exists(f.getParameter(index)) and sideEffect = f and not sideEffect.parameterEscapes(index)
67+
)
68+
}

0 commit comments

Comments
 (0)