Files
codeql/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll
2023-10-30 17:08:01 +00:00

459 lines
17 KiB
Plaintext

/**
* Provides implementation classes modeling various methods of allocation
* (`malloc`, `new` etc). See `semmle.code.cpp.models.interfaces.Allocation`
* for usage information.
*/
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.Taint
/**
* An allocation function (such as `malloc`) that has an argument for the size
* in bytes.
*/
private class MallocAllocationFunction extends AllocationFunction {
int sizeArg;
MallocAllocationFunction() {
// --- C library allocation
this.hasGlobalOrStdOrBslName("malloc") and // malloc(size)
sizeArg = 0
or
this.hasGlobalName([
// --- Windows Memory Management for Windows Drivers
"MmAllocateContiguousMemory", // MmAllocateContiguousMemory(size, maxaddress)
"MmAllocateContiguousNodeMemory", // MmAllocateContiguousNodeMemory(size, minaddress, maxaddress, bound, flag, prefer)
"MmAllocateContiguousMemorySpecifyCache", // MmAllocateContiguousMemorySpecifyCache(size, minaddress, maxaddress, bound, type)
"MmAllocateContiguousMemorySpecifyCacheNode", // MmAllocateContiguousMemorySpecifyCacheNode(size, minaddress, maxaddress, bound, type, prefer)
"MmAllocateNonCachedMemory", // MmAllocateNonCachedMemory(size)
"MmAllocateMappingAddress", // MmAllocateMappingAddress(size, tag)
// --- Windows COM allocation
"CoTaskMemAlloc", // CoTaskMemAlloc(size)
// --- Solaris/BSD kernel memory allocator
"kmem_alloc", // kmem_alloc(size, flags)
"kmem_zalloc", // kmem_zalloc(size, flags)
// --- OpenSSL memory allocation
"CRYPTO_malloc", // CRYPTO_malloc(size_t num, const char *file, int line)
"CRYPTO_zalloc", // CRYPTO_zalloc(size_t num, const char *file, int line)
"CRYPTO_secure_malloc", // CRYPTO_secure_malloc(size_t num, const char *file, int line)
"CRYPTO_secure_zalloc" // CRYPTO_secure_zalloc(size_t num, const char *file, int line)
]) and
sizeArg = 0
or
this.hasGlobalName([
// --- Windows Memory Management for Windows Drivers
"ExAllocatePool", // ExAllocatePool(type, size)
"ExAllocatePool2", // ExAllocatePool2(flags, size, tag)
"ExAllocatePool3", // ExAllocatePool3(flags, size, tag, extparams, extparamscount)
"ExAllocatePoolWithTag", // ExAllocatePool(type, size, tag)
"ExAllocatePoolWithTagPriority", // ExAllocatePoolWithTagPriority(type, size, tag, priority)
"ExAllocatePoolWithQuota", // ExAllocatePoolWithQuota(type, size)
"ExAllocatePoolWithQuotaTag", // ExAllocatePoolWithQuotaTag(type, size, tag)
"ExAllocatePoolZero", // ExAllocatePoolZero(type, size, tag)
"IoAllocateMdl", // IoAllocateMdl(address, size, flag, flag, irp)
"IoAllocateErrorLogEntry", // IoAllocateErrorLogEntry(object, size)
// --- Windows Global / Local legacy allocation
"LocalAlloc", // LocalAlloc(flags, size)
"GlobalAlloc", // GlobalAlloc(flags, size)
// --- Windows System Services allocation
"VirtualAlloc" // VirtualAlloc(address, size, type, flag)
]) and
sizeArg = 1
or
this.hasGlobalName("HeapAlloc") and // HeapAlloc(heap, flags, size)
sizeArg = 2
or
this.hasGlobalName([
// --- Windows Memory Management for Windows Drivers
"MmAllocatePagesForMdl", // MmAllocatePagesForMdl(minaddress, maxaddress, skip, size)
"MmAllocatePagesForMdlEx", // MmAllocatePagesForMdlEx(minaddress, maxaddress, skip, size, type, flags)
"MmAllocateNodePagesForMdlEx" // MmAllocateNodePagesForMdlEx(minaddress, maxaddress, skip, size, type, prefer, flags)
]) and
sizeArg = 3
}
override int getSizeArg() { result = sizeArg }
}
/**
* An allocation function (such as `alloca`) that does not require a
* corresponding free (and has an argument for the size in bytes).
*/
private class AllocaAllocationFunction extends AllocationFunction {
int sizeArg;
AllocaAllocationFunction() {
this.hasGlobalName([
// --- stack allocation
"alloca", // // alloca(size)
"__builtin_alloca", // __builtin_alloca(size)
"_alloca", // _alloca(size)
"_malloca" // _malloca(size)
]) and
sizeArg = 0
}
override int getSizeArg() { result = sizeArg }
override predicate requiresDealloc() { none() }
}
/**
* An allocation function (such as `calloc`) that has an argument for the size
* and another argument for the size of those units (in bytes).
*/
private class CallocAllocationFunction extends AllocationFunction {
int sizeArg;
int multArg;
CallocAllocationFunction() {
// --- C library allocation
this.hasGlobalOrStdOrBslName("calloc") and // calloc(num, size)
sizeArg = 1 and
multArg = 0
}
override int getSizeArg() { result = sizeArg }
override int getSizeMult() { result = multArg }
}
/**
* An allocation function (such as `realloc`) that has an argument for the size
* in bytes, and an argument for an existing pointer that is to be reallocated.
*/
private class ReallocAllocationFunction extends AllocationFunction, TaintFunction {
int sizeArg;
int reallocArg;
ReallocAllocationFunction() {
// --- C library allocation
this.hasGlobalOrStdOrBslName("realloc") and // realloc(ptr, size)
sizeArg = 1 and
reallocArg = 0
or
this.hasGlobalName([
// --- Windows Global / Local legacy allocation
"LocalReAlloc", // LocalReAlloc(ptr, size, flags)
"GlobalReAlloc", // GlobalReAlloc(ptr, size, flags)
// --- Windows COM allocation
"CoTaskMemRealloc", // CoTaskMemRealloc(ptr, size)
// --- OpenSSL memory allocation
"CRYPTO_realloc" // CRYPTO_realloc(void *addr, size_t num, const char *file, int line)
]) and
sizeArg = 1 and
reallocArg = 0
or
this.hasGlobalName("HeapReAlloc") and // HeapReAlloc(heap, flags, ptr, size)
sizeArg = 3 and
reallocArg = 2
}
override int getSizeArg() { result = sizeArg }
override int getReallocPtrArg() { result = reallocArg }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(this.getReallocPtrArg()) and output.isReturnValueDeref()
}
}
/**
* A miscellaneous allocation function that has no explicit argument for
* the size of the allocation.
*/
private class SizelessAllocationFunction extends AllocationFunction {
SizelessAllocationFunction() {
this.hasGlobalName([
// --- Windows Memory Management for Windows Drivers
"ExAllocateFromLookasideListEx", // ExAllocateFromLookasideListEx(list)
"ExAllocateFromPagedLookasideList", // ExAllocateFromPagedLookasideList(list)
"ExAllocateFromNPagedLookasideList", // ExAllocateFromNPagedLookasideList(list)
"ExAllocateTimer", // ExAllocateTimer(callback, context, attributes)
"IoAllocateWorkItem", // IoAllocateWorkItem(object)
"MmMapLockedPagesWithReservedMapping", // MmMapLockedPagesWithReservedMapping(address, tag, list, type)
"MmMapLockedPages", // MmMapLockedPages(list, mode)
"MmMapLockedPagesSpecifyCache", // MmMapLockedPagesSpecifyCache(list, mode, type, address, flag, flag)
// --- NetBSD pool manager
"pool_get", // pool_get(pool, flags)
"pool_cache_get" // pool_cache_get(pool, flags)
])
}
}
/**
* Holds if `sizeExpr` is an expression consisting of a subexpression
* `lengthExpr` multiplied by a constant `sizeof` that is the result of a
* `sizeof()` expression. Alternatively if there isn't a suitable `sizeof()`
* expression, `lengthExpr = sizeExpr` and `sizeof = 1`. For example:
* ```
* malloc(a * 2 * sizeof(char32_t));
* ```
* In this case if the `sizeExpr` is the argument to `malloc`, the `lengthExpr`
* is `a * 2` and `sizeof` is `4`.
*/
private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof) {
exists(SizeofOperator sizeofOp |
sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
lengthExpr = sizeExpr.(MulExpr).getAnOperand() and
not lengthExpr instanceof SizeofOperator and
sizeof = sizeofOp.getValue().toInt()
)
or
not exists(SizeofOperator sizeofOp, Expr lengthOp |
sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
lengthOp = sizeExpr.(MulExpr).getAnOperand() and
not lengthOp instanceof SizeofOperator and
exists(sizeofOp.getValue().toInt())
) and
lengthExpr = sizeExpr and
sizeof = 1
}
/** A `Function` that is a call target of an allocation. */
private signature class CallAllocationExprTarget extends Function {
/**
* Gets the index of the input pointer argument to be reallocated, if
* this is a `realloc` function.
*/
int getReallocPtrArg();
/**
* Gets the index of the argument for the allocation size, if any. The actual
* allocation size is the value of this argument multiplied by the result of
* `getSizeMult()`, in bytes.
*/
int getSizeArg();
/**
* Gets the index of an argument that multiplies the allocation size given
* by `getSizeArg`, if any.
*/
int getSizeMult();
/**
* Holds if this allocation requires a
* corresponding deallocation of some sort (most do, but `alloca` for example
* does not). If it is unclear, we default to no (for example a placement `new`
* allocation may or may not require a corresponding `delete`).
*/
predicate requiresDealloc();
}
/**
* This module abstracts over the type of allocation call-targets and provides a
* class `CallAllocationExprImpl` which contains the implementation of the various
* predicates required by the `Allocation` class.
*
* This module is then instantiated for two types of allocation call-targets:
* - `AllocationFunction`: Functions that we've explicitly modeled as functions that
* perform allocations (i.e., `malloc`).
* - `HeuristicAllocationFunction`: Functions that we deduce as behaving like an allocation
* function using various heuristics.
*/
private module CallAllocationExprBase<CallAllocationExprTarget Target> {
/**
* An allocation expression that is a function call, such as call to `malloc`.
*/
class CallAllocationExprImpl instanceof FunctionCall {
Target target;
CallAllocationExprImpl() {
target = this.getTarget() and
// realloc(ptr, 0) only frees the pointer
not (
exists(target.getReallocPtrArg()) and
this.getArgument(target.getSizeArg()).getValue().toInt() = 0
) and
// these are modeled directly (and more accurately), avoid duplication
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
}
string toString() { result = super.toString() }
Expr getSizeExprImpl() {
exists(Expr sizeExpr | sizeExpr = super.getArgument(target.getSizeArg()) |
if exists(target.getSizeMult())
then result = sizeExpr
else
exists(Expr lengthExpr |
deconstructSizeExpr(sizeExpr, lengthExpr, _) and
result = lengthExpr
)
)
}
int getSizeMultImpl() {
// malloc with multiplier argument that is a constant
result = super.getArgument(target.getSizeMult()).getValue().toInt()
or
// malloc with no multiplier argument
not exists(target.getSizeMult()) and
deconstructSizeExpr(super.getArgument(target.getSizeArg()), _, result)
}
int getSizeBytesImpl() {
result = this.getSizeExprImpl().getValue().toInt() * this.getSizeMultImpl()
}
Expr getReallocPtrImpl() { result = super.getArgument(target.getReallocPtrArg()) }
Type getAllocatedElementTypeImpl() {
result =
super.getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and
not result instanceof VoidType
}
predicate requiresDeallocImpl() { target.requiresDealloc() }
}
}
private module CallAllocationExpr {
/**
* A class that provides the implementation of `AllocationExpr` for an allocation
* that calls an `AllocationFunction`.
*/
private class Base = CallAllocationExprBase<AllocationFunction>::CallAllocationExprImpl;
class CallAllocationExpr extends AllocationExpr, Base {
override Expr getSizeExpr() { result = super.getSizeExprImpl() }
override int getSizeMult() { result = super.getSizeMultImpl() }
override Type getAllocatedElementType() { result = super.getAllocatedElementTypeImpl() }
override predicate requiresDealloc() { super.requiresDeallocImpl() }
override int getSizeBytes() { result = super.getSizeBytesImpl() }
override Expr getReallocPtr() { result = super.getReallocPtrImpl() }
override string toString() { result = AllocationExpr.super.toString() }
}
}
/**
* An allocation expression that is a `new` expression.
*/
private class NewAllocationExpr extends AllocationExpr, NewExpr {
NewAllocationExpr() { this instanceof NewExpr }
override int getSizeBytes() { result = this.getAllocatedType().getSize() }
override Type getAllocatedElementType() { result = this.getAllocatedType() }
override predicate requiresDealloc() { not exists(this.getPlacementPointer()) }
}
/**
* An allocation expression that is a `new []` expression.
*/
private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
NewArrayAllocationExpr() { this instanceof NewArrayExpr }
override Expr getSizeExpr() {
// new array expr with variable size
result = this.getExtent()
}
override int getSizeMult() {
// new array expr with variable size
exists(this.getExtent()) and
result = this.getAllocatedElementType().getSize()
}
override Type getAllocatedElementType() { result = NewArrayExpr.super.getAllocatedElementType() }
override int getSizeBytes() { result = this.getAllocatedType().getSize() }
override predicate requiresDealloc() { not exists(this.getPlacementPointer()) }
}
private module HeuristicAllocation {
/** A class that maps an `AllocationExpr` to an `HeuristicAllocationExpr`. */
private class HeuristicAllocationModeled extends HeuristicAllocationExpr instanceof AllocationExpr
{
override Expr getSizeExpr() { result = AllocationExpr.super.getSizeExpr() }
override int getSizeMult() { result = AllocationExpr.super.getSizeMult() }
override int getSizeBytes() { result = AllocationExpr.super.getSizeBytes() }
override Expr getReallocPtr() { result = AllocationExpr.super.getReallocPtr() }
override Type getAllocatedElementType() {
result = AllocationExpr.super.getAllocatedElementType()
}
override predicate requiresDealloc() { AllocationExpr.super.requiresDealloc() }
}
/** A class that maps an `AllocationFunction` to an `HeuristicAllocationFunction`. */
private class HeuristicAllocationFunctionModeled extends HeuristicAllocationFunction instanceof AllocationFunction
{
override int getSizeArg() { result = AllocationFunction.super.getSizeArg() }
override int getSizeMult() { result = AllocationFunction.super.getSizeMult() }
override int getReallocPtrArg() { result = AllocationFunction.super.getReallocPtrArg() }
override predicate requiresDealloc() { AllocationFunction.super.requiresDealloc() }
}
private int getAnUnsignedParameter(Function f) {
f.getParameter(result).getUnspecifiedType().(IntegralType).isUnsigned()
}
private int getAPointerParameter(Function f) {
f.getParameter(result).getUnspecifiedType() instanceof PointerType
}
/**
* A class that uses heuristics to find additional allocation functions. The required are as follows:
* 1. The word `alloc` must appear in the function name
* 2. The function must return a pointer type
* 3. There must be a unique parameter of unsigned integral type.
*/
private class HeuristicAllocationFunctionByName extends HeuristicAllocationFunction instanceof Function
{
int sizeArg;
HeuristicAllocationFunctionByName() {
Function.super.getName().matches(["%alloc%", "%Alloc%"]) and
Function.super.getUnspecifiedType() instanceof PointerType and
sizeArg = unique( | | getAnUnsignedParameter(this))
}
override int getSizeArg() { result = sizeArg }
override int getReallocPtrArg() {
Function.super.getName().matches("%realloc%") and
result = unique( | | getAPointerParameter(this))
}
override predicate requiresDealloc() { none() }
}
/**
* A class that provides the implementation of `AllocationExpr` for an allocation
* that calls an `HeuristicAllocationFunction`.
*/
private class Base = CallAllocationExprBase<HeuristicAllocationFunction>::CallAllocationExprImpl;
private class CallAllocationExpr extends HeuristicAllocationExpr, Base {
override Expr getSizeExpr() { result = super.getSizeExprImpl() }
override int getSizeMult() { result = super.getSizeMultImpl() }
override Type getAllocatedElementType() { result = super.getAllocatedElementTypeImpl() }
override predicate requiresDealloc() { super.requiresDeallocImpl() }
override int getSizeBytes() { result = super.getSizeBytesImpl() }
override Expr getReallocPtr() { result = super.getReallocPtrImpl() }
override string toString() { result = HeuristicAllocationExpr.super.toString() }
}
}