Skip to content

Commit 16d090d

Browse files
committed
Merge pull request #2184 from pguyot/w11/fix-codeql-false-positive
Improve precision of CodeQL allocation query These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents e2dda4e + c77640f commit 16d090d

1 file changed

Lines changed: 124 additions & 4 deletions

File tree

code-queries/allocations-exceeding-ensure-free.ql

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,121 @@ int leafAllocSize(FunctionCall call) {
182182
)
183183
}
184184

185+
// ============================================================
186+
// Branch resolution: when the caller passes a constant, determine
187+
// which branch of a conditional in the callee is actually taken,
188+
// so that allocations on the not-taken branch are excluded.
189+
// ============================================================
190+
191+
/**
192+
* Holds if `predFunc` is a simple threshold predicate function: it has
193+
* a single return statement of the form `param < THRESHOLD`, returning
194+
* true when the parameter is below the threshold.
195+
*/
196+
pragma[noinline]
197+
predicate isLTThresholdPredicate(Function predFunc, int paramIndex, int threshold) {
198+
exists(ReturnStmt ret, LTExpr lt |
199+
strictcount(ReturnStmt r | r.getEnclosingFunction() = predFunc) = 1 and
200+
ret.getEnclosingFunction() = predFunc and
201+
(
202+
lt = ret.getExpr()
203+
or
204+
lt = ret.getExpr().(Conversion).getExpr()
205+
) and
206+
lt.getLeftOperand().(VariableAccess).getTarget() = predFunc.getParameter(paramIndex) and
207+
threshold = constExprValue(lt.getRightOperand())
208+
)
209+
}
210+
211+
/**
212+
* Holds if function `wrapper` directly passes its parameter `wrapperParamIdx`
213+
* as argument `calleeParamIdx` in a call to `callee`.
214+
*/
215+
pragma[noinline]
216+
predicate paramPassthrough(Function wrapper, int wrapperParamIdx, Function callee, int calleeParamIdx) {
217+
exists(FunctionCall directCall |
218+
directCall.getEnclosingFunction() = wrapper and
219+
directCall.getTarget() = callee and
220+
directCall.getArgument(calleeParamIdx).(VariableAccess).getTarget() =
221+
wrapper.getParameter(wrapperParamIdx)
222+
)
223+
}
224+
225+
/**
226+
* Gets the constant value that `targetFunc.getParameter(targetParamIdx)`
227+
* receives when `outerCall` is invoked, tracing constants through up to
228+
* two levels of wrapper functions that pass the parameter through.
229+
*/
230+
pragma[noinline]
231+
int resolvedParamValue(FunctionCall outerCall, Function targetFunc, int targetParamIdx) {
232+
// Direct: targetFunc is the immediate callee
233+
targetFunc = outerCall.getTarget() and
234+
result = constExprValue(outerCall.getArgument(targetParamIdx))
235+
or
236+
// One level deep: outerCallee passes through to targetFunc
237+
exists(Function outerCallee, int outerParamIdx |
238+
outerCallee = outerCall.getTarget() and
239+
paramPassthrough(outerCallee, outerParamIdx, targetFunc, targetParamIdx) and
240+
result = constExprValue(outerCall.getArgument(outerParamIdx))
241+
)
242+
or
243+
// Two levels deep: outerCallee -> intermediate -> targetFunc
244+
exists(
245+
Function outerCallee, Function intermediate, int outerParamIdx, int intermediateParamIdx
246+
|
247+
outerCallee = outerCall.getTarget() and
248+
paramPassthrough(outerCallee, outerParamIdx, intermediate, intermediateParamIdx) and
249+
paramPassthrough(intermediate, intermediateParamIdx, targetFunc, targetParamIdx) and
250+
result = constExprValue(outerCall.getArgument(outerParamIdx))
251+
)
252+
}
253+
254+
/**
255+
* Holds if `leafCall` in function `leafFunc` is on a branch that is provably
256+
* not taken when the outer call `outerCall` passes constant arguments.
257+
*
258+
* Detects the pattern: if (predicateFunc(param)) { ... } else { ... }
259+
* where predicateFunc is a simple `param < THRESHOLD` function, and the
260+
* constant value of param resolves the condition.
261+
*/
262+
pragma[noinline]
263+
predicate leafOnResolvedNotTakenBranch(FunctionCall outerCall, FunctionCall leafCall) {
264+
exists(
265+
Function leafFunc, IfStmt ifStmt, FunctionCall condCall, Function condFunc,
266+
int condFuncParamIdx, int threshold, int leafFuncParamIdx, int paramValue,
267+
BasicBlock condBB, BasicBlock notTakenBB
268+
|
269+
leafCall.getEnclosingFunction() = leafFunc and
270+
ifStmt.getEnclosingFunction() = leafFunc and
271+
// The condition is a call to a simple threshold predicate
272+
(
273+
condCall = ifStmt.getCondition()
274+
or
275+
condCall = ifStmt.getCondition().(Conversion).getExpr()
276+
) and
277+
condFunc = condCall.getTarget() and
278+
isLTThresholdPredicate(condFunc, condFuncParamIdx, threshold) and
279+
// The predicate receives a parameter of leafFunc
280+
condCall.getArgument(condFuncParamIdx).(VariableAccess).getTarget() =
281+
leafFunc.getParameter(leafFuncParamIdx) and
282+
// Resolve the constant value from the outer call
283+
paramValue = resolvedParamValue(outerCall, leafFunc, leafFuncParamIdx) and
284+
condBB = condCall.getBasicBlock() and
285+
// Determine which branch is NOT taken
286+
(
287+
// param < threshold is TRUE → false branch not taken
288+
paramValue < threshold and
289+
notTakenBB = condBB.getAFalseSuccessor()
290+
or
291+
// param < threshold is FALSE → true branch not taken
292+
paramValue >= threshold and
293+
notTakenBB = condBB.getATrueSuccessor()
294+
) and
295+
// The leaf call is on the not-taken branch
296+
bbDominates(notTakenBB, leafCall.getBasicBlock())
297+
)
298+
}
299+
185300
/**
186301
* Computes the total constant allocation size for a call to a function that
187302
* transitively calls `memory_heap_alloc` without its own ensure_free.
@@ -190,7 +305,9 @@ int leafAllocSize(FunctionCall call) {
190305
* - All fully-constant direct `memory_heap_alloc` sizes
191306
* - All `memory_heap_alloc(heap, constant + param)` where the caller passes
192307
* a constant for that parameter
193-
* - All transitive wrapper function contributions (via reachableLeafAllocCall)
308+
* - All transitive wrapper function contributions (via reachableLeafAllocCall),
309+
* excluding leaf calls on branches that are provably not taken given the
310+
* caller's constant arguments
194311
*/
195312
pragma[noinline]
196313
int getConstAllocSize(FunctionCall call) {
@@ -208,7 +325,8 @@ int getConstAllocSize(FunctionCall call) {
208325
or
209326
exists(FunctionCall leafCall |
210327
reachableLeafAllocCall(callee, leafCall) and
211-
exists(leafAllocSize(leafCall))
328+
exists(leafAllocSize(leafCall)) and
329+
not leafOnResolvedNotTakenBranch(call, leafCall)
212330
)
213331
) and
214332
result =
@@ -221,10 +339,12 @@ int getConstAllocSize(FunctionCall call) {
221339
exists(constExprValue(call.getArgument(paramIndex)))
222340
| constPart + constExprValue(call.getArgument(paramIndex)))
223341
+
224-
// Sum transitive wrapper contributions via reachable leaf calls
342+
// Sum transitive wrapper contributions via reachable leaf calls,
343+
// excluding those on provably not-taken branches
225344
sum(FunctionCall leafCall, int leafSize |
226345
reachableLeafAllocCall(callee, leafCall) and
227-
leafSize = leafAllocSize(leafCall)
346+
leafSize = leafAllocSize(leafCall) and
347+
not leafOnResolvedNotTakenBranch(call, leafCall)
228348
| leafSize)
229349
)
230350
}

0 commit comments

Comments
 (0)