mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Merge pull request #2463 from geoffw0/overflowcalc
CPP: Allocation and Deallocation libraries
This commit is contained in:
@@ -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`.
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
private import implementations.Allocation
|
||||
private import implementations.Deallocation
|
||||
private import implementations.Fread
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
|
||||
300
cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
Normal file
300
cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
Normal 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()) }
|
||||
}
|
||||
@@ -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() }
|
||||
}
|
||||
82
cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
Normal file
82
cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
Normal 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() }
|
||||
}
|
||||
@@ -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() }
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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 | |
|
||||
|
||||
@@ -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(), ", ")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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. |
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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. |
|
||||
|
||||
Reference in New Issue
Block a user