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

Skip to content

Commit 4bcf1f4

Browse files
committed
C++: Add new query for unsafe use of this.
1 parent 25ba6ca commit 4bcf1f4

6 files changed

Lines changed: 397 additions & 0 deletions

File tree

cpp/config/suites/cpp/correctness

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
+ semmlecode-cpp-queries/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql: /Correctness/Dangerous Conversions
1010
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql: /Correctness/Dangerous Conversions
1111
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
12+
+ semmlecode-cpp-queries/Likely Bugs/OO/UnsafeUseOfThis.ql: /Correctness/Dangerous Conversions
1213
# Consistent Use
1314
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
1415
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class Base {
2+
private:
3+
// pure virtual member function used for initialization of derived classes.
4+
virtual void construct() = 0;
5+
public:
6+
Base() {
7+
// wrong: the virtual table of `Derived` has not been initialized yet. So this
8+
// call will resolve to `B::construct`, which cannot be called as it is a pure
9+
// virtual function.
10+
construct();
11+
}
12+
};
13+
14+
class Derived : public Base {
15+
int field;
16+
17+
void construct() override {
18+
field = 1;
19+
}
20+
};
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/**
2+
* @name Unsafe use of this in constructor
3+
* @description A call to a pure virtual function using a this
4+
* pointer of an object that is under construction
5+
* may lead to undefined behavior.
6+
* @kind path-problem
7+
* @id cpp/unsafe-use-of-this
8+
* @problem.severity error
9+
* @precision high
10+
* @tags correctness
11+
* language-features
12+
* security
13+
*/
14+
15+
import semmle.code.cpp.ir.IR
16+
import cpp
17+
18+
predicate irBbFunctionExit(IRBlock exit) {
19+
exit.getLastInstruction() instanceof ExitFunctionInstruction
20+
}
21+
22+
predicate irBbNodePred(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
23+
24+
predicate irBbIPostDominates(IRBlock postDominator, IRBlock node) =
25+
idominance(irBbFunctionExit/1, irBbNodePred/2)(_, postDominator, node)
26+
27+
predicate irBbStrictlyPostDominates(IRBlock postDominator, IRBlock node) {
28+
irBbIPostDominates+(postDominator, node)
29+
}
30+
31+
/**
32+
* Holds if `postDominator` is a post-dominator of `node` in the control-flow graph. This
33+
* is reflexive.
34+
*/
35+
predicate irBbPostDominates(IRBlock postDominator, IRBlock node) {
36+
irBbStrictlyPostDominates(postDominator, node) or postDominator = node
37+
}
38+
39+
bindingset[n, result]
40+
int unbind(int n) { result >= n and result <= n }
41+
42+
/** Holds if `p` is the `n`'th parameter of function `f`. */
43+
predicate parameterOf(Parameter p, Function f, int n) { p.getFunction() = f and p.getIndex() = n }
44+
45+
/**
46+
* Holds if `instr` is the `n`'th argument to a call to the function `f`, and
47+
* `init` is the corresponding initiazation instruction that receives the value of
48+
* `instr` in `f`.
49+
*/
50+
predicate flowIntoParameter(
51+
CallInstruction call, Instruction instr, Function f, int n, InitializeParameterInstruction init
52+
) {
53+
call.getPositionalArgument(n) = instr and
54+
f = call.getStaticCallTarget() and
55+
init.getEnclosingFunction() = f
56+
}
57+
58+
/**
59+
* Holds if `instr` is an argument to a call to the function `f`, and
60+
* `init` is the corresponding initiazation instruction that receives the value of
61+
* `instr` in `f`.
62+
*/
63+
pragma[noinline]
64+
predicate getPositionalArgumentInitParam(
65+
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
66+
) {
67+
exists(int n |
68+
parameterOf(_, f, n) and
69+
flowIntoParameter(call, instr, f, unbind(n), init)
70+
)
71+
}
72+
73+
/**
74+
* Holds if `instr` is the qualifier to a call to the function `f`, and
75+
* `init` is the corresponding initiazation instruction that receives the value of
76+
* `instr` in `f`.
77+
*/
78+
pragma[noinline]
79+
predicate getThisArgumentInitParam(
80+
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
81+
) {
82+
call.getStaticCallTarget() = f and
83+
init.getEnclosingFunction() = f and
84+
call.getThisArgument() = instr and
85+
init.getIRVariable() instanceof IRThisVariable
86+
}
87+
88+
/** Holds if `instr` is a `this` pointer of type `c` used by the call instruction `call`. */
89+
predicate isSink(Instruction instr, CallInstruction call, Class c) {
90+
exists(PureVirtualFunction func |
91+
call.getStaticCallTarget() = func and
92+
call.getThisArgument() = instr and
93+
instr.getResultType().stripType() = c and
94+
// Weed out implicit calls to destructors of a base class
95+
not func instanceof Destructor
96+
)
97+
}
98+
99+
/** Holds if `init` initializes the `this` pointer in class `c`. */
100+
predicate isSource(InitializeParameterInstruction init, string msg, Class c) {
101+
(
102+
exists(Constructor func |
103+
not func instanceof CopyConstructor and
104+
not func instanceof MoveConstructor and
105+
func = init.getEnclosingFunction() and
106+
msg = "construction"
107+
)
108+
or
109+
init.getEnclosingFunction() instanceof Destructor and msg = "destruction"
110+
) and
111+
init.getIRVariable() instanceof IRThisVariable and
112+
init.getEnclosingFunction().getDeclaringType() = c
113+
}
114+
115+
/**
116+
* Holds if `instr` flows to the sink instruction `sink` that is a `this`
117+
* pointer of type `sinkClass`
118+
*/
119+
predicate flowsToSink(Instruction instr, Instruction sink, Class sinkClass) {
120+
instr = sink and
121+
isSink(sink, _, sinkClass)
122+
or
123+
exists(Instruction mid |
124+
successor(instr, mid, sinkClass) and
125+
flowsToSink(mid, sink, sinkClass)
126+
)
127+
}
128+
129+
/**
130+
* Holds if `source` is an initialization of a `this` pointer of type `sourceClass`, and
131+
* `source` flows to instruction `instr`.
132+
*/
133+
predicate flowsFromSource(Instruction source, Instruction instr, Class sourceClass) {
134+
source = instr and
135+
isSource(source, _, sourceClass)
136+
or
137+
exists(Instruction mid |
138+
successorFwd(mid, instr, sourceClass) and
139+
flowsFromSource(source, mid, sourceClass)
140+
)
141+
}
142+
143+
/**
144+
* Holds if
145+
* - `instr` is an argument (or argument indirection) to a call, and
146+
* - `succ` is the corresponding initialization instruction in the call target, and
147+
* - `succ` eventually flows to an instruction that is used as a `this` pointer of type `sinkClass`.
148+
*/
149+
predicate flowThroughCallable(Instruction instr, Instruction succ, Class sinkClass) {
150+
flowsToSink(succ, _, sinkClass) and
151+
(
152+
// Flow from an argument to a parameter
153+
exists(CallInstruction call, InitializeParameterInstruction init | init = succ |
154+
getPositionalArgumentInitParam(call, instr, init, call.getStaticCallTarget())
155+
or
156+
getThisArgumentInitParam(call, instr, init, call.getStaticCallTarget())
157+
)
158+
or
159+
// Flow from argument indirection to parameter indirection
160+
exists(
161+
CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init
162+
|
163+
init = succ and
164+
read.getPrimaryInstruction() = call and
165+
init.getEnclosingFunction() = call.getStaticCallTarget()
166+
|
167+
exists(int n |
168+
read.getSideEffectOperand().getAnyDef() = instr and
169+
read.getIndex() = n and
170+
init.getParameter().getIndex() = unbind(n)
171+
)
172+
or
173+
call.getThisArgument() = instr and
174+
init.getIRVariable() instanceof IRThisVariable
175+
)
176+
)
177+
}
178+
179+
/**
180+
* Holds if `instr` flows to `succ` and `succ` eventually flows to an instruction that is used
181+
* as a `this` pointer of type `sinkClass`.
182+
*/
183+
predicate successor(Instruction instr, Instruction succ, Class sinkClass) {
184+
flowsToSink(succ, _, sinkClass) and
185+
(
186+
irBbPostDominates(succ.getBlock(), instr.getBlock()) and
187+
(
188+
(
189+
succ.(CopyInstruction).getSourceValue() = instr or
190+
succ.(CheckedConvertOrNullInstruction).getUnary() = instr or
191+
succ.(ChiInstruction).getTotal() = instr or
192+
succ.(ConvertInstruction).getUnary() = instr
193+
)
194+
or
195+
succ.(InheritanceConversionInstruction).getUnary() = instr
196+
)
197+
or
198+
flowThroughCallable(instr, succ, sinkClass)
199+
)
200+
}
201+
202+
/**
203+
* Holds if
204+
* - `instr` is an argument (or argument indirection) to a call, and
205+
* - `succ` is the corresponding initialization instruction in the call target, and
206+
* - there exists an initialization of a `this` pointer of type `sourceClass` that flows to `instr`.
207+
*/
208+
predicate flowThroughCallableFwd(Instruction instr, Instruction succ, Class sourceClass) {
209+
flowsFromSource(_, instr, sourceClass) and
210+
(
211+
// Flow from an argument to a parameter
212+
exists(CallInstruction call, InitializeParameterInstruction init | init = succ |
213+
getPositionalArgumentInitParam(call, instr, init, call.getStaticCallTarget())
214+
or
215+
getThisArgumentInitParam(call, instr, init, call.getStaticCallTarget())
216+
)
217+
or
218+
// Flow from argument indirection to parameter indirection
219+
exists(
220+
CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init
221+
|
222+
init = succ and
223+
init.getEnclosingFunction() = call.getStaticCallTarget() and
224+
read.getPrimaryInstruction() = call and
225+
read.getSideEffectOperand().getAnyDef() = instr
226+
|
227+
exists(int n |
228+
read.getIndex() = n and
229+
init.getParameter().getIndex() = unbind(n)
230+
)
231+
or
232+
call.getThisArgument() = instr and
233+
init.getIRVariable() instanceof IRThisVariable
234+
)
235+
)
236+
}
237+
238+
/**
239+
* Holds if there exists an initialization of a `this` pointer of type `sourceClass` that flows
240+
* to `instr`, and `instr` flows to `succ` and `succ`.
241+
*/
242+
predicate successorFwd(Instruction instr, Instruction succ, Class sourceClass) {
243+
flowsFromSource(_, instr, sourceClass) and
244+
(
245+
irBbPostDominates(succ.getBlock(), instr.getBlock()) and
246+
(
247+
(
248+
succ.(CopyInstruction).getSourceValue() = instr or
249+
succ.(CheckedConvertOrNullInstruction).getUnary() = instr or
250+
succ.(ChiInstruction).getTotal() = instr or
251+
succ.(ConvertInstruction).getUnary() = instr
252+
)
253+
or
254+
succ.(InheritanceConversionInstruction).getUnary() = instr
255+
)
256+
or
257+
flowThroughCallableFwd(instr, succ, sourceClass)
258+
)
259+
}
260+
261+
/**
262+
* Holds if `instr` is in the path from an initialization of a `this` pointer in a subclass, to a use
263+
* of the `this` pointer in a baseclass.
264+
*/
265+
predicate isInPath(Instruction instr) {
266+
exists(Class sourceClass, Class sinkClass |
267+
flowsFromSource(_, instr, sourceClass) and
268+
flowsToSink(instr, _, sinkClass) and
269+
sourceClass.getABaseClass+() = sinkClass
270+
)
271+
}
272+
273+
query predicate edges(Instruction a, Instruction b) { successor(a, b, _) }
274+
275+
query predicate nodes(Instruction n, string key, string val) {
276+
isInPath(n) and key = "semmle.label" and val = n.toString()
277+
}
278+
279+
from
280+
Instruction source, Instruction sink, CallInstruction call, string msg, Class sourceClass,
281+
Class sinkClass
282+
where
283+
isSource(source, msg, sourceClass) and
284+
flowsToSink(source, sink, sinkClass) and
285+
isSink(sink, call, sinkClass) and
286+
sourceClass.getABaseClass+() = sinkClass
287+
select call.getUnconvertedResultExpression(), source, sink,
288+
"Call to pure virtual function during " + msg
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
edges
2+
| test.cpp:7:2:7:2 | InitializeParameter: B | test.cpp:8:10:8:13 | Load: this |
3+
| test.cpp:8:10:8:13 | Load: this | test.cpp:30:16:30:16 | InitializeParameter: x |
4+
| test.cpp:11:10:11:10 | InitializeParameter: b | test.cpp:12:9:12:9 | Load: b |
5+
| test.cpp:12:9:12:9 | CopyValue: (reference dereference) | test.cpp:12:9:12:9 | ConvertToNonVirtualBase: (A)... |
6+
| test.cpp:12:9:12:9 | Load: b | test.cpp:12:9:12:9 | CopyValue: (reference dereference) |
7+
| test.cpp:15:2:15:3 | InitializeParameter: ~B | test.cpp:16:3:16:3 | Load: this |
8+
| test.cpp:16:3:16:3 | Load: this | file://:0:0:0:0 | ConvertToNonVirtualBase: (A *)... |
9+
| test.cpp:21:2:21:2 | InitializeParameter: C | test.cpp:21:6:21:6 | ConvertToNonVirtualBase: call to B |
10+
| test.cpp:21:2:21:2 | InitializeParameter: C | test.cpp:22:10:22:13 | Load: this |
11+
| test.cpp:21:6:21:6 | ConvertToNonVirtualBase: call to B | test.cpp:7:2:7:2 | InitializeParameter: B |
12+
| test.cpp:22:10:22:13 | ConvertToNonVirtualBase: (B *)... | test.cpp:30:16:30:16 | InitializeParameter: x |
13+
| test.cpp:22:10:22:13 | Load: this | test.cpp:22:10:22:13 | ConvertToNonVirtualBase: (B *)... |
14+
| test.cpp:27:5:27:5 | InitializeParameter: D | test.cpp:27:14:27:17 | Load: this |
15+
| test.cpp:27:13:27:17 | ConvertToNonVirtualBase: (B)... | test.cpp:27:13:27:17 | CopyValue: (reference to) |
16+
| test.cpp:27:13:27:17 | CopyValue: (reference to) | test.cpp:11:10:11:10 | InitializeParameter: b |
17+
| test.cpp:27:13:27:17 | CopyValue: * ... | test.cpp:27:13:27:17 | ConvertToNonVirtualBase: (B)... |
18+
| test.cpp:27:14:27:17 | Load: this | test.cpp:27:13:27:17 | CopyValue: * ... |
19+
| test.cpp:30:16:30:16 | InitializeParameter: x | test.cpp:31:2:31:2 | Load: x |
20+
| test.cpp:31:2:31:2 | Load: x | test.cpp:31:2:31:2 | ConvertToNonVirtualBase: (A *)... |
21+
nodes
22+
| file://:0:0:0:0 | ConvertToNonVirtualBase: (A *)... | semmle.label | ConvertToNonVirtualBase: (A *)... |
23+
| test.cpp:7:2:7:2 | InitializeParameter: B | semmle.label | InitializeParameter: B |
24+
| test.cpp:8:10:8:13 | Load: this | semmle.label | Load: this |
25+
| test.cpp:11:10:11:10 | InitializeParameter: b | semmle.label | InitializeParameter: b |
26+
| test.cpp:12:9:12:9 | ConvertToNonVirtualBase: (A)... | semmle.label | ConvertToNonVirtualBase: (A)... |
27+
| test.cpp:12:9:12:9 | CopyValue: (reference dereference) | semmle.label | CopyValue: (reference dereference) |
28+
| test.cpp:12:9:12:9 | Load: b | semmle.label | Load: b |
29+
| test.cpp:15:2:15:3 | InitializeParameter: ~B | semmle.label | InitializeParameter: ~B |
30+
| test.cpp:16:3:16:3 | Load: this | semmle.label | Load: this |
31+
| test.cpp:21:2:21:2 | InitializeParameter: C | semmle.label | InitializeParameter: C |
32+
| test.cpp:21:6:21:6 | ConvertToNonVirtualBase: call to B | semmle.label | ConvertToNonVirtualBase: call to B |
33+
| test.cpp:22:10:22:13 | ConvertToNonVirtualBase: (B *)... | semmle.label | ConvertToNonVirtualBase: (B *)... |
34+
| test.cpp:22:10:22:13 | Load: this | semmle.label | Load: this |
35+
| test.cpp:27:5:27:5 | InitializeParameter: D | semmle.label | InitializeParameter: D |
36+
| test.cpp:27:13:27:17 | ConvertToNonVirtualBase: (B)... | semmle.label | ConvertToNonVirtualBase: (B)... |
37+
| test.cpp:27:13:27:17 | CopyValue: (reference to) | semmle.label | CopyValue: (reference to) |
38+
| test.cpp:27:13:27:17 | CopyValue: * ... | semmle.label | CopyValue: * ... |
39+
| test.cpp:27:14:27:17 | Load: this | semmle.label | Load: this |
40+
| test.cpp:30:16:30:16 | InitializeParameter: x | semmle.label | InitializeParameter: x |
41+
| test.cpp:31:2:31:2 | ConvertToNonVirtualBase: (A *)... | semmle.label | ConvertToNonVirtualBase: (A *)... |
42+
| test.cpp:31:2:31:2 | Load: x | semmle.label | Load: x |
43+
#select
44+
| test.cpp:12:11:12:11 | call to f | test.cpp:27:5:27:5 | InitializeParameter: D | test.cpp:12:9:12:9 | ConvertToNonVirtualBase: (A)... | Call to pure virtual function during construction |
45+
| test.cpp:16:3:16:3 | call to f | test.cpp:15:2:15:3 | InitializeParameter: ~B | file://:0:0:0:0 | ConvertToNonVirtualBase: (A *)... | Call to pure virtual function during destruction |
46+
| test.cpp:31:5:31:5 | call to f | test.cpp:7:2:7:2 | InitializeParameter: B | test.cpp:31:2:31:2 | ConvertToNonVirtualBase: (A *)... | Call to pure virtual function during construction |
47+
| test.cpp:31:5:31:5 | call to f | test.cpp:21:2:21:2 | InitializeParameter: C | test.cpp:31:2:31:2 | ConvertToNonVirtualBase: (A *)... | Call to pure virtual function during construction |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/OO/UnsafeUseOfThis.ql

0 commit comments

Comments
 (0)