Skip to content

Commit a971045

Browse files
committed
C++: Add class with heuristics to detect allocations.
1 parent 7ffbc73 commit a971045

File tree

2 files changed

+254
-44
lines changed

2 files changed

+254
-44
lines changed

cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll

Lines changed: 173 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -205,57 +205,104 @@ private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof
205205
sizeof = 1
206206
}
207207

208-
/**
209-
* An allocation expression that is a function call, such as call to `malloc`.
210-
*/
211-
private class CallAllocationExpr extends AllocationExpr, FunctionCall {
212-
AllocationFunction target;
213-
214-
CallAllocationExpr() {
215-
target = this.getTarget() and
216-
// realloc(ptr, 0) only frees the pointer
217-
not (
218-
exists(target.getReallocPtrArg()) and
219-
this.getArgument(target.getSizeArg()).getValue().toInt() = 0
220-
) and
221-
// these are modeled directly (and more accurately), avoid duplication
222-
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
223-
}
224-
225-
override Expr getSizeExpr() {
226-
exists(Expr sizeExpr | sizeExpr = this.getArgument(target.getSizeArg()) |
227-
if exists(target.getSizeMult())
228-
then result = sizeExpr
229-
else
230-
exists(Expr lengthExpr |
231-
deconstructSizeExpr(sizeExpr, lengthExpr, _) and
232-
result = lengthExpr
208+
private signature class CallAllocationExprTarget extends Function;
209+
210+
private module CallAllocationExprBase<CallAllocationExprTarget Target> {
211+
signature int getReallocPtrArgSig(Target target);
212+
213+
signature int getSizeArgSig(Target target);
214+
215+
signature int getSizeMultSig(Target target);
216+
217+
signature predicate requiresDeallocSig(Target target);
218+
219+
module With<
220+
getReallocPtrArgSig/1 getReallocPtrArg, getSizeArgSig/1 getSizeArg, getSizeMultSig/1 getSizeMult,
221+
requiresDeallocSig/1 requiresDealloc> {
222+
/**
223+
* An allocation expression that is a function call, such as call to `malloc`.
224+
*/
225+
class CallAllocationExprImpl instanceof FunctionCall {
226+
Target target;
227+
228+
CallAllocationExprImpl() {
229+
target = this.getTarget() and
230+
// realloc(ptr, 0) only frees the pointer
231+
not (
232+
exists(getReallocPtrArg(target)) and
233+
this.getArgument(getSizeArg(target)).getValue().toInt() = 0
234+
) and
235+
// these are modeled directly (and more accurately), avoid duplication
236+
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
237+
}
238+
239+
string toString() { result = super.toString() }
240+
241+
Expr getSizeExprImpl() {
242+
exists(Expr sizeExpr | sizeExpr = super.getArgument(getSizeArg(target)) |
243+
if exists(getSizeMult(target))
244+
then result = sizeExpr
245+
else
246+
exists(Expr lengthExpr |
247+
deconstructSizeExpr(sizeExpr, lengthExpr, _) and
248+
result = lengthExpr
249+
)
233250
)
234-
)
251+
}
252+
253+
int getSizeMultImpl() {
254+
// malloc with multiplier argument that is a constant
255+
result = super.getArgument(getSizeMult(target)).getValue().toInt()
256+
or
257+
// malloc with no multiplier argument
258+
not exists(getSizeMult(target)) and
259+
deconstructSizeExpr(super.getArgument(getSizeArg(target)), _, result)
260+
}
261+
262+
int getSizeBytesImpl() {
263+
result = this.getSizeExprImpl().getValue().toInt() * this.getSizeMultImpl()
264+
}
265+
266+
Expr getReallocPtrImpl() { result = super.getArgument(getReallocPtrArg(target)) }
267+
268+
Type getAllocatedElementTypeImpl() {
269+
result =
270+
super.getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and
271+
not result instanceof VoidType
272+
}
273+
274+
predicate requiresDeallocImpl() { requiresDealloc(target) }
275+
}
235276
}
277+
}
236278

237-
override int getSizeMult() {
238-
// malloc with multiplier argument that is a constant
239-
result = this.getArgument(target.getSizeMult()).getValue().toInt()
240-
or
241-
// malloc with no multiplier argument
242-
not exists(target.getSizeMult()) and
243-
deconstructSizeExpr(this.getArgument(target.getSizeArg()), _, result)
244-
}
279+
private module CallAllocationExpr {
280+
private int getReallocPtrArg(AllocationFunction f) { result = f.getReallocPtrArg() }
245281

246-
override int getSizeBytes() {
247-
result = this.getSizeExpr().getValue().toInt() * this.getSizeMult()
248-
}
282+
private int getSizeArg(AllocationFunction f) { result = f.getSizeArg() }
249283

250-
override Expr getReallocPtr() { result = this.getArgument(target.getReallocPtrArg()) }
284+
private int getSizeMult(AllocationFunction f) { result = f.getSizeMult() }
251285

252-
override Type getAllocatedElementType() {
253-
result =
254-
this.getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and
255-
not result instanceof VoidType
256-
}
286+
private predicate requiresDealloc(AllocationFunction f) { f.requiresDealloc() }
287+
288+
private class Base =
289+
CallAllocationExprBase<AllocationFunction>::With<getReallocPtrArg/1, getSizeArg/1, getSizeMult/1, requiresDealloc/1>::CallAllocationExprImpl;
290+
291+
class CallAllocationExpr extends AllocationExpr, Base {
292+
override Expr getSizeExpr() { result = super.getSizeExprImpl() }
293+
294+
override int getSizeMult() { result = super.getSizeMultImpl() }
257295

258-
override predicate requiresDealloc() { target.requiresDealloc() }
296+
override Type getAllocatedElementType() { result = super.getAllocatedElementTypeImpl() }
297+
298+
override predicate requiresDealloc() { super.requiresDeallocImpl() }
299+
300+
override int getSizeBytes() { result = super.getSizeBytesImpl() }
301+
302+
override Expr getReallocPtr() { result = super.getReallocPtrImpl() }
303+
304+
override string toString() { result = AllocationExpr.super.toString() }
305+
}
259306
}
260307

261308
/**
@@ -294,3 +341,85 @@ private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
294341

295342
override predicate requiresDealloc() { not exists(this.getPlacementPointer()) }
296343
}
344+
345+
private module HeuristicAllocation {
346+
private class HeuristicAllocationModeled extends HeuristicAllocationExpr instanceof AllocationExpr {
347+
override Expr getSizeExpr() { result = AllocationExpr.super.getSizeExpr() }
348+
349+
override int getSizeMult() { result = AllocationExpr.super.getSizeMult() }
350+
351+
override int getSizeBytes() { result = AllocationExpr.super.getSizeBytes() }
352+
353+
override Expr getReallocPtr() { result = AllocationExpr.super.getReallocPtr() }
354+
355+
override Type getAllocatedElementType() {
356+
result = AllocationExpr.super.getAllocatedElementType()
357+
}
358+
359+
override predicate requiresDealloc() { AllocationExpr.super.requiresDealloc() }
360+
}
361+
362+
private class HeuristicAllocationFunctionModeled extends HeuristicAllocationFunction instanceof AllocationFunction {
363+
override int getSizeArg() { result = AllocationFunction.super.getSizeArg() }
364+
365+
override int getSizeMult() { result = AllocationFunction.super.getSizeMult() }
366+
367+
override int getReallocPtrArg() { result = AllocationFunction.super.getReallocPtrArg() }
368+
369+
override predicate requiresDealloc() { AllocationFunction.super.requiresDealloc() }
370+
}
371+
372+
private int getAnUnsignedParameter(Function f) {
373+
f.getParameter(result).getUnspecifiedType().(IntegralType).isUnsigned()
374+
}
375+
376+
private int getAPointerParameter(Function f) {
377+
f.getParameter(result).getUnspecifiedType() instanceof PointerType
378+
}
379+
380+
private class HeuristicAllocationFunctionByName extends HeuristicAllocationFunction instanceof Function {
381+
int sizeArg;
382+
383+
HeuristicAllocationFunctionByName() {
384+
Function.super.getName().matches("%alloc%") and
385+
Function.super.getUnspecifiedType() instanceof PointerType and
386+
sizeArg = unique( | | getAnUnsignedParameter(this))
387+
}
388+
389+
override int getSizeArg() { result = sizeArg }
390+
391+
override int getReallocPtrArg() {
392+
Function.super.getName().matches("%realloc%") and
393+
result = unique( | | getAPointerParameter(this))
394+
}
395+
396+
override predicate requiresDealloc() { none() }
397+
}
398+
399+
private int getReallocPtrArg(HeuristicAllocationFunction f) { result = f.getReallocPtrArg() }
400+
401+
private int getSizeArg(HeuristicAllocationFunction f) { result = f.getSizeArg() }
402+
403+
private int getSizeMult(HeuristicAllocationFunction f) { result = f.getSizeMult() }
404+
405+
private predicate requiresDealloc(HeuristicAllocationFunction f) { f.requiresDealloc() }
406+
407+
private class Base =
408+
CallAllocationExprBase<HeuristicAllocationFunction>::With<getReallocPtrArg/1, getSizeArg/1, getSizeMult/1, requiresDealloc/1>::CallAllocationExprImpl;
409+
410+
private class CallAllocationExpr extends HeuristicAllocationExpr, Base {
411+
override Expr getSizeExpr() { result = super.getSizeExprImpl() }
412+
413+
override int getSizeMult() { result = super.getSizeMultImpl() }
414+
415+
override Type getAllocatedElementType() { result = super.getAllocatedElementTypeImpl() }
416+
417+
override predicate requiresDealloc() { super.requiresDeallocImpl() }
418+
419+
override int getSizeBytes() { result = super.getSizeBytesImpl() }
420+
421+
override Expr getReallocPtr() { result = super.getReallocPtrImpl() }
422+
423+
override string toString() { result = HeuristicAllocationExpr.super.toString() }
424+
}
425+
}

cpp/ql/lib/semmle/code/cpp/models/interfaces/Allocation.qll

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,84 @@ class OperatorNewAllocationFunction extends AllocationFunction {
113113
result = 1
114114
}
115115
}
116+
117+
/**
118+
* An expression that _might_ allocate memory.
119+
*
120+
* Unlike `AllocationExpr`, this class uses heuristics (such as a call target's
121+
* name and parameters) to include additional expressions.
122+
*/
123+
abstract class HeuristicAllocationExpr extends Expr {
124+
/**
125+
* Gets an expression for the allocation size, if any. The actual allocation
126+
* size is the value of this expression multiplied by the result of
127+
* `getSizeMult()`, in bytes.
128+
*/
129+
Expr getSizeExpr() { none() }
130+
131+
/**
132+
* Gets a constant multiplier for the allocation size given by `getSizeExpr`,
133+
* in bytes.
134+
*/
135+
int getSizeMult() { none() }
136+
137+
/**
138+
* Gets the size of this allocation in bytes, if it is a fixed size and that
139+
* size can be determined.
140+
*/
141+
int getSizeBytes() { none() }
142+
143+
/**
144+
* Gets the expression for the input pointer argument to be reallocated, if
145+
* this is a `realloc` function.
146+
*/
147+
Expr getReallocPtr() { none() }
148+
149+
/**
150+
* Gets the type of the elements that are allocated, if it can be determined.
151+
*/
152+
Type getAllocatedElementType() { none() }
153+
154+
/**
155+
* Whether or not this allocation requires a corresponding deallocation of
156+
* some sort (most do, but `alloca` for example does not). If it is unclear,
157+
* we default to no (for example a placement `new` allocation may or may not
158+
* require a corresponding `delete`).
159+
*/
160+
predicate requiresDealloc() { any() }
161+
}
162+
163+
/**
164+
* An function that _might_ allocate memory.
165+
*
166+
* Unlike `AllocationFunction`, this class uses heuristics (such as the function's
167+
* name and its parameters) to include additional functions.
168+
*/
169+
abstract class HeuristicAllocationFunction extends Function {
170+
/**
171+
* Gets the index of the argument for the allocation size, if any. The actual
172+
* allocation size is the value of this argument multiplied by the result of
173+
* `getSizeMult()`, in bytes.
174+
*/
175+
int getSizeArg() { none() }
176+
177+
/**
178+
* Gets the index of an argument that multiplies the allocation size given by
179+
* `getSizeArg`, if any.
180+
*/
181+
int getSizeMult() { none() }
182+
183+
/**
184+
* Gets the index of the input pointer argument to be reallocated, if this
185+
* is a `realloc` function.
186+
*/
187+
int getReallocPtrArg() { none() }
188+
189+
/**
190+
* Whether or not this allocation requires a corresponding deallocation of
191+
* some sort (most do, but `alloca` for example does not). If it is unclear,
192+
* we default to no (for example a placement `new` allocation may or may not
193+
* require a corresponding `delete`).
194+
*/
195+
predicate requiresDealloc() { any() }
196+
}

0 commit comments

Comments
 (0)