Merge pull request #2463 from geoffw0/overflowcalc

CPP: Allocation and Deallocation libraries
This commit is contained in:
Jonas Jensen
2019-12-19 21:27:42 +01:00
committed by GitHub
26 changed files with 710 additions and 235 deletions

View File

@@ -13,11 +13,17 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
| Buffer not sufficient for string (`cpp/overflow-calculated`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| No space for zero terminator (`cpp/no-space-for-terminator`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| Memory is never freed (`cpp/memory-never-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
## Changes to libraries
* Created the `semmle.code.cpp.models.interfaces.Allocation` library to model allocation such as `new` expressions and calls to `malloc`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
* Created the `semmle.code.cpp.models.interfaces.Deallocation` library to model deallocation such as `delete` expressions and calls to `free`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
* The new class `StackVariable` should be used in place of `LocalScopeVariable`
in most cases. The difference is that `StackVariable` does not include
variables declared with `static` or `thread_local`.

View File

@@ -1,14 +1,7 @@
import semmle.code.cpp.pointsto.PointsTo
private predicate freed(Expr e) {
exists(FunctionCall fc, Expr arg |
freeCall(fc, arg) and
arg = e
)
or
exists(DeleteExpr de | de.getExpr() = e)
or
exists(DeleteArrayExpr de | de.getExpr() = e)
e = any(DeallocationExpr de).getFreedExpr()
or
exists(ExprCall c |
// cautiously assume that any ExprCall could be a freeCall.
@@ -22,7 +15,4 @@ class FreedExpr extends PointsToExpr {
override predicate interesting() { freed(this) }
}
predicate allocMayBeFreed(Expr alloc) {
isAllocationExpr(alloc) and
anythingPointsTo(alloc)
}
predicate allocMayBeFreed(AllocationExpr alloc) { anythingPointsTo(alloc) }

View File

@@ -24,7 +24,7 @@ predicate mayCallFunction(Expr call, Function f) {
predicate allocCallOrIndirect(Expr e) {
// direct alloc call
isAllocationExpr(e) and
e.(AllocationExpr).requiresDealloc() and
// We are only interested in alloc calls that are
// actually freed somehow, as MemoryNeverFreed
// will catch those that aren't.
@@ -53,8 +53,7 @@ predicate allocCallOrIndirect(Expr e) {
* can cause memory leaks.
*/
predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) {
reallocCall.getTarget().hasGlobalOrStdName("realloc") and
reallocCall.getArgument(0) = v.getAnAccess() and
reallocCall.(AllocationExpr).getReallocPtr() = v.getAnAccess() and
(
exists(Variable newV, ControlFlowNode node |
// a realloc followed by a null check at 'node' (return the non-null
@@ -71,23 +70,19 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
or
// a realloc(ptr, 0), which always succeeds and frees
// (return the realloc itself)
reallocCall.getArgument(1).getValue() = "0" and
reallocCall.(AllocationExpr).getReallocPtr().getValue() = "0" and
verified = reallocCall
)
}
predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
// direct free call
freeCall(n, v.getAnAccess()) and
not n.(FunctionCall).getTarget().hasGlobalOrStdName("realloc")
n.(DeallocationExpr).getFreedExpr() = v.getAnAccess() and
not exists(n.(AllocationExpr).getReallocPtr())
or
// verified realloc call
verifiedRealloc(_, v, n)
or
n.(DeleteExpr).getExpr() = v.getAnAccess()
or
n.(DeleteArrayExpr).getExpr() = v.getAnAccess()
or
exists(FunctionCall midcall, Function mid, int arg |
// indirect free call
n.(Call).getArgument(arg) = v.getAnAccess() and

View File

@@ -11,6 +11,8 @@
import MemoryFreed
from Expr alloc
where isAllocationExpr(alloc) and not allocMayBeFreed(alloc)
from AllocationExpr alloc
where
alloc.requiresDealloc() and
not allocMayBeFreed(alloc)
select alloc, "This memory is never freed"

View File

@@ -11,26 +11,16 @@
*/
import cpp
class MallocCall extends FunctionCall {
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
Expr getAllocatedSize() {
if this.getArgument(0) instanceof VariableAccess
then
exists(StackVariable v, ControlFlowNode def |
definitionUsePair(v, def, this.getArgument(0)) and
exprDefinition(v, def, result)
)
else result = this.getArgument(0)
}
}
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.models.interfaces.Allocation
predicate spaceProblem(FunctionCall append, string msg) {
exists(MallocCall malloc, StrlenCall strlen, AddExpr add, FunctionCall insert, Variable buffer |
exists(
AllocationExpr malloc, StrlenCall strlen, AddExpr add, FunctionCall insert, Variable buffer
|
add.getAChild() = strlen and
exists(add.getAChild().getValue()) and
malloc.getAllocatedSize() = add and
DataFlow::localExprFlow(add, malloc.getSizeExpr()) and
buffer.getAnAccess() = strlen.getStringExpr() and
(
insert.getTarget().hasGlobalOrStdName("strcpy") or

View File

@@ -17,16 +17,11 @@
import cpp
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Allocation
class MallocCall extends FunctionCall {
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
Expr getAllocatedSize() { result = this.getArgument(0) }
}
predicate terminationProblem(MallocCall malloc, string msg) {
predicate terminationProblem(AllocationExpr malloc, string msg) {
// malloc(strlen(...))
exists(StrlenCall strlen | DataFlow::localExprFlow(strlen, malloc.getAllocatedSize())) and
exists(StrlenCall strlen | DataFlow::localExprFlow(strlen, malloc.getSizeExpr())) and
// flows into a null-terminated string function
exists(ArrayFunction af, FunctionCall fc, int arg |
DataFlow::localExprFlow(malloc, fc.getArgument(arg)) and

View File

@@ -1,207 +1,64 @@
import cpp
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.Deallocation
/**
* A library routine that allocates memory.
*
* DEPRECATED: Use the `AllocationFunction` class instead of this predicate.
*/
predicate allocationFunction(Function f) {
exists(string name |
f.hasGlobalOrStdName(name) and
(
name = "malloc" or
name = "calloc" or
name = "realloc" or
name = "strdup" or
name = "wcsdup"
)
or
f.hasGlobalName(name) and
(
name = "_strdup" or
name = "_wcsdup" or
name = "_mbsdup" or
name = "ExAllocatePool" or
name = "ExAllocatePoolWithTag" or
name = "ExAllocatePoolWithTagPriority" or
name = "ExAllocatePoolWithQuota" or
name = "ExAllocatePoolWithQuotaTag" or
name = "ExAllocateFromLookasideListEx" or
name = "ExAllocateFromPagedLookasideList" or
name = "ExAllocateFromNPagedLookasideList" or
name = "ExAllocateTimer" or
name = "IoAllocateMdl" or
name = "IoAllocateWorkItem" or
name = "IoAllocateErrorLogEntry" or
name = "MmAllocateContiguousMemory" or
name = "MmAllocateContiguousNodeMemory" or
name = "MmAllocateContiguousMemorySpecifyCache" or
name = "MmAllocateContiguousMemorySpecifyCacheNode" or
name = "MmAllocateNonCachedMemory" or
name = "MmAllocateMappingAddress" or
name = "MmAllocatePagesForMdl" or
name = "MmAllocatePagesForMdlEx" or
name = "MmAllocateNodePagesForMdlEx" or
name = "MmMapLockedPagesWithReservedMapping" or
name = "MmMapLockedPages" or
name = "MmMapLockedPagesSpecifyCache" or
name = "LocalAlloc" or
name = "LocalReAlloc" or
name = "GlobalAlloc" or
name = "GlobalReAlloc" or
name = "HeapAlloc" or
name = "HeapReAlloc" or
name = "VirtualAlloc" or
name = "CoTaskMemAlloc" or
name = "CoTaskMemRealloc" or
name = "kmem_alloc" or
name = "kmem_zalloc" or
name = "pool_get" or
name = "pool_cache_get"
)
)
}
deprecated predicate allocationFunction(Function f) { f instanceof AllocationFunction }
/**
* A call to a library routine that allocates memory.
*
* DEPRECATED: Use `AllocationExpr` instead (this also includes `new` expressions).
*/
predicate allocationCall(FunctionCall fc) {
allocationFunction(fc.getTarget()) and
(
// realloc(ptr, 0) only frees the pointer
fc.getTarget().hasGlobalOrStdName("realloc") implies not fc.getArgument(1).getValue() = "0"
)
}
deprecated predicate allocationCall(FunctionCall fc) { fc instanceof AllocationExpr }
/**
* A library routine that frees memory.
*/
predicate freeFunction(Function f, int argNum) {
exists(string name |
f.hasGlobalName(name) and
(
name = "free" and argNum = 0
or
name = "realloc" and argNum = 0
or
name = "kmem_free" and argNum = 0
or
name = "pool_put" and argNum = 1
or
name = "pool_cache_put" and argNum = 1
)
or
f.hasGlobalOrStdName(name) and
(
name = "ExFreePoolWithTag" and argNum = 0
or
name = "ExFreeToLookasideListEx" and argNum = 1
or
name = "ExFreeToPagedLookasideList" and argNum = 1
or
name = "ExFreeToNPagedLookasideList" and argNum = 1
or
name = "ExDeleteTimer" and argNum = 0
or
name = "IoFreeMdl" and argNum = 0
or
name = "IoFreeWorkItem" and argNum = 0
or
name = "IoFreeErrorLogEntry" and argNum = 0
or
name = "MmFreeContiguousMemory" and argNum = 0
or
name = "MmFreeContiguousMemorySpecifyCache" and argNum = 0
or
name = "MmFreeNonCachedMemory" and argNum = 0
or
name = "MmFreeMappingAddress" and argNum = 0
or
name = "MmFreePagesFromMdl" and argNum = 0
or
name = "MmUnmapReservedMapping" and argNum = 0
or
name = "MmUnmapLockedPages" and argNum = 0
or
name = "LocalFree" and argNum = 0
or
name = "GlobalFree" and argNum = 0
or
name = "HeapFree" and argNum = 2
or
name = "VirtualFree" and argNum = 0
or
name = "CoTaskMemFree" and argNum = 0
or
name = "SysFreeString" and argNum = 0
or
name = "LocalReAlloc" and argNum = 0
or
name = "GlobalReAlloc" and argNum = 0
or
name = "HeapReAlloc" and argNum = 2
or
name = "CoTaskMemRealloc" and argNum = 0
)
)
}
predicate freeFunction(Function f, int argNum) { argNum = f.(DeallocationFunction).getFreedArg() }
/**
* A call to a library routine that frees memory.
*/
predicate freeCall(FunctionCall fc, Expr arg) {
exists(int argNum |
freeFunction(fc.getTarget(), argNum) and
arg = fc.getArgument(argNum)
)
}
predicate freeCall(FunctionCall fc, Expr arg) { arg = fc.(DeallocationExpr).getFreedExpr() }
/**
* Is e some kind of allocation or deallocation (`new`, `alloc`, `realloc`, `delete`, `free` etc)?
*/
predicate isMemoryManagementExpr(Expr e) { isAllocationExpr(e) or isDeallocationExpr(e) }
predicate isMemoryManagementExpr(Expr e) { isAllocationExpr(e) or e instanceof DeallocationExpr }
/**
* Is e an allocation from stdlib.h (`malloc`, `realloc` etc)?
*
* DEPRECATED: Use `AllocationExpr` instead (this also includes `new` expressions).
*/
predicate isStdLibAllocationExpr(Expr e) { allocationCall(e) }
deprecated predicate isStdLibAllocationExpr(Expr e) { allocationCall(e) }
/**
* Is e some kind of allocation (`new`, `alloc`, `realloc` etc)?
*/
predicate isAllocationExpr(Expr e) {
allocationCall(e)
e.(FunctionCall) instanceof AllocationExpr
or
e = any(NewOrNewArrayExpr new | not exists(new.getPlacementPointer()))
}
/**
* Is e some kind of allocation (`new`, `alloc`, `realloc` etc) with a fixed size?
*
* DEPRECATED: Use `AllocationExpr.getSizeBytes()` instead.
*/
predicate isFixedSizeAllocationExpr(Expr allocExpr, int size) {
exists(FunctionCall fc, string name | fc = allocExpr and name = fc.getTarget().getName() |
name = "malloc" and
size = fc.getArgument(0).getValue().toInt()
or
name = "alloca" and
size = fc.getArgument(0).getValue().toInt()
or
name = "calloc" and
size = fc.getArgument(0).getValue().toInt() * fc.getArgument(1).getValue().toInt()
or
name = "realloc" and
size = fc.getArgument(1).getValue().toInt() and
size > 0 // realloc(ptr, 0) only frees the pointer
)
or
size = allocExpr.(NewExpr).getAllocatedType().getSize()
or
size = allocExpr.(NewArrayExpr).getAllocatedType().getSize()
deprecated predicate isFixedSizeAllocationExpr(Expr allocExpr, int size) {
size = allocExpr.(AllocationExpr).getSizeBytes()
}
/**
* Is e some kind of deallocation (`delete`, `free`, `realloc` etc)?
*
* DEPRECATED: Use `DeallocationExpr` instead.
*/
predicate isDeallocationExpr(Expr e) {
freeCall(e, _) or
e instanceof DeleteExpr or
e instanceof DeleteArrayExpr
}
deprecated predicate isDeallocationExpr(Expr e) { e instanceof DeallocationExpr }

View File

@@ -85,7 +85,7 @@ int getBufferSize(Expr bufferExpr, Element why) {
)
or
// buffer is a fixed size dynamic allocation
isFixedSizeAllocationExpr(bufferExpr, result) and
result = bufferExpr.(AllocationExpr).getSizeBytes() and
why = bufferExpr
or
exists(DataFlow::ExprNode bufferExprNode |

View File

@@ -1,3 +1,5 @@
private import implementations.Allocation
private import implementations.Deallocation
private import implementations.Fread
private import implementations.IdentityFunction
private import implementations.Inet

View File

@@ -0,0 +1,300 @@
import semmle.code.cpp.models.interfaces.Allocation
/**
* An allocation function (such as `malloc`) that has an argument for the size
* in bytes.
*/
class MallocAllocationFunction extends AllocationFunction {
int sizeArg;
MallocAllocationFunction() {
exists(string name |
hasGlobalOrStdName(name) and
// malloc(size)
(name = "malloc" and sizeArg = 0)
or
hasGlobalName(name) and
(
// ExAllocatePool(type, size)
name = "ExAllocatePool" and sizeArg = 1
or
// ExAllocatePool(type, size, tag)
name = "ExAllocatePoolWithTag" and sizeArg = 1
or
// ExAllocatePoolWithTagPriority(type, size, tag, priority)
name = "ExAllocatePoolWithTagPriority" and sizeArg = 1
or
// ExAllocatePoolWithQuota(type, size)
name = "ExAllocatePoolWithQuota" and sizeArg = 1
or
// ExAllocatePoolWithQuotaTag(type, size, tag)
name = "ExAllocatePoolWithQuotaTag" and sizeArg = 1
or
// IoAllocateMdl(address, size, flag, flag, irp)
name = "IoAllocateMdl" and sizeArg = 1
or
// IoAllocateErrorLogEntry(object, size)
name = "IoAllocateErrorLogEntry" and sizeArg = 1
or
// MmAllocateContiguousMemory(size, maxaddress)
name = "MmAllocateContiguousMemory" and sizeArg = 0
or
// MmAllocateContiguousNodeMemory(size, minaddress, maxaddress, bound, flag, prefer)
name = "MmAllocateContiguousNodeMemory" and sizeArg = 0
or
// MmAllocateContiguousMemorySpecifyCache(size, minaddress, maxaddress, bound, type)
name = "MmAllocateContiguousMemorySpecifyCache" and sizeArg = 0
or
// MmAllocateContiguousMemorySpecifyCacheNode(size, minaddress, maxaddress, bound, type, prefer)
name = "MmAllocateContiguousMemorySpecifyCacheNode" and sizeArg = 0
or
// MmAllocateNonCachedMemory(size)
name = "MmAllocateNonCachedMemory" and sizeArg = 0
or
// MmAllocateMappingAddress(size, tag)
name = "MmAllocateMappingAddress" and sizeArg = 0
or
// MmAllocatePagesForMdl(minaddress, maxaddress, skip, size)
name = "MmAllocatePagesForMdl" and sizeArg = 3
or
// MmAllocatePagesForMdlEx(minaddress, maxaddress, skip, size, type, flags)
name = "MmAllocatePagesForMdlEx" and sizeArg = 3
or
// MmAllocateNodePagesForMdlEx(minaddress, maxaddress, skip, size, type, prefer, flags)
name = "MmAllocateNodePagesForMdlEx" and sizeArg = 3
or
// LocalAlloc(flags, size)
name = "LocalAlloc" and sizeArg = 1
or
// GlobalAlloc(flags, size)
name = "GlobalAlloc" and sizeArg = 1
or
// HeapAlloc(heap, flags, size)
name = "HeapAlloc" and sizeArg = 2
or
// VirtualAlloc(address, size, type, flag)
name = "VirtualAlloc" and sizeArg = 1
or
// CoTaskMemAlloc(size)
name = "CoTaskMemAlloc" and sizeArg = 0
or
// kmem_alloc(size, flags)
name = "kmem_alloc" and sizeArg = 0
or
// kmem_zalloc(size, flags)
name = "kmem_zalloc" and sizeArg = 0
)
)
}
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).
*/
class AllocaAllocationFunction extends AllocationFunction {
int sizeArg;
AllocaAllocationFunction() {
exists(string name |
hasGlobalName(name) and
(
// alloca(size)
name = "alloca" and sizeArg = 0
or
// __builtin_alloca(size)
name = "__builtin_alloca" 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).
*/
class CallocAllocationFunction extends AllocationFunction {
int sizeArg;
int multArg;
CallocAllocationFunction() {
exists(string name |
hasGlobalOrStdName(name) and
// calloc(num, size)
(name = "calloc" and 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.
*/
class ReallocAllocationFunction extends AllocationFunction {
int sizeArg;
int reallocArg;
ReallocAllocationFunction() {
exists(string name |
hasGlobalOrStdName(name) and
// realloc(ptr, size)
(name = "realloc" and sizeArg = 1 and reallocArg = 0)
or
hasGlobalName(name) and
(
// LocalReAlloc(ptr, size, flags)
name = "LocalReAlloc" and sizeArg = 1 and reallocArg = 0
or
// GlobalReAlloc(ptr, size, flags)
name = "GlobalReAlloc" and sizeArg = 1 and reallocArg = 0
or
// HeapReAlloc(heap, flags, ptr, size)
name = "HeapReAlloc" and sizeArg = 3 and reallocArg = 2
or
// CoTaskMemRealloc(ptr, size)
name = "CoTaskMemRealloc" and sizeArg = 1 and reallocArg = 0
)
)
}
override int getSizeArg() { result = sizeArg }
override int getReallocPtrArg() { result = reallocArg }
}
/**
* An allocation function (such as `strdup`) that has no explicit argument for
* the size of the allocation.
*/
class StrdupAllocationFunction extends AllocationFunction {
StrdupAllocationFunction() {
exists(string name |
hasGlobalOrStdName(name) and
(
// strdup(str)
name = "strdup"
or
// wcsdup(str)
name = "wcsdup"
)
or
hasGlobalName(name) and
(
// _strdup(str)
name = "_strdup"
or
// _wcsdup(str)
name = "_wcsdup"
or
// _mbsdup(str)
name = "_mbsdup"
or
// ExAllocateFromLookasideListEx(list)
name = "ExAllocateFromLookasideListEx"
or
// ExAllocateFromPagedLookasideList(list)
name = "ExAllocateFromPagedLookasideList"
or
// ExAllocateFromNPagedLookasideList(list)
name = "ExAllocateFromNPagedLookasideList"
or
// ExAllocateTimer(callback, context, attributes)
name = "ExAllocateTimer"
or
// IoAllocateWorkItem(object)
name = "IoAllocateWorkItem"
or
// MmMapLockedPagesWithReservedMapping(address, tag, list, type)
name = "MmMapLockedPagesWithReservedMapping"
or
// MmMapLockedPages(list, mode)
name = "MmMapLockedPages"
or
// MmMapLockedPagesSpecifyCache(list, mode, type, address, flag, flag)
name = "MmMapLockedPagesSpecifyCache"
or
// pool_get(pool, flags)
name = "pool_get"
or
// pool_cache_get(pool, flags)
name = "pool_cache_get"
)
)
}
}
/**
* An allocation expression that is a function call, such as call to `malloc`.
*/
class CallAllocationExpr extends AllocationExpr, FunctionCall {
AllocationFunction target;
CallAllocationExpr() {
target = getTarget() and
// realloc(ptr, 0) only frees the pointer
not (
exists(target.getReallocPtrArg()) and
getArgument(target.getSizeArg()).getValue().toInt() = 0
)
}
override Expr getSizeExpr() { result = getArgument(target.getSizeArg()) }
override int getSizeMult() {
// malloc with multiplier argument that is a constant
result = getArgument(target.getSizeMult()).getValue().toInt()
or
// malloc with no multiplier argument
not exists(target.getSizeMult()) and
result = 1
}
override int getSizeBytes() { result = getSizeExpr().getValue().toInt() * getSizeMult() }
override Expr getReallocPtr() { result = getArgument(target.getReallocPtrArg()) }
override predicate requiresDealloc() { target.requiresDealloc() }
}
/**
* An allocation expression that is a `new` expression.
*/
class NewAllocationExpr extends AllocationExpr, NewExpr {
NewAllocationExpr() { this instanceof NewExpr }
override int getSizeBytes() { result = getAllocatedType().getSize() }
override predicate requiresDealloc() { not exists(getPlacementPointer()) }
}
/**
* An allocation expression that is a `new []` expression.
*/
class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
NewArrayAllocationExpr() { this instanceof NewArrayExpr }
override Expr getSizeExpr() {
// new array expr with variable size
result = getExtent()
}
override int getSizeMult() {
// new array expr with variable size
exists(getExtent()) and
result = getAllocatedElementType().getSize()
}
override int getSizeBytes() { result = getAllocatedType().getSize() }
override predicate requiresDealloc() { not exists(getPlacementPointer()) }
}

View File

@@ -0,0 +1,109 @@
import semmle.code.cpp.models.interfaces.Allocation
/**
* A deallocation function such as `free`.
*/
class StandardDeallocationFunction extends DeallocationFunction {
int freedArg;
StandardDeallocationFunction() {
exists(string name |
hasGlobalName(name) and
(
name = "free" and freedArg = 0
or
name = "realloc" and freedArg = 0
)
or
hasGlobalOrStdName(name) and
(
name = "ExFreePoolWithTag" and freedArg = 0
or
name = "ExFreeToLookasideListEx" and freedArg = 1
or
name = "ExFreeToPagedLookasideList" and freedArg = 1
or
name = "ExFreeToNPagedLookasideList" and freedArg = 1
or
name = "ExDeleteTimer" and freedArg = 0
or
name = "IoFreeMdl" and freedArg = 0
or
name = "IoFreeWorkItem" and freedArg = 0
or
name = "IoFreeErrorLogEntry" and freedArg = 0
or
name = "MmFreeContiguousMemory" and freedArg = 0
or
name = "MmFreeContiguousMemorySpecifyCache" and freedArg = 0
or
name = "MmFreeNonCachedMemory" and freedArg = 0
or
name = "MmFreeMappingAddress" and freedArg = 0
or
name = "MmFreePagesFromMdl" and freedArg = 0
or
name = "MmUnmapReservedMapping" and freedArg = 0
or
name = "MmUnmapLockedPages" and freedArg = 0
or
name = "LocalFree" and freedArg = 0
or
name = "GlobalFree" and freedArg = 0
or
name = "HeapFree" and freedArg = 2
or
name = "VirtualFree" and freedArg = 0
or
name = "CoTaskMemFree" and freedArg = 0
or
name = "SysFreeString" and freedArg = 0
or
name = "LocalReAlloc" and freedArg = 0
or
name = "GlobalReAlloc" and freedArg = 0
or
name = "HeapReAlloc" and freedArg = 2
or
name = "CoTaskMemRealloc" and freedArg = 0
or
name = "kmem_free" and freedArg = 0
or
name = "pool_put" and freedArg = 1
or
name = "pool_cache_put" and freedArg = 1
)
)
}
override int getFreedArg() { result = freedArg }
}
/**
* An deallocation expression that is a function call, such as call to `free`.
*/
class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
DeallocationFunction target;
CallDeallocationExpr() { target = getTarget() }
override Expr getFreedExpr() { result = getArgument(target.getFreedArg()) }
}
/**
* An deallocation expression that is a `delete` expression.
*/
class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
DeleteDeallocationExpr() { this instanceof DeleteExpr }
override Expr getFreedExpr() { result = getExpr() }
}
/**
* An deallocation expression that is a `delete []` expression.
*/
class DeleteArrayDeallocationExpr extends DeallocationExpr, DeleteArrayExpr {
DeleteArrayDeallocationExpr() { this instanceof DeleteArrayExpr }
override Expr getFreedExpr() { result = getExpr() }
}

View File

@@ -0,0 +1,82 @@
/**
* Provides an abstract class for modelling functions and expressions that
* allocate memory, such as the standard `malloc` function. To use this QL
* library, create one or more QL classes extending a class here with a
* characteristic predicate that selects the functions or expressions you are
* trying to model. Within that class, override the predicates provided
* by the abstract class to match the specifics of those functions or
* expressions. Finally, add a private import statement to `Models.qll`.
*/
import semmle.code.cpp.Function
import semmle.code.cpp.models.Models
/**
* An allocation function such as `malloc`.
*/
abstract class AllocationFunction extends Function {
/**
* 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() { none() }
/**
* Gets the index of an argument that multiplies the allocation size given by
* `getSizeArg`, if any.
*/
int getSizeMult() { none() }
/**
* Gets the index of the input pointer argument to be reallocated, if this
* is a `realloc` function.
*/
int getReallocPtrArg() { none() }
/**
* Whether or not 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() { any() }
}
/**
* An allocation expression such as call to `malloc` or a `new` expression.
*/
abstract class AllocationExpr extends Expr {
/**
* Gets an expression for the allocation size, if any. The actual allocation
* size is the value of this expression multiplied by the result of
* `getSizeMult()`, in bytes.
*/
Expr getSizeExpr() { none() }
/**
* Gets a constant multiplier for the allocation size given by `getSizeExpr`,
* in bytes.
*/
int getSizeMult() { none() }
/**
* Gets the size of this allocation in bytes, if it is a fixed size and that
* size can be determined.
*/
int getSizeBytes() { none() }
/**
* Gets the expression for the input pointer argument to be reallocated, if
* this is a `realloc` function.
*/
Expr getReallocPtr() { none() }
/**
* Whether or not 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() { any() }
}

View File

@@ -0,0 +1,32 @@
/**
* Provides an abstract class for modelling functions and expressions that
* deallocate memory, such as the standard `free` function. To use this QL
* library, create one or more QL classes extending a class here with a
* characteristic predicate that selects the functions or expressions you are
* trying to model. Within that class, override the predicates provided
* by the abstract class to match the specifics of those functions or
* expressions. Finally, add a private import statement to `Models.qll`.
*/
import semmle.code.cpp.Function
import semmle.code.cpp.models.Models
/**
* A deallocation function such as `free`.
*/
abstract class DeallocationFunction extends Function {
/**
* Gets the index of the argument that is freed by this function.
*/
int getFreedArg() { none() }
}
/**
* An deallocation expression such as call to `free` or a `delete` expression.
*/
abstract class DeallocationExpr extends Expr {
/**
* Gets the expression that is freed by this function.
*/
Expr getFreedExpr() { none() }
}

View File

@@ -137,3 +137,9 @@ int overloadedNew() {
return five;
}
void multidimensionalNew(int x, int y) {
auto p1 = new char[x][10];
auto p2 = new char[20][20];
auto p3 = new char[x][30][30];
}

View File

@@ -11,15 +11,18 @@ newExprs
| allocators.cpp:129:3:129:21 | new | int | operator new(size_t, void *) -> void * | 4 | 4 | |
| allocators.cpp:135:3:135:26 | new | int | operator new(size_t, const nothrow_t &) -> void * | 4 | 4 | |
newArrayExprs
| allocators.cpp:68:3:68:12 | new[] | int | operator new[](unsigned long) -> void * | 4 | 4 | |
| allocators.cpp:69:3:69:18 | new[] | int | operator new[](size_t, float) -> void * | 4 | 4 | |
| allocators.cpp:70:3:70:15 | new[] | String | operator new[](unsigned long) -> void * | 8 | 8 | |
| allocators.cpp:71:3:71:20 | new[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned |
| allocators.cpp:72:3:72:16 | new[] | String | operator new[](unsigned long) -> void * | 8 | 8 | |
| allocators.cpp:108:3:108:19 | new[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | |
| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned |
| allocators.cpp:132:3:132:17 | new[] | int | operator new[](size_t, void *) -> void * | 4 | 4 | |
| allocators.cpp:136:3:136:26 | new[] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | |
| allocators.cpp:68:3:68:12 | new[] | int[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | n |
| allocators.cpp:69:3:69:18 | new[] | int[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | n |
| allocators.cpp:70:3:70:15 | new[] | String[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | n |
| allocators.cpp:71:3:71:20 | new[] | Overaligned[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | n |
| allocators.cpp:72:3:72:16 | new[] | String[10] | String | operator new[](unsigned long) -> void * | 8 | 8 | | |
| allocators.cpp:108:3:108:19 | new[] | FailedInit[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | n |
| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned[10] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | |
| allocators.cpp:132:3:132:17 | new[] | int[1] | int | operator new[](size_t, void *) -> void * | 4 | 4 | | |
| allocators.cpp:136:3:136:26 | new[] | int[2] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | | |
| allocators.cpp:142:13:142:27 | new[] | char[][10] | char[10] | operator new[](unsigned long) -> void * | 10 | 1 | | x |
| allocators.cpp:143:13:143:28 | new[] | char[20][20] | char[20] | operator new[](unsigned long) -> void * | 20 | 1 | | |
| allocators.cpp:144:13:144:31 | new[] | char[][30][30] | char[30][30] | operator new[](unsigned long) -> void * | 900 | 1 | | x |
newExprDeallocators
| allocators.cpp:52:3:52:14 | new | String | operator delete(void *, unsigned long) -> void | 8 | 8 | sized |
| allocators.cpp:53:3:53:27 | new | String | operator delete(void *, float) -> void | 8 | 8 | |

View File

@@ -13,16 +13,20 @@ query predicate newExprs(NewExpr expr, string type, string sig, int size, int al
}
query predicate newArrayExprs(
NewArrayExpr expr, string type, string sig, int size, int alignment, string form
NewArrayExpr expr, string t1, string t2, string sig, int size, int alignment, string form,
string extents
) {
exists(Function allocator, Type elementType |
exists(Function allocator, Type arrayType, Type elementType |
expr.getAllocator() = allocator and
sig = allocator.getFullSignature() and
arrayType = expr.getAllocatedType() and
t1 = arrayType.toString() and
elementType = expr.getAllocatedElementType() and
type = elementType.toString() and
t2 = elementType.toString() and
size = elementType.getSize() and
alignment = elementType.getAlignment() and
if expr.hasAlignedAllocation() then form = "aligned" else form = ""
(if expr.hasAlignedAllocation() then form = "aligned" else form = "") and
extents = concat(Expr e | e = expr.getExtent() | e.toString(), ", ")
)
}

View File

@@ -1,5 +1,7 @@
| tests1.cpp:26:21:26:26 | call to malloc | This allocation does not include space to null-terminate the string. |
| tests1.cpp:56:21:56:27 | call to realloc | This allocation does not include space to null-terminate the string. |
| tests1.cpp:67:21:67:26 | call to malloc | This allocation does not include space to null-terminate the string. |
| tests1.cpp:89:25:89:30 | call to malloc | This allocation does not include space to null-terminate the string. |
| tests3.cpp:25:21:25:31 | call to malloc | This allocation does not include space to null-terminate the string. |
| tests3.cpp:30:21:30:31 | call to malloc | This allocation does not include space to null-terminate the string. |
| tests3.cpp:53:17:53:44 | new[] | This allocation does not include space to null-terminate the string. |

View File

@@ -53,7 +53,7 @@ void tests1(int case_num)
break;
case 7:
buffer = (char *)realloc(buffer, strlen(str)); // BAD [NOT DETECTED]
buffer = (char *)realloc(buffer, strlen(str)); // BAD
strcpy(buffer, str);
break;

View File

@@ -50,7 +50,7 @@ void tests3(int case_num)
void test3b()
{
char *buffer = new char[strlen(str3global)]; // BAD [NOT DETECTED]
char *buffer = new char[strlen(str3global)]; // BAD
strcpy(buffer, str3global);

View File

@@ -1,3 +1,10 @@
| tests2.cpp:17:3:17:8 | call to wcscpy | This 'call to wcscpy' operation requires 12 bytes but the destination is only 8 bytes. |
| tests2.cpp:22:3:22:8 | call to wcscpy | This 'call to wcscpy' operation requires 16 bytes but the destination is only 12 bytes. |
| tests2.cpp:27:3:27:8 | call to wcscpy | This 'call to wcscpy' operation requires 20 bytes but the destination is only 16 bytes. |
| tests2.cpp:31:3:31:8 | call to wcscpy | This 'call to wcscpy' operation requires 24 bytes but the destination is only 20 bytes. |
| tests2.cpp:36:3:36:8 | call to wcscpy | This 'call to wcscpy' operation requires 28 bytes but the destination is only 24 bytes. |
| tests2.cpp:41:3:41:8 | call to wcscpy | This 'call to wcscpy' operation requires 32 bytes but the destination is only 28 bytes. |
| tests2.cpp:46:3:46:8 | call to wcscpy | This 'call to wcscpy' operation requires 36 bytes but the destination is only 32 bytes. |
| tests.c:54:3:54:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
| tests.c:58:3:58:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
| tests.c:62:17:62:24 | buffer10 | This 'scanf string argument' operation requires 11 bytes but the destination is only 10 bytes. |

View File

@@ -0,0 +1,48 @@
// library types, functions etc
typedef unsigned long size_t;
void *malloc(size_t size);
void *realloc(void *ptr, size_t size);
void *calloc(size_t nmemb, size_t size);
void free(void *ptr);
wchar_t *wcscpy(wchar_t *s1, const wchar_t *s2);
// --- Semmle tests ---
void tests2() {
wchar_t *buffer;
buffer = (wchar_t *)malloc(2 * sizeof(wchar_t));
wcscpy(buffer, L"1"); // GOOD
wcscpy(buffer, L"12"); // BAD: buffer overflow
free(buffer);
buffer = (wchar_t *)malloc(3 * sizeof(wchar_t));
wcscpy(buffer, L"12"); // GOOD
wcscpy(buffer, L"123"); // BAD: buffer overflow
free(buffer);
buffer = (wchar_t *)realloc(0, 4 * sizeof(wchar_t));
wcscpy(buffer, L"123"); // GOOD
wcscpy(buffer, L"1234"); // BAD: buffer overflow
buffer = (wchar_t *)realloc(buffer, 5 * sizeof(wchar_t));
wcscpy(buffer, L"1234"); // GOOD
wcscpy(buffer, L"12345"); // BAD: buffer overflow
free(buffer);
buffer = (wchar_t *)calloc(6, sizeof(wchar_t));
wcscpy(buffer, L"12345"); // GOOD
wcscpy(buffer, L"123456"); // BAD: buffer overflow
free(buffer);
buffer = (wchar_t *)calloc(sizeof(wchar_t), 7);
wcscpy(buffer, L"123456"); // GOOD
wcscpy(buffer, L"1234567"); // BAD: buffer overflow
free(buffer);
buffer = new wchar_t[8];
wcscpy(buffer, L"1234567"); // GOOD
wcscpy(buffer, L"12345678"); // BAD: buffer overflow
delete [] buffer;
}

View File

@@ -1,6 +1,9 @@
| test2.cpp:64:34:64:39 | call to calloc | This allocation does not include space to null-terminate the string. |
| test2.cpp:71:28:71:34 | call to realloc | This allocation does not include space to null-terminate the string. |
| test.c:16:20:16:25 | call to malloc | This allocation does not include space to null-terminate the string. |
| test.c:32:20:32:25 | call to malloc | This allocation does not include space to null-terminate the string. |
| test.c:49:20:49:25 | call to malloc | This allocation does not include space to null-terminate the string. |
| test.cpp:24:35:24:40 | call to malloc | This allocation does not include space to null-terminate the string. |
| test.cpp:63:28:63:33 | call to malloc | This allocation does not include space to null-terminate the string. |
| test.cpp:71:28:71:33 | call to malloc | This allocation does not include space to null-terminate the string. |
| test.cpp:106:24:106:48 | new[] | This allocation does not include space to null-terminate the string. |

View File

@@ -102,7 +102,7 @@ void good2(char *str, char *dest) {
}
void bad9(wchar_t *wstr) {
// BAD -- using new [NOT DETECTED]
// BAD -- using new
wchar_t *wbuffer = new wchar_t[wcslen(wstr)];
wcscpy(wbuffer, wstr);
delete wbuffer;

View File

@@ -4,8 +4,13 @@
typedef unsigned long size_t;
void *malloc(size_t size);
void *realloc(void *ptr, size_t size);
void *calloc(size_t nmemb, size_t size);
void free(void *ptr);
size_t strlen(const char *s);
size_t wcslen(const wchar_t *s);
char *strcpy(char *s1, const char *s2);
wchar_t *wcscpy(wchar_t *s1, const wchar_t *s2);
namespace std
{
@@ -47,4 +52,23 @@ void good1(char *str) {
free(buffer);
}
void bad2(wchar_t *str) {
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
wchar_t *buffer = (wchar_t *)calloc(wcslen(str), sizeof(wchar_t));
wcscpy(buffer, str);
free(buffer);
}
void bad3(wchar_t *str) {
// BAD -- Not allocating space for '\0' terminator
wchar_t *buffer = (wchar_t *)calloc(sizeof(wchar_t), wcslen(str));
wcscpy(buffer, str);
free(buffer);
}
void bad4(char *str) {
// BAD -- Not allocating space for '\0' terminator
char *buffer = (char *)realloc(0, strlen(str));
strcpy(buffer, str);
free(buffer);
}

View File

@@ -2,6 +2,8 @@
// library
typedef unsigned int size_t;
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void free(void* ptr);
struct FILE;
@@ -51,6 +53,13 @@ public:
myFile1 = fopen("file1.txt", "rt"); // GOOD
myFile2 = fopen("file2.txt", "rt"); // BAD: not closed in destructor
myArray1 = (int *)calloc(100, sizeof(int)); // BAD: not freed in destructor
myArray2 = new int[100]; // BAD: not deleted in destructor
myArray3 = new int[100]; // GOOD: deleted in destructor
myPtr7 = (int*)realloc(0, sizeof(int)); // GOOD: freed below (assuming the realloc succeeds)
myPtr8 = (int*)realloc(myPtr7, sizeof(int)); // BAD: not freed in destructor
}
~MyClass()
@@ -58,6 +67,7 @@ public:
delete myPtr1;
free(myPtr3);
fclose(myFile1);
delete [] myArray3;
}
void close()
@@ -75,6 +85,11 @@ public:
AutoPtr<int> myAutoPtr;
FILE *myFile1;
FILE *myFile2;
int *myArray1;
int *myArray2;
int *myArray3;
int *myPtr7;
int *myPtr8;
};
int main()

View File

@@ -1,8 +1,11 @@
| AV Rule 79.cpp:44:3:44:18 | ... = ... | Resource myPtr2 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:46:3:46:37 | ... = ... | Resource myPtr4 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:47:3:47:18 | ... = ... | Resource myPtr5 is acquired by class MyClass but not released in the destructor. It is released from close on line 65, so this function may need to be called from the destructor. |
| AV Rule 79.cpp:48:3:48:37 | ... = ... | Resource myPtr6 is acquired by class MyClass but not released in the destructor. It is released from close on line 66, so this function may need to be called from the destructor. |
| AV Rule 79.cpp:53:3:53:36 | ... = ... | Resource myFile2 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:46:3:46:18 | ... = ... | Resource myPtr2 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:48:3:48:37 | ... = ... | Resource myPtr4 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:49:3:49:18 | ... = ... | Resource myPtr5 is acquired by class MyClass but not released in the destructor. It is released from close on line 75, so this function may need to be called from the destructor. |
| AV Rule 79.cpp:50:3:50:37 | ... = ... | Resource myPtr6 is acquired by class MyClass but not released in the destructor. It is released from close on line 76, so this function may need to be called from the destructor. |
| AV Rule 79.cpp:55:3:55:36 | ... = ... | Resource myFile2 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:57:3:57:44 | ... = ... | Resource myArray1 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:58:3:58:25 | ... = ... | Resource myArray2 is acquired by class MyClass but not released anywhere in this class. |
| AV Rule 79.cpp:62:3:62:45 | ... = ... | Resource myPtr8 is acquired by class MyClass but not released anywhere in this class. |
| Container2.cpp:21:3:21:16 | ... = ... | Resource ptr3 is acquired by class Container2<char> but not released anywhere in this class. |
| Container2.cpp:21:3:21:16 | ... = ... | Resource ptr3 is acquired by class Container2<int> but not released anywhere in this class. |
| DeleteThis.cpp:56:3:56:24 | ... = ... | Resource ptr10 is acquired by class MyClass3 but not released anywhere in this class. |