mirror of
https://github.com/github/codeql.git
synced 2026-04-10 17:44:03 +02:00
168 lines
4.8 KiB
Plaintext
168 lines
4.8 KiB
Plaintext
/**
|
|
* Provides predicates for associating new/malloc calls with delete/free.
|
|
*/
|
|
|
|
import cpp
|
|
import semmle.code.cpp.controlflow.SSA
|
|
import semmle.code.cpp.ir.dataflow.DataFlow
|
|
|
|
/**
|
|
* Holds if `alloc` is a use of `malloc` or `new`. `kind` is
|
|
* a string describing the type of the allocation.
|
|
*/
|
|
predicate allocExpr(Expr alloc, string kind) {
|
|
(
|
|
exists(Function target |
|
|
alloc.(AllocationExpr).(FunctionCall).getTarget() = target and
|
|
(
|
|
target.getName() = "operator new" and
|
|
kind = "new" and
|
|
// exclude placement new and custom overloads as they
|
|
// may not conform to assumptions
|
|
not target.getNumberOfParameters() > 1
|
|
or
|
|
target.getName() = "operator new[]" and
|
|
kind = "new[]" and
|
|
// exclude placement new and custom overloads as they
|
|
// may not conform to assumptions
|
|
not target.getNumberOfParameters() > 1
|
|
or
|
|
not target instanceof OperatorNewAllocationFunction and
|
|
kind = "malloc"
|
|
)
|
|
)
|
|
or
|
|
alloc instanceof NewExpr and
|
|
kind = "new" and
|
|
// exclude placement new and custom overloads as they
|
|
// may not conform to assumptions
|
|
not alloc.(NewExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1
|
|
or
|
|
alloc instanceof NewArrayExpr and
|
|
kind = "new[]" and
|
|
// exclude placement new and custom overloads as they
|
|
// may not conform to assumptions
|
|
not alloc.(NewArrayExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1
|
|
) and
|
|
not alloc.isFromUninstantiatedTemplate(_)
|
|
}
|
|
|
|
/**
|
|
* Holds if `alloc` is a use of `malloc` or `new`, or a function
|
|
* wrapping one of those. `kind` is a string describing the type
|
|
* of the allocation.
|
|
*/
|
|
predicate allocExprOrIndirect(Expr alloc, string kind) {
|
|
// direct alloc
|
|
allocExpr(alloc, kind)
|
|
or
|
|
exists(ReturnStmt rtn |
|
|
// indirect alloc via function call
|
|
alloc.(FunctionCall).getTarget() = rtn.getEnclosingFunction() and
|
|
(
|
|
allocExprOrIndirect(rtn.getExpr(), kind)
|
|
or
|
|
exists(Expr e |
|
|
allocExprOrIndirect(e, kind) and
|
|
DataFlow::localExprFlow(e, rtn.getExpr())
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `v` is a non-local variable which is assigned with allocations of
|
|
* type `kind`.
|
|
*/
|
|
pragma[nomagic]
|
|
private predicate allocReachesVariable(Variable v, Expr alloc, string kind) {
|
|
exists(Expr mid |
|
|
not v instanceof StackVariable and
|
|
v.getAnAssignedValue() = mid and
|
|
allocReaches0(mid, alloc, kind)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `e` is an expression which may evaluate to the
|
|
* result of a previous memory allocation `alloc`. `kind` is a
|
|
* string describing the type of that allocation.
|
|
*/
|
|
private predicate allocReaches0(Expr e, Expr alloc, string kind) {
|
|
// alloc
|
|
allocExprOrIndirect(alloc, kind) and
|
|
e = alloc
|
|
or
|
|
exists(SsaDefinition def, StackVariable v |
|
|
// alloc via SSA
|
|
allocReaches0(def.getAnUltimateDefiningValue(v), alloc, kind) and
|
|
e = def.getAUse(v)
|
|
)
|
|
or
|
|
exists(Variable v |
|
|
// alloc via a global
|
|
allocReachesVariable(v, alloc, kind) and
|
|
strictcount(VariableAccess va | va.getTarget() = v) <= 50 and // avoid very expensive cases
|
|
e.(VariableAccess).getTarget() = v
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `e` is an expression which may evaluate to the
|
|
* result of previous memory allocations `alloc` only of type
|
|
* `kind`.
|
|
*/
|
|
predicate allocReaches(Expr e, Expr alloc, string kind) {
|
|
allocReaches0(e, alloc, kind) and
|
|
not exists(string k2 |
|
|
allocReaches0(e, _, k2) and
|
|
kind != k2
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `free` is a use of free or delete. `freed` is the
|
|
* expression that is freed / deleted and `kind` is a string
|
|
* describing the type of that free or delete.
|
|
*/
|
|
predicate freeExpr(Expr free, Expr freed, string kind) {
|
|
exists(Function target |
|
|
freed = free.(DeallocationExpr).getFreedExpr() and
|
|
free.(FunctionCall).getTarget() = target and
|
|
(
|
|
target.getName() = "operator delete" and
|
|
kind = "delete"
|
|
or
|
|
target.getName() = "operator delete[]" and
|
|
kind = "delete[]"
|
|
or
|
|
not target instanceof OperatorDeleteDeallocationFunction and
|
|
kind = "free"
|
|
)
|
|
)
|
|
or
|
|
free.(DeleteExpr).getExpr() = freed and
|
|
kind = "delete"
|
|
or
|
|
free.(DeleteArrayExpr).getExpr() = freed and
|
|
kind = "delete[]"
|
|
}
|
|
|
|
/**
|
|
* Holds if `free` is a use of free or delete, or a function
|
|
* wrapping one of those. `freed` is the expression that is
|
|
* freed / deleted and `kind` is a string describing the type
|
|
* of that free or delete.
|
|
*/
|
|
predicate freeExprOrIndirect(Expr free, Expr freed, string kind) {
|
|
// direct free
|
|
freeExpr(free, freed, kind)
|
|
or
|
|
// indirect free via function call
|
|
exists(Expr internalFreed, int arg |
|
|
freeExprOrIndirect(_, internalFreed, kind) and
|
|
free.(FunctionCall).getTarget().getParameter(arg) = internalFreed.(VariableAccess).getTarget() and
|
|
free.(FunctionCall).getArgument(arg) = freed
|
|
)
|
|
}
|