Merge branch 'main' into rdmarsh2/cpp/operand-reuse

This commit is contained in:
Robert Marsh
2021-02-16 15:42:58 -08:00
1569 changed files with 157120 additions and 26136 deletions

View File

@@ -4,9 +4,6 @@
* @kind graph
* @id cpp/architecture/class-hierarchies
* @graph.layout organic
* @workingset jhotdraw
* @result succeed 48
* @result_ondemand succeed 48
* @tags maintainability
*/

View File

@@ -4,9 +4,6 @@
* @kind chart
* @id cpp/architecture/inheritance-depth-distribution
* @chart.type line
* @workingset jhotdraw
* @result succeed 48
* @result_ondemand succeed 48
* @tags maintainability
*/

View File

@@ -1,7 +1,8 @@
/**
* @name Global namespace classes
* @description Finds classes that belong to no namespace.
* @kind table
* @kind problem
* @problem.severity recommendation
* @id cpp/architecture/global-namespace-classes
* @tags maintainability
* modularity

View File

@@ -4,9 +4,6 @@
* @kind problem
* @id cpp/architecture/classes-with-many-dependencies
* @problem.severity recommendation
* @workingset jhotdraw
* @result succeed 20
* @result_ondemand succeed 20
* @tags maintainability
* statistical
* non-attributable

View File

@@ -8,168 +8,41 @@ import semmle.code.cpp.AutogeneratedFile
predicate trivialPositiveIntValue(string s) {
// Small numbers
s = [0 .. 20].toString() or
// Popular powers of two (decimal)
s = "16" or
s = "24" or
s = "32" or
s = "64" or
s = "128" or
s = "256" or
s = "512" or
s = "1024" or
s = "2048" or
s = "4096" or
s = "16384" or
s = "32768" or
s = "65536" or
s = "1048576" or
s = "2147483648" or
s = "4294967296" or
// Popular powers of two, minus one (decimal)
s = "15" or
s = "31" or
s = "63" or
s = "127" or
s = "255" or
s = "511" or
s = "1023" or
s = "2047" or
s = "4095" or
s = "16383" or
s = "32767" or
s = "65535" or
s = "1048577" or
s = "2147483647" or
s = "4294967295" or
// Popular powers of two (32-bit hex)
s = "0x00000001" or
s = "0x00000002" or
s = "0x00000004" or
s = "0x00000008" or
s = "0x00000010" or
s = "0x00000020" or
s = "0x00000040" or
s = "0x00000080" or
s = "0x00000100" or
s = "0x00000200" or
s = "0x00000400" or
s = "0x00000800" or
s = "0x00001000" or
s = "0x00002000" or
s = "0x00004000" or
s = "0x00008000" or
s = "0x00010000" or
s = "0x00020000" or
s = "0x00040000" or
s = "0x00080000" or
s = "0x00100000" or
s = "0x00200000" or
s = "0x00400000" or
s = "0x00800000" or
s = "0x01000000" or
s = "0x02000000" or
s = "0x04000000" or
s = "0x08000000" or
s = "0x10000000" or
s = "0x20000000" or
s = "0x40000000" or
s = "0x80000000" or
// Popular powers of two, minus one (32-bit hex)
s = "0x00000001" or
s = "0x00000003" or
s = "0x00000007" or
s = "0x0000000f" or
s = "0x0000001f" or
s = "0x0000003f" or
s = "0x0000007f" or
s = "0x000000ff" or
s = "0x000001ff" or
s = "0x000003ff" or
s = "0x000007ff" or
s = "0x00000fff" or
s = "0x00001fff" or
s = "0x00003fff" or
s = "0x00007fff" or
s = "0x0000ffff" or
s = "0x0001ffff" or
s = "0x0003ffff" or
s = "0x0007ffff" or
s = "0x000fffff" or
s = "0x001fffff" or
s = "0x003fffff" or
s = "0x007fffff" or
s = "0x00ffffff" or
s = "0x01ffffff" or
s = "0x03ffffff" or
s = "0x07ffffff" or
s = "0x0fffffff" or
s = "0x1fffffff" or
s = "0x3fffffff" or
s = "0x7fffffff" or
s = "0xffffffff" or
// Popular powers of two (16-bit hex)
s = "0x0001" or
s = "0x0002" or
s = "0x0004" or
s = "0x0008" or
s = "0x0010" or
s = "0x0020" or
s = "0x0040" or
s = "0x0080" or
s = "0x0100" or
s = "0x0200" or
s = "0x0400" or
s = "0x0800" or
s = "0x1000" or
s = "0x2000" or
s = "0x4000" or
s = "0x8000" or
// Popular powers of two, minus one (16-bit hex)
s = "0x0001" or
s = "0x0003" or
s = "0x0007" or
s = "0x000f" or
s = "0x001f" or
s = "0x003f" or
s = "0x007f" or
s = "0x00ff" or
s = "0x01ff" or
s = "0x03ff" or
s = "0x07ff" or
s = "0x0fff" or
s = "0x1fff" or
s = "0x3fff" or
s = "0x7fff" or
s = "0xffff" or
// Popular powers of two (8-bit hex)
s = "0x01" or
s = "0x02" or
s = "0x04" or
s = "0x08" or
s = "0x10" or
s = "0x20" or
s = "0x40" or
s = "0x80" or
// Popular powers of two, minus one (8-bit hex)
s = "0x01" or
s = "0x03" or
s = "0x07" or
s = "0x0f" or
s = "0x1f" or
s = "0x3f" or
s = "0x7f" or
s = "0xff" or
s = "0x00" or
// Powers of ten
s = "10" or
s = "100" or
s = "1000" or
s = "10000" or
s = "100000" or
s = "1000000" or
s = "10000000" or
s = "100000000" or
s = "1000000000"
s =
[
// Popular powers of two (decimal)
"16", "24", "32", "64", "128", "256", "512", "1024", "2048", "4096", "16384", "32768",
"65536", "1048576", "2147483648", "4294967296",
// Popular powers of two, minus one (decimal)
"15", "31", "63", "127", "255", "511", "1023", "2047", "4095", "16383", "32767", "65535",
"1048577", "2147483647", "4294967295",
// Popular powers of two (32-bit hex)
"0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", "0x00000020",
"0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", "0x00000800",
"0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", "0x00020000",
"0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", "0x00800000",
"0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", "0x20000000",
"0x40000000", "0x80000000",
// Popular powers of two, minus one (32-bit hex)
"0x00000001", "0x00000003", "0x00000007", "0x0000000f", "0x0000001f", "0x0000003f",
"0x0000007f", "0x000000ff", "0x000001ff", "0x000003ff", "0x000007ff", "0x00000fff",
"0x00001fff", "0x00003fff", "0x00007fff", "0x0000ffff", "0x0001ffff", "0x0003ffff",
"0x0007ffff", "0x000fffff", "0x001fffff", "0x003fffff", "0x007fffff", "0x00ffffff",
"0x01ffffff", "0x03ffffff", "0x07ffffff", "0x0fffffff", "0x1fffffff", "0x3fffffff",
"0x7fffffff", "0xffffffff",
// Popular powers of two (16-bit hex)
"0x0001", "0x0002", "0x0004", "0x0008", "0x0010", "0x0020", "0x0040", "0x0080", "0x0100",
"0x0200", "0x0400", "0x0800", "0x1000", "0x2000", "0x4000", "0x8000",
// Popular powers of two, minus one (16-bit hex)
"0x0001", "0x0003", "0x0007", "0x000f", "0x001f", "0x003f", "0x007f", "0x00ff", "0x01ff",
"0x03ff", "0x07ff", "0x0fff", "0x1fff", "0x3fff", "0x7fff", "0xffff",
// Popular powers of two (8-bit hex)
"0x01", "0x02", "0x04", "0x08", "0x10", "0x20", "0x40", "0x80",
// Popular powers of two, minus one (8-bit hex)
"0x01", "0x03", "0x07", "0x0f", "0x1f", "0x3f", "0x7f", "0xff", "0x00",
// Powers of ten
"10", "100", "1000", "10000", "100000", "1000000", "10000000", "100000000", "1000000000"
]
}
predicate trivialIntValue(string s) {
@@ -235,10 +108,7 @@ predicate joiningStringTrivial(Literal lit) {
// understand (which is against the spirit of these queries).
stringLiteral(lit) and
exists(FunctionCall fc |
(
fc.getTarget().getName() = "operator+" or
fc.getTarget().getName() = "operator<<"
) and
fc.getTarget().getName() = ["operator+", "operator<<"] and
fc.getAnArgument().getAChild*() = lit
) and
lit.getValue().length() < 16

View File

@@ -59,14 +59,9 @@ class Options extends string {
predicate exits(Function f) {
f.getAnAttribute().hasName("noreturn")
or
exists(string name | f.hasGlobalOrStdName(name) |
name = "exit" or
name = "_exit" or
name = "abort" or
name = "__assert_fail" or
name = "longjmp" or
name = "__builtin_unreachable"
)
f.hasGlobalOrStdName([
"exit", "_exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
])
or
CustomOptions::exits(f) // old Options.qll
}

View File

@@ -21,15 +21,7 @@ class Initialization extends Function {
}
class Allocation extends FunctionCall {
Allocation() {
exists(string name | name = this.getTarget().getName() |
name = "malloc" or
name = "calloc" or
name = "alloca" or
name = "sbrk" or
name = "valloc"
)
}
Allocation() { this.getTarget().getName() = ["malloc", "calloc", "alloca", "sbrk", "valloc"] }
}
from Function f, Allocation a

View File

@@ -13,13 +13,8 @@ import cpp
class ForbiddenCall extends FunctionCall {
ForbiddenCall() {
exists(string name | name = this.getTarget().getName() |
name = "task_delay" or
name = "taskDelay" or
name = "sleep" or
name = "nanosleep" or
name = "clock_nanosleep"
)
this.getTarget().getName() =
["task_delay", "taskDelay", "sleep", "nanosleep", "clock_nanosleep"]
}
}

View File

@@ -6,12 +6,7 @@ import cpp
class SemaphoreCreation extends FunctionCall {
SemaphoreCreation() {
exists(string name | name = this.getTarget().getName() |
name = "semBCreate" or
name = "semMCreate" or
name = "semCCreate" or
name = "semRWCreate"
)
this.getTarget().getName() = ["semBCreate", "semMCreate", "semCCreate", "semRWCreate"]
}
Variable getSemaphore() { result.getAnAccess() = this.getParent().(Assignment).getLValue() }
@@ -72,11 +67,7 @@ class SemaphoreGive extends UnlockOperation {
}
class LockingPrimitive extends FunctionCall, LockOperation {
LockingPrimitive() {
exists(string name | name = this.getTarget().getName() |
name = "taskLock" or name = "intLock" or name = "taskRtpLock"
)
}
LockingPrimitive() { this.getTarget().getName() = ["taskLock", "intLock", "taskRtpLock"] }
override Function getLocked() { result = this.getTarget() }
@@ -89,11 +80,7 @@ class LockingPrimitive extends FunctionCall, LockOperation {
}
class UnlockingPrimitive extends FunctionCall, UnlockOperation {
UnlockingPrimitive() {
exists(string name | name = this.getTarget().getName() |
name = "taskUnlock" or name = "intUnlock" or name = "taskRtpUnlock"
)
}
UnlockingPrimitive() { this.getTarget().getName() = ["taskUnlock", "intUnlock", "taskRtpUnlock"] }
Function getLocked() { result = getMatchingLock().getLocked() }

View File

@@ -12,18 +12,7 @@
import cpp
predicate allowedTypedefs(TypedefType t) {
exists(string name | name = t.getName() |
name = "I64" or
name = "U64" or
name = "I32" or
name = "U32" or
name = "I16" or
name = "U16" or
name = "I8" or
name = "U8" or
name = "F64" or
name = "F32"
)
t.getName() = ["I64", "U64", "I32", "U32", "I16", "U16", "I8", "U8", "F64", "F32"]
}
/**

View File

@@ -5,8 +5,8 @@ import cpp
*/
class Task extends Function {
Task() {
exists(FunctionCall taskCreate, string name | name = "taskCreate" or name = "taskSpawn" |
name = taskCreate.getTarget().getName() and
exists(FunctionCall taskCreate |
taskCreate.getTarget().getName() = ["taskCreate", "taskSpawn"] and
this = taskCreate.getArgument(4).(AddressOfExpr).getAddressable()
)
}

View File

@@ -13,38 +13,17 @@ import cpp
import semmle.code.cpp.dataflow.DataFlow
predicate whitelist(Function f) {
exists(string fName |
fName = f.getName() and
(
fName = "ceil" or
fName = "ceilf" or
fName = "ceill" or
fName = "floor" or
fName = "floorf" or
fName = "floorl" or
fName = "nearbyint" or
fName = "nearbyintf" or
fName = "nearbyintl" or
fName = "rint" or
fName = "rintf" or
fName = "rintl" or
fName = "round" or
fName = "roundf" or
fName = "roundl" or
fName = "trunc" or
fName = "truncf" or
fName = "truncl" or
fName.matches("__builtin_%")
)
)
f.getName() =
[
"ceil", "ceilf", "ceill", "floor", "floorf", "floorl", "nearbyint", "nearbyintf",
"nearbyintl", "rint", "rintf", "rintl", "round", "roundf", "roundl", "trunc", "truncf",
"truncl"
] or
f.getName().matches("__builtin_%")
}
predicate whitelistPow(FunctionCall fc) {
(
fc.getTarget().getName() = "pow" or
fc.getTarget().getName() = "powf" or
fc.getTarget().getName() = "powl"
) and
fc.getTarget().getName() = ["pow", "powf", "powl"] and
exists(float value |
value = fc.getArgument(0).getValue().toFloat() and
(value.floor() - value).abs() < 0.001

View File

@@ -46,7 +46,7 @@ class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
override predicate isSource(DataFlow::Node source) {
exists(RemoteFlowFunction remoteFlow |
exists(RemoteFlowSourceFunction remoteFlow |
remoteFlow = source.asExpr().(Call).getTarget() and
remoteFlow.hasRemoteFlowSource(_, _)
)

View File

@@ -29,6 +29,8 @@ class QueryString extends EnvironmentRead {
}
class Configuration extends TaintTrackingConfiguration {
override predicate isSource(Expr source) { source instanceof QueryString }
override predicate isSink(Element tainted) {
exists(PrintStdoutCall call | call.getAnArgument() = tainted)
}

View File

@@ -27,6 +27,10 @@ class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) {
exists(SQLLikeFunction runSql | runSql.outermostWrapperFunctionCall(tainted, _))
}
override predicate isBarrier(Expr e) {
super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType
}
}
from

View File

@@ -0,0 +1,5 @@
unsigned limit = get_limit();
unsigned total = 0;
while (limit - total > 0) { // wrong: if `total` is greater than `limit` this will underflow and continue executing the loop.
total += get_data();
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule finds relational comparisons between the result of an unsigned subtraction and the value <code>0</code>.
Such comparisons are likely to be wrong as the value of an unsigned subtraction can never be negative. So the
relational comparison ends up checking whether the result of the subtraction is equal to <code>0</code>.
This is probably not what the programmer intended.
</p>
</overview>
<recommendation>
<p>If a relational comparison is intended, consider casting the result of the subtraction to a signed type.
If the intention was to test for equality, consider replacing the relational comparison with an equality test.
</p>
</recommendation>
<example>
<sample src="UnsignedDifferenceExpressionComparedZero.c" />
</example>
<references>
<li>SEI CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules">INT02-C. Understand integer conversion rules</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,49 @@
/**
* @name Unsigned difference expression compared to zero
* @description A subtraction with an unsigned result can never be negative. Using such an expression in a relational comparison with `0` is likely to be wrong.
* @kind problem
* @id cpp/unsigned-difference-expression-compared-zero
* @problem.severity warning
* @precision medium
* @tags security
* correctness
* external/cwe/cwe-191
*/
import cpp
import semmle.code.cpp.commons.Exclusions
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.controlflow.Guards
/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */
pragma[noinline]
predicate isGuarded(SubExpr sub, Expr left, Expr right) {
exists(GuardCondition guard |
guard.controls(sub.getBasicBlock(), true) and
guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false)
)
}
/** Holds if `sub` will never be negative. */
predicate nonNegative(SubExpr sub) {
not exprMightOverflowNegatively(sub.getFullyConverted())
or
// The subtraction is guarded by a check of the form `left >= right`.
exists(GVN left, GVN right |
// This is basically a poor man's version of a directional unbind operator.
strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and
strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and
isGuarded(sub, left.getAnExpr(), right.getAnExpr())
)
}
from RelationalOperation ro, SubExpr sub
where
not isFromMacroDefinition(ro) and
not isFromMacroDefinition(sub) and
ro.getLesserOperand().getValue().toInt() = 0 and
ro.getGreaterOperand() = sub and
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and
not nonNegative(sub)
select ro, "Unsigned subtraction can never be negative."

View File

@@ -34,6 +34,10 @@ predicate sqlite_encryption_used() {
}
class Configuration extends TaintTrackingConfiguration {
override predicate isSource(Expr source) {
super.isSource(source) and source instanceof SensitiveExpr
}
override predicate isSink(Element taintedArg) {
exists(SqliteFunctionCall sqliteCall |
taintedArg = sqliteCall.getASource() and

View File

@@ -326,52 +326,37 @@ class InitializationFunction extends Function {
// Return value is not a success code but the output functions never fail.
name.matches("_Interlocked%")
or
// Functions that never fail, according to MSDN.
name = "QueryPerformanceCounter"
or
name = "QueryPerformanceFrequency"
or
// Functions that never fail post-Vista, according to MSDN.
name = "InitializeCriticalSectionAndSpinCount"
or
// `rand_s` writes 0 to a non-null argument if it fails, according to MSDN.
name = "rand_s"
or
// IntersectRect initializes the argument regardless of whether the input intersects
name = "IntersectRect"
or
name = "SetRect"
or
name = "UnionRect"
or
// These functions appears to have an incorrect CFG, which leads to false positives
name = "PhysicalToLogicalDPIPoint"
or
name = "LogicalToPhysicalDPIPoint"
or
// Sets NtProductType to default on error
name = "RtlGetNtProductType"
or
// Our CFG is not sophisticated enough to detect that the argument is always initialized
name = "StringCchLengthA"
or
// All paths init the argument, and always returns SUCCESS.
name = "RtlUnicodeToMultiByteSize"
or
// All paths init the argument, and always returns SUCCESS.
name = "RtlMultiByteToUnicodeSize"
or
// All paths init the argument, and always returns SUCCESS.
name = "RtlUnicodeToMultiByteN"
or
// Always initializes argument
name = "RtlGetFirstRange"
or
// Destination range is zeroed out on failure, assuming first two parameters are valid
name = "memcpy_s"
or
// This zeroes the memory unconditionally
name = "SeCreateAccessState"
name =
[
// Functions that never fail, according to MSDN.
"QueryPerformanceCounter", "QueryPerformanceFrequency",
// Functions that never fail post-Vista, according to MSDN.
"InitializeCriticalSectionAndSpinCount",
// `rand_s` writes 0 to a non-null argument if it fails, according to MSDN.
"rand_s",
// IntersectRect initializes the argument regardless of whether the input intersects
"IntersectRect", "SetRect", "UnionRect",
// These functions appears to have an incorrect CFG, which leads to false positives
"PhysicalToLogicalDPIPoint", "LogicalToPhysicalDPIPoint",
// Sets NtProductType to default on error
"RtlGetNtProductType",
// Our CFG is not sophisticated enough to detect that the argument is always initialized
"StringCchLengthA",
// All paths init the argument, and always returns SUCCESS.
"RtlUnicodeToMultiByteSize",
// All paths init the argument, and always returns SUCCESS.
"RtlMultiByteToUnicodeSize",
// All paths init the argument, and always returns SUCCESS.
"RtlUnicodeToMultiByteN",
// Always initializes argument
"RtlGetFirstRange",
// Destination range is zeroed out on failure, assuming first two parameters are valid
"memcpy_s",
// This zeroes the memory unconditionally
"SeCreateAccessState",
// Argument initialization is optional, but always succeeds
"KeGetCurrentProcessorNumberEx"
]
)
}
}

View File

@@ -140,12 +140,9 @@ class FopenCreationExpr extends FileCreationExpr {
class FopensCreationExpr extends FileCreationExpr {
FopensCreationExpr() {
exists(string name | name = this.getTarget().getName() |
name = "fopen_s" or
name = "_wfopen_s"
) and
this.getTarget().getName() = ["fopen_s", "_wfopen_s"] and
exists(string mode |
(mode = "w" or mode = "a") and
mode = ["w", "a"] and
this.getArgument(2).getValue().matches(mode + "%")
)
}

View File

@@ -13,9 +13,10 @@ int main(int argc, char **argv)
char buf1[10];
scanf("%s", buf1);
// GOOD, length is specified. The length should be one less than the size of the buffer, since the last character is the NULL terminator.
char buf2[10];
sscanf(buf2, "%9s");
// GOOD, length is specified. The length should be one less than the size of the destination buffer, since the last character is the NULL terminator.
char buf2[20];
char buf3[10];
sscanf(buf2, "%9s", buf3);
// BAD, do not use scanf without specifying a length first
char file[10];

View File

@@ -0,0 +1,35 @@
// BAD: the memset call will probably be removed.
void getPassword(void) {
char pwd[64];
if (GetPassword(pwd, sizeof(pwd))) {
/* Checking of password, secure operations, etc. */
}
memset(pwd, 0, sizeof(pwd));
}
// GOOD: in this case the memset will not be removed.
void getPassword(void) {
char pwd[64];
if (retrievePassword(pwd, sizeof(pwd))) {
/* Checking of password, secure operations, etc. */
}
memset_s(pwd, 0, sizeof(pwd));
}
// GOOD: in this case the memset will not be removed.
void getPassword(void) {
char pwd[64];
if (retrievePassword(pwd, sizeof(pwd))) {
/* Checking of password, secure operations, etc. */
}
SecureZeroMemory(pwd, sizeof(pwd));
}
// GOOD: in this case the memset will not be removed.
void getPassword(void) {
char pwd[64];
if (retrievePassword(pwd, sizeof(pwd))) {
/* Checking of password, secure operations, etc. */
}
#pragma optimize("", off)
memset(pwd, 0, sizeof(pwd));
#pragma optimize("", on)
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Compiler optimization will exclude the cleaning of private information.
Using the <code>memset</code> function to clear private data in a variable that has no subsequent use is potentially dangerous, since the compiler can remove the call.
For some compilers, optimization is also possible when using calls to free memory after the <code>memset</code> function.</p>
<p>It is possible to miss detection of vulnerabilities if used to clear fields of structures or parts of a buffer.</p>
</overview>
<recommendation>
<p>We recommend to use the <code>RtlSecureZeroMemory</code> or <code>memset_s</code> functions, or compilation flags that exclude optimization of <code>memset</code> calls (e.g. -fno-builtin-memset).</p>
</recommendation>
<example>
<p>The following example demonstrates an erroneous and corrected use of the <code>memset</code> function.</p>
<sample src="CompilerRemovalOfCodeToClearBuffers.c" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations">MSC06-C. Beware of compiler optimizations</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,127 @@
/**
* @name Compiler Removal Of Code To Clear Buffers
* @description Using <code>memset</code> the function to clear private data in a variable that has no subsequent use
* is potentially dangerous because the compiler can remove the call.
* @kind problem
* @id cpp/compiler-removal-of-code-to-clear-buffers
* @problem.severity warning
* @precision medium
* @tags security
* external/cwe/cwe-14
*/
import cpp
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.StackAddress
/**
* A call to `memset` of the form `memset(ptr, value, num)`, for some local variable `ptr`.
*/
class CompilerRemovaMemset extends FunctionCall {
CompilerRemovaMemset() {
this.getTarget().hasGlobalOrStdName("memset") and
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
DataFlow::localFlow(source, sink) and
this.getArgument(0) = isv.getAnAccess() and
(
source.asExpr() = exp
or
// handle the case where exp is defined by an address being passed into some function.
source.asDefiningArgument() = exp
) and
exp.getLocation().getEndLine() < this.getArgument(0).getLocation().getStartLine() and
sink.asExpr() = this.getArgument(0)
)
}
predicate isExistsAllocForThisVariable() {
exists(AllocationExpr alloc, Variable v |
alloc = v.getAnAssignedValue() and
this.getArgument(0) = v.getAnAccess() and
alloc.getASuccessor+() = this
)
or
not stackPointerFlowsToUse(this.getArgument(0), _, _, _)
}
predicate isExistsFreeForThisVariable() {
exists(DeallocationExpr free, Variable v |
this.getArgument(0) = v.getAnAccess() and
free.getFreedExpr() = v.getAnAccess() and
this.getASuccessor+() = free
)
}
predicate isExistsCallWithThisVariableExcludingDeallocationCalls() {
exists(FunctionCall fc, Variable v |
not fc instanceof DeallocationExpr and
this.getArgument(0) = v.getAnAccess() and
fc.getAnArgument() = v.getAnAccess() and
this.getASuccessor+() = fc
)
}
predicate isVariableUseAfterMemsetExcludingCalls() {
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
DataFlow::localFlow(source, sink) and
this.getArgument(0) = isv.getAnAccess() and
source.asExpr() = isv.getAnAccess() and
exp.getLocation().getStartLine() > this.getArgument(2).getLocation().getEndLine() and
not exp.getParent() instanceof FunctionCall and
sink.asExpr() = exp
)
}
predicate isVariableUseBoundWithArgumentFunction() {
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Parameter p, Expr exp |
DataFlow::localFlow(source, sink) and
this.getArgument(0) = isv.getAnAccess() and
this.getEnclosingFunction().getAParameter() = p and
exp.getAChild*() = p.getAnAccess() and
source.asExpr() = exp and
sink.asExpr() = isv.getAnAccess()
)
}
predicate isVariableUseBoundWithGlobalVariable() {
exists(
DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, GlobalVariable gv, Expr exp
|
DataFlow::localFlow(source, sink) and
this.getArgument(0) = isv.getAnAccess() and
exp.getAChild*() = gv.getAnAccess() and
source.asExpr() = exp and
sink.asExpr() = isv.getAnAccess()
)
}
predicate isExistsCompilationFlagsBlockingRemoval() {
exists(Compilation c |
c.getAFileCompiled() = this.getFile() and
c.getAnArgument() = "-fno-builtin-memset"
)
}
predicate isUseVCCompilation() {
exists(Compilation c |
c.getAFileCompiled() = this.getFile() and
(
c.getArgument(2).matches("%gcc%") or
c.getArgument(2).matches("%g++%") or
c.getArgument(2).matches("%clang%") or
c.getArgument(2) = "--force-recompute"
)
)
}
}
from CompilerRemovaMemset fc
where
not (fc.isExistsAllocForThisVariable() and not fc.isExistsFreeForThisVariable()) and
not (fc.isExistsFreeForThisVariable() and not fc.isUseVCCompilation()) and
not fc.isVariableUseAfterMemsetExcludingCalls() and
not fc.isExistsCallWithThisVariableExcludingDeallocationCalls() and
not fc.isVariableUseBoundWithArgumentFunction() and
not fc.isVariableUseBoundWithGlobalVariable() and
not fc.isExistsCompilationFlagsBlockingRemoval()
select fc.getArgument(0), "This variable will not be cleared."

View File

@@ -0,0 +1,19 @@
image::image(int width, int height)
{
int x, y;
// allocate width * height pixels
pixels = new uint32_t[width * height];
// fill width * height pixels
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
pixels[(y * width) + height] = 0;
}
}
// ...
}

View File

@@ -0,0 +1,25 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The result of a multiplication is used in the size of an allocation. If the multiplication can be made to overflow, a much smaller amount of memory may be allocated than the rest of the code expects. This may lead to overflowing writes when the buffer is accessed later.</p>
</overview>
<recommendation>
<p>To fix this issue, ensure that the arithmetic used in the size of an allocation cannot overflow before memory is allocated.</p>
</recommendation>
<example>
<p>In the following example, an array of size <code>width * height</code> is allocated and stored as <code>pixels</code>. If <code>width</code> and <code>height</code> are set such that the multiplication overflows and wraps to a small value (say, 4) then the initialization code that follows the allocation will write beyond the end of the array.</p>
<sample src="AllocMultiplicationOverflow.cpp"/>
</example>
<references>
<li>
Cplusplus.com: <a href="http://www.cplusplus.com/articles/DE18T05o/">Integer overflow</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,40 @@
/**
* @name Multiplication result may overflow and be used in allocation
* @description Using a multiplication result that may overflow in the size of an allocation may lead to buffer overflows when the allocated memory is used.
* @kind path-problem
* @problem.severity warning
* @precision low
* @tags security
* correctness
* external/cwe/cwe-190
* external/cwe/cwe-128
* @id cpp/multiplication-overflow-in-alloc
*/
import cpp
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.dataflow.DataFlow
import DataFlow::PathGraph
class MultToAllocConfig extends DataFlow::Configuration {
MultToAllocConfig() { this = "MultToAllocConfig" }
override predicate isSource(DataFlow::Node node) {
// a multiplication of two non-constant expressions
exists(MulExpr me |
me = node.asExpr() and
forall(Expr e | e = me.getAnOperand() | not exists(e.getValue()))
)
}
override predicate isSink(DataFlow::Node node) {
// something that affects an allocation size
node.asExpr() = any(AllocationExpr ae).getSizeExpr().getAChild*()
}
}
from MultToAllocConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink,
"Potentially overflowing value from $@ is used in the size of this allocation.", source,
"multiplication"

View File

@@ -1,11 +0,0 @@
unsigned long sizeArray;
// BAD: let's consider several values, taking ULONG_MAX =18446744073709551615
// sizeArray = 60; (sizeArray - 10) = 50; true
// sizeArray = 10; (sizeArray - 10) = 0; false
// sizeArray = 1; (sizeArray - 10) = 18446744073709551607; true
// sizeArray = 0; (sizeArray - 10) = 18446744073709551606; true
if (sizeArray - 10 > 0)
// GOOD: Prevent overflow by checking the input
if (sizeArray > 10)

View File

@@ -1,33 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The code compares the unsigned difference with zero.
It is highly probable that the condition is wrong if the difference expression has the unsigned type.
The condition holds in all the cases when difference is not equal to zero.
It means that we may use condition not equal. But the programmer probably wanted to compare the difference of elements.</p>
<p>False positives include code in which the first difference element is always greater than or equal to the second.
For comparison, ">" such conditions are equivalent to "! =", And are recommended for replacement.
For comparison "> =", the conditions are always true and are recommended to be excluded.</p>
</overview>
<recommendation>
<p>Use a simple comparison of two elements, instead of comparing their difference to zero.</p>
</recommendation>
<example>
<p>The following example demonstrates an erroneous and corrected use of comparison.</p>
<sample src="UnsignedDifferenceExpressionComparedZero.c" />
</example>
<references>
<li>CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules">INT02-C. Understand integer conversion rules</a>.
</li>
</references>
</qhelp>

View File

@@ -1,23 +0,0 @@
/**
* @name Unsigned difference expression compared to zero
* @description It is highly probable that the condition is wrong if the difference expression has the unsigned type.
* The condition holds in all the cases when difference is not equal to zero. It means that we may use condition not equal.
* But the programmer probably wanted to compare the difference of elements.
* @kind problem
* @id cpp/unsigned-difference-expression-compared-zero
* @problem.severity warning
* @precision medium
* @tags security
* external/cwe/cwe-191
*/
import cpp
import semmle.code.cpp.commons.Exclusions
from RelationalOperation ro, SubExpr sub
where
not isFromMacroDefinition(ro) and
ro.getLesserOperand().getValue().toInt() = 0 and
ro.getGreaterOperand() = sub and
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned()
select ro, "Difference in condition is always greater than or equal to zero"

View File

@@ -16,6 +16,6 @@ import DataFlow::PathGraph
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
where b.hasFlowPath(source, sink)
select sink.getNode(),
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
source, "this source."
select sink.getNode(), source, sink,
"This write into the external location '" + sink.getNode() +
"' may contain unencrypted data from $@", source, "this source."

View File

@@ -0,0 +1,20 @@
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
// GOOD: this way we will exclude possible memory leak
unsigned char * tmp;
if (currentSize < newSize)
{
tmp = (unsigned char *)realloc(buffer, newSize);
}
if (tmp == NULL)
{
free(buffer);
}
else
buffer = tmp;

View File

@@ -0,0 +1,38 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Memory leak on failed call to realloc.
The expression <code>mem = realloc (mem, size)</code> is potentially dangerous, if the call fails, we will lose the pointer to the memory block.
An unsuccessful call is possible not only when trying to allocate a large amount of memory, but also when the process memory is strongly segmented.</p>
<p>False positives include code in which immediately after calling the realloc function, the pointer is manipulated without first checking for validity.
In this case, an exception will occur in the program and it will terminate.
But from the point of view of safe coding, these places require the attention of developers.
At this stage, false positives are also possible in situations where the exception handling is quite complicated and occurs outside the base block in which memory is redistributed.</p>
</overview>
<recommendation>
<p>We recommend storing the result in a temporary variable and eliminating memory leak.</p>
</recommendation>
<example>
<p>The following example demonstrates an erroneous and corrected use of the <code>realloc</code> function.</p>
<sample src="MemoryLeakOnFailedCallToRealloc.c" />
</example>
<references>
<li>
CERT C++ Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM51-CPP.+Properly+deallocate+dynamically+allocated+resources">MEM51-CPP. Properly deallocate dynamically allocated resources</a>.
</li>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/WIN30-C.+Properly+pair+allocation+and+deallocation+functions">WIN30-C. Properly pair allocation and deallocation functions</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,61 @@
/**
* @name Memory leak on failed call to realloc
* @description The expression mem = realloc (mem, size) is potentially dangerous, if the call fails, we will lose the pointer to the memory block.
* We recommend storing the result in a temporary variable and eliminating memory leak.
* @kind problem
* @id cpp/memory-leak-on-failed-call-to-realloc
* @problem.severity warning
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-401
*/
import cpp
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.valuenumbering.HashCons
/**
* A function call that potentially does not return (such as `exit`).
*/
class CallMayNotReturn extends FunctionCall {
CallMayNotReturn() {
// call that is known to not return
not exists(this.(ControlFlowNode).getASuccessor())
or
// call to another function that may not return
exists(CallMayNotReturn exit | getTarget() = exit.getEnclosingFunction())
}
}
/**
* A call to `realloc` of the form `v = realloc(v, size)`, for some variable `v`.
*/
class ReallocCallLeak extends FunctionCall {
Variable v;
ReallocCallLeak() {
exists(AssignExpr ex |
this.getTarget().hasGlobalOrStdName("realloc") and
this = ex.getRValue() and
hashCons(ex.getLValue()) = hashCons(this.getArgument(0)) and
v.getAnAccess() = this.getArgument(0)
)
}
/**
* Holds if failure of this allocation may be handled by termination, for
* example a call to `exit()`.
*/
predicate mayHandleByTermination() {
exists(GuardCondition guard, CallMayNotReturn exit |
this.(ControlFlowNode).getASuccessor*() = guard and
guard.getAChild*() = v.getAnAccess() and
guard.controls(exit.getBasicBlock(), _)
)
}
}
from ReallocCallLeak rcl
where not rcl.mayHandleByTermination()
select rcl, "possible loss of original pointer on unsuccessful call realloc"

View File

@@ -0,0 +1,35 @@
// BAD: on memory allocation error, the program terminates.
void badFunction(const int *source, std::size_t length) noexcept {
int * dest = new int[length];
std::memset(dest, 0, length);
// ..
}
// GOOD: memory allocation error will be handled.
void goodFunction(const int *source, std::size_t length) noexcept {
try {
int * dest = new int[length];
} catch(std::bad_alloc) {
// ...
}
std::memset(dest, 0, length);
// ..
}
// BAD: memory allocation error will not be handled.
void badFunction(const int *source, std::size_t length) noexcept {
try {
int * dest = new (std::nothrow) int[length];
} catch(std::bad_alloc) {
// ...
}
std::memset(dest, 0, length);
// ..
}
// GOOD: memory allocation error will be handled.
void goodFunction(const int *source, std::size_t length) noexcept {
int * dest = new (std::nothrow) int[length];
if (!dest) {
return;
}
std::memset(dest, 0, length);
// ..
}

View File

@@ -0,0 +1,27 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>When using the <code>new</code> operator to allocate memory, you need to pay attention to the different ways of detecting errors. <code>::operator new(std::size_t)</code> throws an exception on error, whereas <code>::operator new(std::size_t, const std::nothrow_t &amp;)</code> returns zero on error. The programmer can get confused and check the error that occurs when allocating memory incorrectly. That can lead to an unhandled program termination or to a violation of the program logic.</p>
</overview>
<recommendation>
<p>Use the correct error detection method corresponding with the memory allocation.</p>
</recommendation>
<example>
<p>The following example demonstrates various approaches to detecting memory allocation errors using the <code>new</code> operator.</p>
<sample src="WrongInDetectingAndHandlingMemoryAllocationErrors.cpp" />
</example>
<references>
<li>
CERT C++ Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors">MEM52-CPP. Detect and handle memory allocation errors</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,87 @@
/**
* @name Detect And Handle Memory Allocation Errors
* @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error.
* --the programmer can get confused when check the error that occurs when allocating memory incorrectly.
* @kind problem
* @id cpp/detect-and-handle-memory-allocation-errors
* @problem.severity warning
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-570
*/
import cpp
/**
* Lookup if condition compare with 0
*/
class IfCompareWithZero extends IfStmt {
IfCompareWithZero() {
this.getCondition().(EQExpr).getAChild().getValue() = "0"
or
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
this.hasElse()
or
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
this.getThen().getAChild*() instanceof ReturnStmt
}
}
/**
* lookup for calls to `operator new`, with incorrect error handling.
*/
class WrongCheckErrorOperatorNew extends FunctionCall {
Expr exp;
WrongCheckErrorOperatorNew() {
this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and
(
this.getTarget().hasGlobalOrStdName("operator new")
or
this.getTarget().hasGlobalOrStdName("operator new[]")
)
}
/**
* Holds if handler `try ... catch` exists.
*/
predicate isExistsTryCatchBlock() {
exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*())
}
/**
* Holds if results call `operator new` check in `operator if`.
*/
predicate isExistsIfCondition() {
exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it |
// call `operator new` directly from the condition of `operator if`.
this = ifc.getCondition().getAChild*()
or
// check results call `operator new` with variable appropriation
postDominates(ifc, this) and
aex.getAChild() = exp and
ifc.getCondition().getAChild().(VariableAccess).getTarget() =
aex.getLValue().(VariableAccess).getTarget()
or
// check results call `operator new` with declaration variable
postDominates(ifc, this) and
exp = it.getExpr() and
it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget()
)
}
/**
* Holds if `(std::nothrow)` exists in call `operator new`.
*/
predicate isExistsNothrow() { this.getAChild().toString() = "nothrow" }
}
from WrongCheckErrorOperatorNew op
where
// use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if`
op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock()
or
// use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block
not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition()
select op, "memory allocation error check is incorrect or missing"

View File

@@ -0,0 +1,9 @@
// BAD: if buffer does not have a terminal zero, then access outside the allocated memory is possible.
buffer[strlen(buffer)] = 0;
// GOOD: we will eliminate dangerous behavior if we use a different method of calculating the length.
size_t len;
...
buffer[len] = 0

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Potentially dangerous use of the strlen function to calculate the length of a string.
The expression <code>buffer[strlen(buffer)] = 0</code> is potentially dangerous, if the variable buffer does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior.
If terminal zero is present, then the specified expression is meaningless.</p>
<p>False positives include heavily nested strlen. This situation is unlikely.</p>
</overview>
<recommendation>
<p>We recommend using another method for calculating the string length</p>
</recommendation>
<example>
<p>The following example demonstrates an erroneous and corrected use of the strlen function.</p>
<sample src="AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.c" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/STR32-C.+Do+not+pass+a+non-null-terminated+character+sequence+to+a+library+function+that+expects+a+string">STR32-C. Do not pass a non-null-terminated character sequence to a library function that expects a string</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,34 @@
/**
* @name Access Of Memory Location After End Of Buffer
* @description The expression `buffer [strlen (buffer)] = 0` is potentially dangerous, if the variable `buffer` does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior.
* If terminal zero is present, then the specified expression is meaningless.
* @kind problem
* @id cpp/access-memory-location-after-end-buffer
* @problem.severity warning
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-788
*/
import cpp
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.dataflow.DataFlow
from StrlenCall fc, AssignExpr expr, ArrayExpr exprarr
where
exprarr = expr.getLValue() and
expr.getRValue().getValue().toInt() = 0 and
globalValueNumber(exprarr.getArrayOffset()) = globalValueNumber(fc) and
not exists(Expr exptmp |
(
DataFlow::localExprFlow(fc, exptmp) or
exptmp.getAChild*() = fc.getArgument(0).(VariableAccess).getTarget().getAnAccess()
) and
dominates(exptmp, expr) and
postDominates(exptmp, fc) and
not exptmp.getEnclosingStmt() = fc.getEnclosingStmt() and
not exptmp.getEnclosingStmt() = expr.getEnclosingStmt()
) and
globalValueNumber(fc.getArgument(0)) = globalValueNumber(exprarr.getArrayBase())
select expr, "potential unsafe or redundant assignment."

View File

@@ -0,0 +1,4 @@
strncat(dest, source, sizeof(dest) - strlen(dest)); // BAD: writes a zero byte past the `dest` buffer.
strncat(dest, source, sizeof(dest) - strlen(dest) -1); // GOOD: Reserves space for the zero byte.

View File

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The standard library function <code>strncat(dest, source, count)</code> appends the <code>source</code> string to the <code>dest</code> string. <code>count</code> specifies the maximum number of characters to append and must be less than the remaining space in the target buffer. Calls of the form <code> strncat (dest, source, sizeof (dest) - strlen (dest)) </code> set the third argument to one more than possible. So when the <code>dest</code> is full, the expression <code> sizeof (dest) - strlen (dest) </code> will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the <code>dest</code> buffer.</p>
</overview>
<recommendation>
<p>We recommend subtracting one from the third argument. For example, replace <code>strncat(dest, source, sizeof(dest)-strlen(dest))</code> with <code>strncat(dest, source, sizeof(dest)-strlen(dest)-1)</code>.</p>
</recommendation>
<example>
<p>The following example demonstrates an erroneous and corrected use of the <code>strncat</code> function.</p>
<sample src="AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.
</li>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,64 @@
/**
* @name Access Of Memory Location After The End Of A Buffer Using Strncat
* @description Calls of the form `strncat(dest, source, sizeof (dest) - strlen (dest))` set the third argument to one more than possible. So when `dest` is full, the expression `sizeof(dest) - strlen (dest)` will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the `dest` buffer.
* @kind problem
* @id cpp/access-memory-location-after-end-buffer
* @problem.severity warning
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-788
*/
import cpp
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/**
* A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`.
*/
class WrongCallStrncat extends FunctionCall {
Expr leftsomeExpr;
WrongCallStrncat() {
this.getTarget().hasGlobalOrStdName("strncat") and
// the expression of the first argument in `strncat` and `strnlen` is identical
globalValueNumber(this.getArgument(0)) =
globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and
// using a string constant often speaks of manually calculating the length of the required buffer.
(
not this.getArgument(1) instanceof StringLiteral and
not this.getArgument(1) instanceof CharLiteral
) and
// for use in predicates
leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand()
}
/**
* Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`.
*/
predicate isExpressionEqualSizeof() {
// the left side of the expression `someExpr` is `sizeof(buf)`.
globalValueNumber(this.getArgument(0)) =
globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand())
or
// value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array.
leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize()
}
/**
* Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer.
*/
predicate isVariableEqualValueSizegBuffer() {
// the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`.
exists(AllocationExpr alc |
leftsomeExpr.(VariableAccess).getTarget() =
alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget()
)
}
}
from WrongCallStrncat sc
where
sc.isExpressionEqualSizeof() or
sc.isVariableEqualValueSizegBuffer()
select sc, "if the used buffer is full, writing out of the buffer is possible"

View File

@@ -50,5 +50,5 @@ class CStyleComment extends Comment {
* ```
*/
class CppStyleComment extends Comment {
CppStyleComment() { this.getContents().prefix(2) = "//" }
CppStyleComment() { this.getContents().matches("//%") }
}

View File

@@ -139,6 +139,19 @@ class Declaration extends Locatable, @declaration {
this.hasQualifiedName("std", "", name)
}
/**
* Holds if this declaration has the given name in the global namespace,
* the `std` namespace or the `bsl` namespace.
* We treat `std` and `bsl` as the same in some of our models.
*/
predicate hasGlobalOrStdOrBslName(string name) {
this.hasGlobalName(name)
or
this.hasQualifiedName("std", "", name)
or
this.hasQualifiedName("bsl", "", name)
}
/** Gets a specifier of this declaration. */
Specifier getASpecifier() { none() } // overridden in subclasses

View File

@@ -270,7 +270,12 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
}
/**
* A C++11 `static_assert` or C11 `_Static_assert` construct.
* A C++11 `static_assert` or C11 `_Static_assert` construct. For example each
* line in the following example contains a static assert:
* ```
* static_assert(sizeof(MyStruct) <= 4096);
* static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!");
* ```
*/
class StaticAssert extends Locatable, @static_assert {
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }

View File

@@ -363,20 +363,8 @@ class File extends Container, @file {
*/
class HeaderFile extends File {
HeaderFile() {
exists(string ext | ext = this.getExtension().toLowerCase() |
ext = "h" or
ext = "r" or
/* --- */ ext = "hpp" or
ext = "hxx" or
ext = "h++" or
ext = "hh" or
ext = "hp" or
ext = "tcc" or
ext = "tpp" or
ext = "txx" or
ext = "t++"
/* --- --- */
)
this.getExtension().toLowerCase() =
["h", "r", "hpp", "hxx", "h++", "hh", "hp", "tcc", "tpp", "txx", "t++"]
or
not exists(this.getExtension()) and
exists(Include i | i.getIncludedFile() = this)
@@ -406,7 +394,7 @@ class HeaderFile extends File {
* `File.compiledAsC`.
*/
class CFile extends File {
CFile() { exists(string ext | ext = this.getExtension().toLowerCase() | ext = "c" or ext = "i") }
CFile() { this.getExtension().toLowerCase() = ["c", "i"] }
override string getAPrimaryQlClass() { result = "CFile" }
}
@@ -419,21 +407,10 @@ class CFile extends File {
*/
class CppFile extends File {
CppFile() {
exists(string ext | ext = this.getExtension().toLowerCase() |
/* --- */ ext = "cpp" or
ext = "cxx" or
ext = "c++" or
ext = "cc" or
ext = "cp" or
ext = "icc" or
ext = "ipp" or
ext = "ixx" or
ext = "i++" or
ext = "ii"
/* --- */
// Note: .C files are indistinguishable from .c files on some
// file systems, so we just treat them as CFile's.
)
this.getExtension().toLowerCase() =
["cpp", "cxx", "c++", "cc", "cp", "icc", "ipp", "ixx", "i++", "ii"]
// Note: .C files are indistinguishable from .c files on some
// file systems, so we just treat them as CFile's.
}
override string getAPrimaryQlClass() { result = "CppFile" }

View File

@@ -334,6 +334,18 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
)
}
/**
* Gets the class of which this function, called `memberName`, is a member.
*
* Prefer to use `getDeclaringType()` or `getName()` directly if you do not
* need to reason about both.
*/
pragma[nomagic]
Class getClassAndName(string memberName) {
this.hasName(memberName) and
this.getDeclaringType() = result
}
/**
* Implements `ControlFlowNode.getControlFlowScope`. The `Function` is
* used to represent the exit node of the control flow graph, so it is
@@ -391,20 +403,30 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/** Holds if this function has a `noexcept` exception specification. */
predicate isNoExcept() { getADeclarationEntry().isNoExcept() }
/** Gets a function that overloads this one. */
/**
* Gets a function that overloads this one.
*
* Note: if _overrides_ are wanted rather than _overloads_ then
* `MemberFunction::getAnOverridingFunction` should be used instead.
*/
Function getAnOverload() {
result.getName() = getName() and
result.getNamespace() = getNamespace() and
result != this and
// If this function is declared in a class, only consider other
// functions from the same class. Conversely, if this function is not
// declared in a class, only consider other functions not declared in a
// class.
(
if exists(getDeclaringType())
then result.getDeclaringType() = getDeclaringType()
else not exists(result.getDeclaringType())
// If this function is declared in a class, only consider other
// functions from the same class.
exists(string name, Class declaringType |
candGetAnOverloadMember(name, declaringType, this) and
candGetAnOverloadMember(name, declaringType, result)
)
or
// Conversely, if this function is not
// declared in a class, only consider other functions not declared in a
// class.
exists(string name, Namespace namespace |
candGetAnOverloadNonMember(name, namespace, this) and
candGetAnOverloadNonMember(name, namespace, result)
)
) and
result != this and
// Instantiations and specializations don't participate in overload
// resolution.
not (
@@ -445,50 +467,15 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
// ... and likewise for destructors.
this.(Destructor).getADestruction().mayBeGloballyImpure()
else
not exists(string name | this.hasGlobalOrStdName(name) |
// Unless it's a function that we know is side-effect-free, it may
// have side-effects.
name = "strcmp" or
name = "wcscmp" or
name = "_mbscmp" or
name = "strlen" or
name = "wcslen" or
name = "_mbslen" or
name = "_mbslen_l" or
name = "_mbstrlen" or
name = "_mbstrlen_l" or
name = "strnlen" or
name = "strnlen_s" or
name = "wcsnlen" or
name = "wcsnlen_s" or
name = "_mbsnlen" or
name = "_mbsnlen_l" or
name = "_mbstrnlen" or
name = "_mbstrnlen_l" or
name = "strncmp" or
name = "wcsncmp" or
name = "_mbsncmp" or
name = "_mbsncmp_l" or
name = "strchr" or
name = "memchr" or
name = "wmemchr" or
name = "memcmp" or
name = "wmemcmp" or
name = "_memicmp" or
name = "_memicmp_l" or
name = "feof" or
name = "isdigit" or
name = "isxdigit" or
name = "abs" or
name = "fabs" or
name = "labs" or
name = "floor" or
name = "ceil" or
name = "atoi" or
name = "atol" or
name = "atoll" or
name = "atof"
)
// Unless it's a function that we know is side-effect-free, it may
// have side-effects.
not this.hasGlobalOrStdName([
"strcmp", "wcscmp", "_mbscmp", "strlen", "wcslen", "_mbslen", "_mbslen_l", "_mbstrlen",
"_mbstrlen_l", "strnlen", "strnlen_s", "wcsnlen", "wcsnlen_s", "_mbsnlen", "_mbsnlen_l",
"_mbstrnlen", "_mbstrnlen_l", "strncmp", "wcsncmp", "_mbsncmp", "_mbsncmp_l", "strchr",
"memchr", "wmemchr", "memcmp", "wmemcmp", "_memicmp", "_memicmp_l", "feof", "isdigit",
"isxdigit", "abs", "fabs", "labs", "floor", "ceil", "atoi", "atol", "atoll", "atof"
])
}
/**
@@ -497,6 +484,19 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
}
pragma[noinline]
private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) {
f.getName() = name and
f.getDeclaringType() = declaringType
}
pragma[noinline]
private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) {
f.getName() = name and
f.getNamespace() = namespace and
not exists(f.getDeclaringType())
}
/**
* A particular declaration or definition of a C/C++ function. For example the
* declaration and definition of `MyFunction` in the following code are each a
@@ -680,7 +680,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
/**
* A C/C++ non-member function (a function that is not a member of any
* class). For example the in the following code, `MyFunction` is a
* class). For example, in the following code, `MyFunction` is a
* `TopLevelFunction` but `MyMemberFunction` is not:
* ```
* void MyFunction() {

View File

@@ -7,8 +7,21 @@ import semmle.code.cpp.Type
import semmle.code.cpp.metrics.MetricNamespace
/**
* A C++ namespace.
* A C++ namespace. For example the (single) namespace `A` in the following
* code:
* ```
* namespace A
* {
* // ...
* }
*
* // ...
*
* namespace A
* {
* // ...
* }
* ```
* Note that namespaces are somewhat nebulous entities, as they do not in
* general have a single well-defined location in the source code. The
* related notion of a `NamespaceDeclarationEntry` is rather more concrete,
@@ -96,10 +109,22 @@ class Namespace extends NameQualifyingElement, @namespace {
}
/**
* A declaration of (part of) a C++ namespace.
* A declaration of (part of) a C++ namespace. This corresponds to a single
* `namespace N { ... }` occurrence in the source code. For example the two
* mentions of `A` in the following code:
* ```
* namespace A
* {
* // ...
* }
*
* This corresponds to a single `namespace N { ... }` occurrence in the
* source code.
* // ...
*
* namespace A
* {
* // ...
* }
* ```
*/
class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
/**
@@ -143,8 +168,9 @@ class UsingEntry extends Locatable, @using {
/**
* A C++ `using` declaration. For example:
*
* `using std::string;`
* ```
* using std::string;
* ```
*/
class UsingDeclarationEntry extends UsingEntry {
UsingDeclarationEntry() {
@@ -162,8 +188,9 @@ class UsingDeclarationEntry extends UsingEntry {
/**
* A C++ `using` directive. For example:
*
* `using namespace std;`
* ```
* using namespace std;
* ```
*/
class UsingDirectiveEntry extends UsingEntry {
UsingDirectiveEntry() {

View File

@@ -2,9 +2,14 @@ import semmle.code.cpp.Location
import semmle.code.cpp.Element
/**
* A C/C++ preprocessor directive.
*
* For example: `#ifdef`, `#line`, or `#pragma`.
* A C/C++ preprocessor directive. For example each of the following lines of
* code contains a `PreprocessorDirective`:
* ```
* #pragma once
* #ifdef MYDEFINE
* #include "myfile.h"
* #line 1 "source.c"
* ```
*/
class PreprocessorDirective extends Locatable, @preprocdirect {
override string toString() { result = "Preprocessor directive" }
@@ -98,9 +103,9 @@ class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBr
* A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`, or
* `#elif`.
*
* A branching directive can have its condition evaluated at compile-time,
* and as a result, the preprocessor will either take the branch, or not
* take the branch.
* A branching directive has a condition and that condition may be evaluated
* at compile-time. As a result, the preprocessor will either take the
* branch, or not take the branch.
*
* However, there are also situations in which a branch's condition isn't
* evaluated. The obvious case of this is when the directive is contained
@@ -136,8 +141,13 @@ class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch {
}
/**
* A C/C++ preprocessor `#if` directive.
*
* A C/C++ preprocessor `#if` directive. For example there is a
* `PreprocessorIf` on the first line of the following code:
* ```
* #if defined(MYDEFINE)
* // ...
* #endif
* ```
* For the related notion of a directive which causes branching (which
* includes `#if`, plus also `#ifdef`, `#ifndef`, and `#elif`), see
* `PreprocessorBranch`.
@@ -147,8 +157,13 @@ class PreprocessorIf extends PreprocessorBranch, @ppd_if {
}
/**
* A C/C++ preprocessor `#ifdef` directive.
*
* A C/C++ preprocessor `#ifdef` directive. For example there is a
* `PreprocessorIfdef` on the first line of the following code:
* ```
* #ifdef MYDEFINE
* // ...
* #endif
* ```
* The syntax `#ifdef X` is shorthand for `#if defined(X)`.
*/
class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
@@ -158,8 +173,13 @@ class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
}
/**
* A C/C++ preprocessor `#ifndef` directive.
*
* A C/C++ preprocessor `#ifndef` directive. For example there is a
* `PreprocessorIfndef` on the first line of the following code:
* ```
* #ifndef MYDEFINE
* // ...
* #endif
* ```
* The syntax `#ifndef X` is shorthand for `#if !defined(X)`.
*/
class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
@@ -167,42 +187,80 @@ class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
}
/**
* A C/C++ preprocessor `#else` directive.
* A C/C++ preprocessor `#else` directive. For example there is a
* `PreprocessorElse` on the fifth line of the following code:
* ```
* #ifdef MYDEFINE1
* // ...
* #elif MYDEFINE2
* // ...
* #else
* // ...
* #endif
* ```
*/
class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else {
override string toString() { result = "#else" }
}
/**
* A C/C++ preprocessor `#elif` directive.
* A C/C++ preprocessor `#elif` directive. For example there is a
* `PreprocessorElif` on the third line of the following code:
* ```
* #ifdef MYDEFINE1
* // ...
* #elif MYDEFINE2
* // ...
* #else
* // ...
* #endif
* ```
*/
class PreprocessorElif extends PreprocessorBranch, @ppd_elif {
override string toString() { result = "#elif " + this.getHead() }
}
/**
* A C/C++ preprocessor `#endif` directive.
* A C/C++ preprocessor `#endif` directive. For example there is a
* `PreprocessorEndif` on the third line of the following code:
* ```
* #ifdef MYDEFINE
* // ...
* #endif
* ```
*/
class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif {
override string toString() { result = "#endif" }
}
/**
* A C/C++ preprocessor `#warning` directive.
* A C/C++ preprocessor `#warning` directive. For example:
* ```
* #warning "This configuration is not supported."
* ```
*/
class PreprocessorWarning extends PreprocessorDirective, @ppd_warning {
override string toString() { result = "#warning " + this.getHead() }
}
/**
* A C/C++ preprocessor `#error` directive.
* A C/C++ preprocessor `#error` directive. For example:
* ```
* #error "This configuration is not implemented."
* ```
*/
class PreprocessorError extends PreprocessorDirective, @ppd_error {
override string toString() { result = "#error " + this.getHead() }
}
/**
* A C/C++ preprocessor `#undef` directive.
* A C/C++ preprocessor `#undef` directive. For example there is a
* `PreprocessorUndef` on the second line of the following code:
* ```
* #ifdef MYMACRO
* #undef MYMACRO
* #endif
* ```
*/
class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
override string toString() { result = "#undef " + this.getHead() }
@@ -214,7 +272,10 @@ class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
}
/**
* A C/C++ preprocessor `#pragma` directive.
* A C/C++ preprocessor `#pragma` directive. For example:
* ```
* #pragma once
* ```
*/
class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
override string toString() {
@@ -223,7 +284,10 @@ class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
}
/**
* A C/C++ preprocessor `#line` directive.
* A C/C++ preprocessor `#line` directive. For example:
* ```
* #line 1 "source.c"
* ```
*/
class PreprocessorLine extends PreprocessorDirective, @ppd_line {
override string toString() { result = "#line " + this.getHead() }

View File

@@ -385,7 +385,7 @@ private class DumpFunction extends DumpDeclaration, Function {
private string getACVQualifier() {
result = getASpecifier().getName() and
(result = "const" or result = "volatile")
result = ["const", "volatile"]
}
private string getDeclaratorSuffix() {

View File

@@ -171,8 +171,11 @@ class StdAttribute extends Attribute, @stdattribute {
}
/**
* An attribute introduced by Microsoft's `__declspec(name)` syntax, for
* example: `__declspec(dllimport)`.
* An attribute introduced by Microsoft's `__declspec(name)` syntax. For
* example the attribute on the following declaration:
* ```
* __declspec(dllimport) void myFunction();
* ```
*/
class Declspec extends Attribute, @declspec { }
@@ -186,8 +189,13 @@ class MicrosoftAttribute extends Attribute, @msattribute {
}
/**
* A C++11 `alignas` construct.
*
* A C++11 `alignas` construct. For example the attribute in the following
* code:
* ```
* struct alignas(16) MyStruct {
* int x;
* };
* ```
* Though it doesn't use the attribute syntax, `alignas(...)` is presented
* as an `Attribute` for consistency with the `[[align(...)]]` attribute.
*/
@@ -197,7 +205,11 @@ class AlignAs extends Attribute, @alignas {
/**
* A GNU `format` attribute of the form `__attribute__((format(archetype, format-index, first-arg)))`
* that declares a function to accept a `printf` style format string.
* that declares a function to accept a `printf` style format string. For example the attribute
* on the following declaration:
* ```
* int myPrintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
* ```
*/
class FormatAttribute extends GnuAttribute {
FormatAttribute() { getName() = "format" }
@@ -242,7 +254,11 @@ class FormatAttribute extends GnuAttribute {
}
/**
* An argument to an `Attribute`.
* An argument to an `Attribute`. For example the argument "dllimport" on the
* attribute in the following code:
* ```
* __declspec(dllimport) void myFunction();
* ```
*/
class AttributeArgument extends Element, @attribute_arg {
/**

View File

@@ -274,7 +274,7 @@ class Type extends Locatable, @type {
/**
* Gets this type with any typedefs resolved. For example, given
* `typedef C T`, this would resolve `const T&amp;` to `const C&amp;`.
* `typedef C T`, this would resolve `const T&` to `const C&`.
* Note that this will only work if the resolved type actually appears
* on its own elsewhere in the program.
*/
@@ -1544,9 +1544,9 @@ class FunctionPointerIshType extends DerivedType {
/**
* A C++ pointer to data member. See 15.5.
* ```
* class C { int m; };
* class C { public: int m; };
* int C::* p = &C::m; // pointer to data member m of class C
* class C *;
* class C c;
* int val = c.*p; // access data member
* ```
*/

View File

@@ -14,11 +14,7 @@ class PackedTimeType extends Type {
}
}
private predicate timeType(string typeName) {
typeName = "_SYSTEMTIME" or
typeName = "SYSTEMTIME" or
typeName = "tm"
}
private predicate timeType(string typeName) { typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm"] }
/**
* A type that is used to represent times and dates in an 'unpacked' form, that is,

View File

@@ -50,7 +50,15 @@ predicate primitiveVariadicFormatter(
then formatParamIndex = f.getNumberOfParameters() - 3
else formatParamIndex = f.getNumberOfParameters() - 2
) and
if type = "" then outputParamIndex = -1 else outputParamIndex = 0 // Conveniently, these buffer parameters are all at index 0.
(
if type = "" then outputParamIndex = -1 else outputParamIndex = 0 // Conveniently, these buffer parameters are all at index 0.
) and
not (
// exclude functions with an implementation in the snapshot source
// directory, as they may not be standard implementations.
exists(f.getBlock()) and
exists(f.getFile().getRelativePath())
)
}
private predicate callsVariadicFormatter(
@@ -900,6 +908,7 @@ class FormatLiteral extends Literal {
*/
int getNumArgNeeded(int n) {
exists(this.getConvSpecOffset(n)) and
exists(this.getConversionChar(n)) and
result = count(int mode | hasFormatArgumentIndexFor(n, mode))
}

View File

@@ -34,10 +34,10 @@ class Scanf extends ScanfFunction {
Scanf() {
this instanceof TopLevelFunction and
(
hasName("scanf") or // scanf(format, args...)
hasName("wscanf") or // wscanf(format, args...)
hasName("_scanf_l") or // _scanf_l(format, locale, args...)
hasName("_wscanf_l") // _wscanf_l(format, locale, args...)
hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
)
}
@@ -53,10 +53,10 @@ class Fscanf extends ScanfFunction {
Fscanf() {
this instanceof TopLevelFunction and
(
hasName("fscanf") or // fscanf(src_stream, format, args...)
hasName("fwscanf") or // fwscanf(src_stream, format, args...)
hasName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
hasName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
)
}
@@ -72,10 +72,10 @@ class Sscanf extends ScanfFunction {
Sscanf() {
this instanceof TopLevelFunction and
(
hasName("sscanf") or // sscanf(src_stream, format, args...)
hasName("swscanf") or // swscanf(src, format, args...)
hasName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
hasName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
)
}
@@ -91,8 +91,10 @@ class Snscanf extends ScanfFunction {
Snscanf() {
this instanceof TopLevelFunction and
(
hasName("_snscanf") or // _snscanf(src, max_amount, format, args...)
hasName("_snwscanf") // _snwscanf(src, max_amount, format, args...)
hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
// note that the max_amount is not a limit on the output length, it's an input length
// limit used with non null-terminated strings.
)
@@ -101,6 +103,12 @@ class Snscanf extends ScanfFunction {
override int getInputParameterIndex() { result = 0 }
override int getFormatParameterIndex() { result = 2 }
/**
* Gets the position at which the maximum number of characters in the
* input string is specified.
*/
int getInputLengthParameterIndex() { result = 1 }
}
/**

View File

@@ -87,7 +87,7 @@ abstract class MutexType extends Type {
private Function mustlockCandidate() {
exists(string name | name = result.getName() |
name = "lock" or
name.suffix(name.length() - 10) = "mutex_lock"
name.matches("%mutex\\_lock")
)
}
@@ -97,7 +97,7 @@ private Function mustlockCandidate() {
private Function trylockCandidate() {
exists(string name | name = result.getName() |
name = "try_lock" or
name.suffix(name.length() - 13) = "mutex_trylock"
name.matches("%mutex\\_trylock")
)
}
@@ -107,7 +107,7 @@ private Function trylockCandidate() {
private Function unlockCandidate() {
exists(string name | name = result.getName() |
name = "unlock" or
name.suffix(name.length() - 12) = "mutex_unlock"
name.matches("%mutex\\_unlock")
)
}

View File

@@ -13,7 +13,7 @@ import Dereferenced
* predicates that implement this analysis.
*/
abstract class DataflowAnnotation extends string {
DataflowAnnotation() { this = "pointer-null" or this = "pointer-valid" }
DataflowAnnotation() { this = ["pointer-null", "pointer-valid"] }
/** Holds if this annotation is the default annotation. */
abstract predicate isDefault();
@@ -98,7 +98,7 @@ abstract class DataflowAnnotation extends string {
* respectively.
*/
class NullnessAnnotation extends DataflowAnnotation {
NullnessAnnotation() { this = "pointer-null" or this = "pointer-valid" }
NullnessAnnotation() { this = ["pointer-null", "pointer-valid"] }
override predicate isDefault() { this = "pointer-valid" }

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -620,7 +620,8 @@ module FlowVar_internal {
private predicate largeVariable(Variable v, int liveBlocks, int defs) {
liveBlocks = strictcount(SubBasicBlock sbb | variableLiveInSBB(sbb, v)) and
defs = strictcount(SubBasicBlock sbb | exists(TBlockVar(sbb, v))) and
liveBlocks * defs > 1000000
// Convert to float to avoid int overflow (32-bit two's complement)
liveBlocks.(float) * defs.(float) > 100000.0
}
/**

View File

@@ -36,43 +36,33 @@ private predicate predictableInstruction(Instruction instr) {
* library's `returnArgument` predicate.
*/
predicate predictableOnlyFlow(string name) {
name = "strcasestr" or
name = "strchnul" or
name = "strchr" or
name = "strchrnul" or
name = "strcmp" or
name = "strcspn" or
name = "strncmp" or
name = "strndup" or
name = "strnlen" or
name = "strrchr" or
name = "strspn" or
name = "strstr" or
name = "strtod" or
name = "strtof" or
name = "strtol" or
name = "strtoll" or
name = "strtoq" or
name = "strtoul"
name =
[
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
"strtoul"
]
}
private DataFlow::Node getNodeForSource(Expr source) {
isUserInput(source, _) and
(
result = DataFlow::exprNode(source)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNodeFromArgument(source) and
not argv(source.(VariableAccess).getTarget())
)
result = getNodeForExpr(source)
}
private DataFlow::Node getNodeForExpr(Expr node) {
result = DataFlow::exprNode(node)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
not argv(node.(VariableAccess).getTarget())
}
private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
@@ -216,16 +206,27 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
cached
private predicate commonTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
instructionToInstructionTaintStep(fromNode.asInstruction(), toNode.asInstruction())
or
operandToInstructionTaintStep(fromNode.asOperand(), toNode.asInstruction())
or
operandToOperandTaintStep(fromNode.asOperand(), toNode.asOperand())
instructionToOperandTaintStep(fromNode.asInstruction(), toNode.asOperand())
}
private predicate operandToOperandTaintStep(Operand fromOperand, Operand toOperand) {
private predicate instructionToOperandTaintStep(Instruction fromInstr, Operand toOperand) {
// Propagate flow from the definition of an operand to the operand, even when the overlap is inexact.
// We only do this in certain cases:
// 1. The instruction's result must not be conflated, and
// 2. The instruction's result type is one the types where we expect element-to-object flow. Currently
// this is array types and union types. This matches the other two cases of element-to-object flow in
// `DefaultTaintTracking`.
toOperand.getAnyDef() = fromInstr and
not fromInstr.isResultConflated() and
(
fromInstr.getResultType() instanceof ArrayType or
fromInstr.getResultType() instanceof Union
)
or
exists(ReadSideEffectInstruction readInstr |
fromOperand = readInstr.getArgumentOperand() and
fromInstr = readInstr.getArgumentDef() and
toOperand = readInstr.getSideEffectOperand()
)
}
@@ -268,18 +269,18 @@ private predicate operandToInstructionTaintStep(Operand fromOperand, Instruction
outInstr.getPrimaryInstruction() = call
)
)
}
private predicate instructionToInstructionTaintStep(Instruction i1, Instruction i2) {
or
// Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1
toInstr.(LoadInstruction).getSourceAddressOperand() = fromOperand
or
// Flow through partial reads of arrays and unions
i2.(LoadInstruction).getSourceValueOperand().getAnyDef() = i1 and
not i1.isResultConflated() and
(
i1.getResultType() instanceof ArrayType or
i1.getResultType() instanceof Union
toInstr.(LoadInstruction).getSourceValueOperand() = fromOperand and
exists(Instruction fromInstr | fromInstr = fromOperand.getAnyDef() |
not fromInstr.isResultConflated() and
(
fromInstr.getResultType() instanceof ArrayType or
fromInstr.getResultType() instanceof Union
)
)
or
// Unary instructions tend to preserve enough information in practice that we
@@ -289,63 +290,54 @@ private predicate instructionToInstructionTaintStep(Instruction i1, Instruction
// `FieldAddressInstruction` could cause flow into one field to come out an
// unrelated field. This would happen across function boundaries, where the IR
// would not be able to match loads to stores.
i2.(UnaryInstruction).getUnary() = i1 and
toInstr.(UnaryInstruction).getUnaryOperand() = fromOperand and
(
not i2 instanceof FieldAddressInstruction
not toInstr instanceof FieldAddressInstruction
or
i2.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
toInstr.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
)
or
// Flow out of definition-by-reference
i2.(ChiInstruction).getPartial() = i1.(WriteSideEffectInstruction) and
not i2.isResultConflated()
or
// Flow from an element to an array or union that contains it.
i2.(ChiInstruction).getPartial() = i1 and
not i2.isResultConflated() and
exists(Type t | i2.getResultLanguageType().hasType(t, false) |
toInstr.(ChiInstruction).getPartialOperand() = fromOperand and
not toInstr.isResultConflated() and
exists(Type t | toInstr.getResultLanguageType().hasType(t, false) |
t instanceof Union
or
t instanceof ArrayType
)
or
exists(BinaryInstruction bin |
bin = i2 and
predictableInstruction(i2.getAnOperand().getDef()) and
i1 = i2.getAnOperand().getDef()
bin = toInstr and
predictableInstruction(toInstr.getAnOperand().getDef()) and
fromOperand = toInstr.getAnOperand()
)
or
// This is part of the translation of `a[i]`, where we want taint to flow
// from `a`.
i2.(PointerAddInstruction).getLeft() = i1
or
// Until we have from through indirections across calls, we'll take flow out
// of the parameter and into its indirection.
exists(IRFunction f, Parameter parameter |
i1 = getInitializeParameter(f, parameter) and
i2 = getInitializeIndirection(f, parameter)
)
toInstr.(PointerAddInstruction).getLeftOperand() = fromOperand
or
// Until we have flow through indirections across calls, we'll take flow out
// of the indirection and into the argument.
// When we get proper flow through indirections across calls, this code can be
// moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
exists(ReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
read.getArgumentDef() = i2
read.getSideEffectOperand() = fromOperand and
read.getArgumentDef() = toInstr
)
}
pragma[noinline]
private InitializeIndirectionInstruction getInitializeIndirection(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
}
pragma[noinline]
private InitializeParameterInstruction getInitializeParameter(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
or
// Until we have from through indirections across calls, we'll take flow out
// of the parameter and into its indirection.
// `InitializeIndirectionInstruction` only has a single operand: the address of the
// value whose indirection we are initializing. When initializing an indirection of a parameter `p`,
// the IR looks like this:
// ```
// m1 = InitializeParameter[p] : &r1
// r2 = Load[p] : r2, m1
// m3 = InitializeIndirection[p] : &r2
// ```
// So by having flow from `r2` to `m3` we're enabling flow from `m1` to `m3`. This relies on the
// `LoadOperand`'s overlap being exact.
toInstr.(InitializeIndirectionInstruction).getAnOperand() = fromOperand
}
/**
@@ -547,9 +539,15 @@ module TaintedWithPath {
* a characteristic predicate.
*/
class TaintTrackingConfiguration extends TSingleton {
/** Override this to specify which elements are sources in this configuration. */
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
/** Override this to specify which elements are sinks in this configuration. */
abstract predicate isSink(Element e);
/** Override this to specify which expressions are barriers in this configuration. */
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
/**
* Override this predicate to `any()` to allow taint to flow through global
* variables.
@@ -563,7 +561,11 @@ module TaintedWithPath {
private class AdjustedConfiguration extends DataFlow3::Configuration {
AdjustedConfiguration() { this = "AdjustedConfiguration" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSource(DataFlow::Node source) {
exists(TaintTrackingConfiguration cfg, Expr e |
cfg.isSource(e) and source = getNodeForExpr(e)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
@@ -579,7 +581,9 @@ module TaintedWithPath {
)
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrier(DataFlow::Node node) {
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
}
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
@@ -606,7 +610,8 @@ module TaintedWithPath {
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
cfg.hasFlow(sourceNode, sinkNode)
|
sourceNode = getNodeForSource(e)
sourceNode = getNodeForExpr(e) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
or
e = adjustedSink(sinkNode) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
@@ -660,7 +665,7 @@ module TaintedWithPath {
/** A PathNode whose `Element` is a source. It may also be a sink. */
private class InitialPathNode extends EndpointPathNode {
InitialPathNode() { exists(getNodeForSource(this.inner())) }
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
}
/** A PathNode whose `Element` is a sink. It may also be a source. */
@@ -682,14 +687,14 @@ module TaintedWithPath {
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner())
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
@@ -712,7 +717,7 @@ module TaintedWithPath {
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForSource(source) and
flowSource = getNodeForExpr(source) and
cfg.hasFlow(flowSource, flowSink) and
tainted = adjustedSink(flowSink) and
tainted = sinkNode.(FinalPathNode).inner()
@@ -734,8 +739,8 @@ module TaintedWithPath {
* through a global variable.
*/
predicate taintedWithoutGlobals(Element tainted) {
exists(PathNode sourceNode, FinalPathNode sinkNode |
sourceNode.(WrapPathNode).inner().getNode() = getNodeForSource(_) and
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
edgesWithoutGlobals+(sourceNode, sinkNode) and
tainted = sinkNode.inner()
)

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -3598,6 +3598,7 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
@@ -3611,6 +3612,7 @@ private module FlowExploration {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
not clearsContent(node, ap.getHead().getContent()) and
if node instanceof CastingNode
then compatibleTypes(getNodeType(node), ap.getType())
else any()

View File

@@ -716,21 +716,15 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
iTo = call
or
exists(int index, WriteSideEffectInstruction outNode |
modelOut.isParameterDeref(index) and
modelOut.isParameterDerefOrQualifierObject(index) and
iTo = outNode and
outNode = getSideEffectFor(call, index)
)
or
exists(WriteSideEffectInstruction outNode |
modelOut.isQualifierObject() and
iTo = outNode and
outNode = getSideEffectFor(call, -1)
)
) and
(
exists(int index |
modelIn.isParameter(index) and
opFrom = call.getPositionalArgumentOperand(index)
modelIn.isParameterOrQualifierAddress(index) and
opFrom = call.getArgumentOperand(index)
)
or
exists(int index, ReadSideEffectInstruction read |
@@ -739,9 +733,6 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
opFrom = read.getSideEffectOperand()
)
or
modelIn.isQualifierAddress() and
opFrom = call.getThisArgumentOperand()
or
exists(ReadSideEffectInstruction read |
modelIn.isQualifierObject() and
read = getSideEffectFor(call, -1) and

View File

@@ -341,7 +341,7 @@ module IRTypeConsistency {
query predicate multipleIRTypes(Language::LanguageType type, string message) {
strictcount(type.getIRType()) > 1 and
message =
"`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " +
"`LanguageType` " + type + " has multiple `IRType`s: " +
concat(type.getIRType().toString(), ", ")
}

View File

@@ -15,6 +15,10 @@ private import implementations.Strcat
private import implementations.Strcpy
private import implementations.Strdup
private import implementations.Strftime
private import implementations.Strtok
private import implementations.Strset
private import implementations.Strcrement
private import implementations.Strnextc
private import implementations.StdContainer
private import implementations.StdPair
private import implementations.StdMap
@@ -23,3 +27,6 @@ private import implementations.StdString
private import implementations.Swap
private import implementations.GetDelim
private import implementations.SmartPointer
private import implementations.Sscanf
private import implementations.Send
private import implementations.Recv

View File

@@ -14,95 +14,58 @@ private 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
or
// CRYPTO_malloc(size_t num, const char *file, int line)
name = "CRYPTO_malloc" and sizeArg = 0
or
// CRYPTO_zalloc(size_t num, const char *file, int line)
name = "CRYPTO_zalloc" and sizeArg = 0
or
// CRYPTO_secure_malloc(size_t num, const char *file, int line)
name = "CRYPTO_secure_malloc" and sizeArg = 0
or
// CRYPTO_secure_zalloc(size_t num, const char *file, int line)
name = "CRYPTO_secure_zalloc" and sizeArg = 0
)
)
// --- C library allocation
hasGlobalOrStdName("malloc") and // malloc(size)
sizeArg = 0
or
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
hasGlobalName([
// --- Windows Memory Management for Windows Drivers
"ExAllocatePool", // ExAllocatePool(type, size)
"ExAllocatePoolWithTag", // ExAllocatePool(type, size, tag)
"ExAllocatePoolWithTagPriority", // ExAllocatePoolWithTagPriority(type, size, tag, priority)
"ExAllocatePoolWithQuota", // ExAllocatePoolWithQuota(type, size)
"ExAllocatePoolWithQuotaTag", // ExAllocatePoolWithQuotaTag(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
hasGlobalName(["HeapAlloc"]) and // HeapAlloc(heap, flags, size)
sizeArg = 2
or
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 }
@@ -116,16 +79,14 @@ private 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
)
)
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 }
@@ -142,11 +103,10 @@ private class CallocAllocationFunction extends AllocationFunction {
int multArg;
CallocAllocationFunction() {
exists(string name |
hasGlobalOrStdName(name) and
// calloc(num, size)
(name = "calloc" and sizeArg = 1 and multArg = 0)
)
// --- C library allocation
hasGlobalOrStdName("calloc") and // calloc(num, size)
sizeArg = 1 and
multArg = 0
}
override int getSizeArg() { result = sizeArg }
@@ -163,29 +123,26 @@ private class ReallocAllocationFunction extends AllocationFunction {
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
or
// CRYPTO_realloc(void *addr, size_t num, const char *file, int line);
name = "CRYPTO_realloc" and sizeArg = 1 and reallocArg = 0
)
)
// --- C library allocation
hasGlobalOrStdName("realloc") and // realloc(ptr, size)
sizeArg = 1 and
reallocArg = 0
or
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
hasGlobalName("HeapReAlloc") and // HeapReAlloc(heap, flags, ptr, size)
sizeArg = 3 and
reallocArg = 2
}
override int getSizeArg() { result = sizeArg }
@@ -199,40 +156,20 @@ private class ReallocAllocationFunction extends AllocationFunction {
*/
private class SizelessAllocationFunction extends AllocationFunction {
SizelessAllocationFunction() {
exists(string name |
hasGlobalName(name) and
(
// 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"
)
)
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)
])
}
}

View File

@@ -13,77 +13,43 @@ private 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
name = "CRYPTO_free" and freedArg = 0
or
name = "CRYPTO_secure_free" 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
)
)
hasGlobalName([
// --- C library allocation
"free", "realloc",
// --- OpenSSL memory allocation
"CRYPTO_free", "CRYPTO_secure_free"
]) and
freedArg = 0
or
hasGlobalOrStdName([
// --- Windows Memory Management for Windows Drivers
"ExFreePoolWithTag", "ExDeleteTimer", "IoFreeMdl", "IoFreeWorkItem", "IoFreeErrorLogEntry",
"MmFreeContiguousMemory", "MmFreeContiguousMemorySpecifyCache", "MmFreeNonCachedMemory",
"MmFreeMappingAddress", "MmFreePagesFromMdl", "MmUnmapReservedMapping",
"MmUnmapLockedPages",
// --- Windows Global / Local legacy allocation
"LocalFree", "GlobalFree", "LocalReAlloc", "GlobalReAlloc",
// --- Windows System Services allocation
"VirtualFree",
// --- Windows COM allocation
"CoTaskMemFree", "CoTaskMemRealloc",
// --- Windows Automation
"SysFreeString",
// --- Solaris/BSD kernel memory allocator
"kmem_free"
]) and
freedArg = 0
or
hasGlobalOrStdName([
// --- Windows Memory Management for Windows Drivers
"ExFreeToLookasideListEx", "ExFreeToPagedLookasideList", "ExFreeToNPagedLookasideList",
// --- NetBSD pool manager
"pool_put", "pool_cache_put"
]) and
freedArg = 1
or
hasGlobalOrStdName(["HeapFree", "HeapReAlloc"]) and
freedArg = 2
}
override int getFreedArg() { result = freedArg }

View File

@@ -1,7 +1,7 @@
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.FlowSource
private class Fread extends AliasFunction, RemoteFlowFunction {
private class Fread extends AliasFunction, RemoteFlowSourceFunction {
Fread() { this.hasGlobalName("fread") }
override predicate parameterNeverEscapes(int n) {

View File

@@ -7,7 +7,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
* The standard functions `getdelim`, `getwdelim` and the glibc variant `__getdelim`.
*/
private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction,
RemoteFlowFunction {
RemoteFlowSourceFunction {
GetDelimFunction() { hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) }
override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) {

View File

@@ -8,7 +8,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
/**
* The POSIX function `getenv`.
*/
class Getenv extends LocalFlowFunction {
class Getenv extends LocalFlowSourceFunction {
Getenv() { this.hasGlobalName("getenv") }
override predicate hasLocalFlowSource(FunctionOutput output, string description) {

View File

@@ -14,7 +14,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
* The standard functions `gets` and `fgets`.
*/
private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
SideEffectFunction, RemoteFlowFunction {
SideEffectFunction, RemoteFlowSourceFunction {
GetsFunction() {
// gets(str)
// fgets(str, num, stream)

View File

@@ -6,12 +6,9 @@ import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function templates `std::move` and `std::forward`.
*/
private class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
IdentityFunction() {
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
this.getNamespace().getName() = "std" and
this.getName() = ["move", "forward"]
}
private class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction,
FunctionTemplateInstantiation {
IdentityFunction() { this.hasQualifiedName("std", ["move", "forward"]) }
override predicate hasOnlySpecificReadSideEffects() { any() }
@@ -32,5 +29,7 @@ private class IdentityFunction extends DataFlowFunction, SideEffectFunction, Ali
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// These functions simply return the argument value.
input.isParameter(0) and output.isReturnValue()
or
input.isParameterDeref(0) and output.isReturnValueDeref()
}
}

View File

@@ -109,6 +109,8 @@ private class IteratorCrementOperator extends Operator, DataFlowFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
or
input.isParameterDeref(0) and output.isReturnValueDeref()
}
}
@@ -159,6 +161,8 @@ private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunctio
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
or
input.isParameterDeref(0) and output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -201,6 +205,9 @@ private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunc
or
input.isReturnValueDeref() and
output.isQualifierObject()
or
input.isQualifierObject() and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {

View File

@@ -6,6 +6,7 @@
import semmle.code.cpp.Function
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.Taint
@@ -13,32 +14,35 @@ import semmle.code.cpp.models.interfaces.Taint
* The standard functions `memcpy`, `memmove` and `bcopy`; and the gcc variant
* `__builtin___memcpy_chk`.
*/
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction,
AliasFunction {
MemcpyFunction() {
// memcpy(dest, src, num)
// memmove(dest, src, num)
// memmove(dest, src, num, remaining)
this.hasName(["memcpy", "memmove", "__builtin___memcpy_chk"])
this.hasGlobalOrStdOrBslName(["memcpy", "memmove"])
or
// bcopy(src, dest, num)
this.hasGlobalOrStdName("bcopy")
// mempcpy(dest, src, num)
// memccpy(dest, src, c, n)
this.hasGlobalName(["bcopy", mempcpy(), "memccpy", "__builtin___memcpy_chk"])
}
/**
* Gets the index of the parameter that is the source buffer for the copy.
*/
int getParamSrc() { if this.hasGlobalOrStdName("bcopy") then result = 0 else result = 1 }
int getParamSrc() { if this.hasGlobalName("bcopy") then result = 0 else result = 1 }
/**
* Gets the index of the parameter that is the destination buffer for the
* copy.
*/
int getParamDest() { if this.hasGlobalOrStdName("bcopy") then result = 1 else result = 0 }
int getParamDest() { if this.hasGlobalName("bcopy") then result = 1 else result = 0 }
/**
* Gets the index of the parameter that is the size of the copy (in bytes).
*/
int getParamSize() { result = 2 }
int getParamSize() { if this.hasGlobalName("memccpy") then result = 3 else result = 2 }
override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() }
@@ -68,7 +72,10 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = getParamDest() and buffer = true and mustWrite = true
i = getParamDest() and
buffer = true and
// memccpy only writes until a given character `c` is found
(if this.hasGlobalName("memccpy") then mustWrite = false else mustWrite = true)
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
@@ -82,4 +89,21 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect
i = getParamSrc()
)
}
override predicate parameterNeverEscapes(int index) {
index = getParamSrc()
or
this.hasGlobalName("bcopy") and index = getParamDest()
}
override predicate parameterEscapesOnlyViaReturn(int index) {
not this.hasGlobalName("bcopy") and index = getParamDest()
}
override predicate parameterIsAlwaysReturned(int index) {
not this.hasGlobalName(["bcopy", mempcpy(), "memccpy"]) and
index = getParamDest()
}
}
private string mempcpy() { result = ["mempcpy", "wmempcpy"] }

View File

@@ -15,13 +15,11 @@ import semmle.code.cpp.models.interfaces.SideEffect
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
SideEffectFunction {
MemsetFunction() {
hasGlobalName("memset") or
hasGlobalName("wmemset") or
hasGlobalName("bzero") or
hasGlobalName("__builtin_memset") or
hasGlobalName("__builtin_memset_chk") or
hasQualifiedName("std", "memset") or
hasQualifiedName("std", "wmemset")
this.hasGlobalOrStdOrBslName("memset")
or
this.hasGlobalOrStdName("wmemset")
or
this.hasGlobalName([bzero(), "__builtin_memset", "__builtin_memset_chk"])
}
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
@@ -33,17 +31,17 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
bufParam = 0 and
(if hasGlobalName("bzero") then countParam = 1 else countParam = 2)
(if hasGlobalName(bzero()) then countParam = 1 else countParam = 2)
}
override predicate parameterNeverEscapes(int index) { hasGlobalName("bzero") and index = 0 }
override predicate parameterNeverEscapes(int index) { hasGlobalName(bzero()) and index = 0 }
override predicate parameterEscapesOnlyViaReturn(int index) {
not hasGlobalName("bzero") and index = 0
not hasGlobalName(bzero()) and index = 0
}
override predicate parameterIsAlwaysReturned(int index) {
not hasGlobalName("bzero") and index = 0
not hasGlobalName(bzero()) and index = 0
}
override predicate hasOnlySpecificReadSideEffects() { any() }
@@ -56,6 +54,8 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
i = 0 and
if hasGlobalName("bzero") then result = 1 else result = 2
if hasGlobalName(bzero()) then result = 1 else result = 2
}
}
private string bzero() { result = ["bzero", "explicit_bzero"] }

View File

@@ -15,21 +15,15 @@ private class Printf extends FormattingFunction, AliasFunction {
Printf() {
this instanceof TopLevelFunction and
(
hasGlobalOrStdName("printf") or
hasGlobalName("printf_s") or
hasGlobalOrStdName("wprintf") or
hasGlobalName("wprintf_s") or
hasGlobalName("g_printf")
hasGlobalOrStdOrBslName(["printf", "wprintf"]) or
hasGlobalName(["printf_s", "wprintf_s", "g_printf"])
) and
not exists(getDefinition().getFile().getRelativePath())
}
override int getFormatParameterIndex() { result = 0 }
deprecated override predicate isWideCharDefault() {
hasGlobalOrStdName("wprintf") or
hasGlobalName("wprintf_s")
}
deprecated override predicate isWideCharDefault() { hasName(["wprintf", "wprintf_s"]) }
override predicate isOutputGlobal() { any() }
@@ -47,8 +41,7 @@ private class Fprintf extends FormattingFunction {
Fprintf() {
this instanceof TopLevelFunction and
(
hasGlobalOrStdName("fprintf") or
hasGlobalOrStdName("fwprintf") or
hasGlobalOrStdOrBslName(["fprintf", "fwprintf"]) or
hasGlobalName("g_fprintf")
) and
not exists(getDefinition().getFile().getRelativePath())
@@ -56,7 +49,7 @@ private class Fprintf extends FormattingFunction {
override int getFormatParameterIndex() { result = 1 }
deprecated override predicate isWideCharDefault() { hasGlobalOrStdName("fwprintf") }
deprecated override predicate isWideCharDefault() { hasName("fwprintf") }
override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = true }
}
@@ -68,26 +61,18 @@ private class Sprintf extends FormattingFunction {
Sprintf() {
this instanceof TopLevelFunction and
(
// sprintf(dst, format, args...)
hasGlobalOrStdName("sprintf")
hasGlobalOrStdOrBslName([
"sprintf", // sprintf(dst, format, args...)
"wsprintf" // wsprintf(dst, format, args...)
])
or
// _sprintf_l(dst, format, locale, args...)
hasGlobalName("_sprintf_l")
or
// __swprintf_l(dst, format, locale, args...)
hasGlobalName("__swprintf_l")
or
// wsprintf(dst, format, args...)
hasGlobalOrStdName("wsprintf")
or
// g_strdup_printf(format, ...)
hasGlobalName("g_strdup_printf")
or
// g_sprintf(dst, format, ...)
hasGlobalName("g_sprintf")
or
// __builtin___sprintf_chk(dst, flag, os, format, ...)
hasGlobalName("__builtin___sprintf_chk")
hasGlobalName([
"_sprintf_l", // _sprintf_l(dst, format, locale, args...)
"__swprintf_l", // __swprintf_l(dst, format, locale, args...)
"g_strdup_printf", // g_strdup_printf(format, ...)
"g_sprintf", // g_sprintf(dst, format, ...)
"__builtin___sprintf_chk" // __builtin___sprintf_chk(dst, flag, os, format, ...)
])
) and
not exists(getDefinition().getFile().getRelativePath())
}
@@ -102,23 +87,20 @@ private class Sprintf extends FormattingFunction {
}
override int getFormatParameterIndex() {
hasGlobalName("g_strdup_printf") and result = 0
hasName("g_strdup_printf") and result = 0
or
hasGlobalName("__builtin___sprintf_chk") and result = 3
hasName("__builtin___sprintf_chk") and result = 3
or
getName() != "g_strdup_printf" and
getName() != "__builtin___sprintf_chk" and
not getName() = ["g_strdup_printf", "__builtin___sprintf_chk"] and
result = 1
}
override int getOutputParameterIndex(boolean isStream) {
not hasGlobalName("g_strdup_printf") and result = 0 and isStream = false
not hasName("g_strdup_printf") and result = 0 and isStream = false
}
override int getFirstFormatArgumentIndex() {
if hasGlobalName("__builtin___sprintf_chk")
then result = 4
else result = getNumberOfParameters()
if hasName("__builtin___sprintf_chk") then result = 4 else result = getNumberOfParameters()
}
}
@@ -129,26 +111,18 @@ private class SnprintfImpl extends Snprintf {
SnprintfImpl() {
this instanceof TopLevelFunction and
(
hasGlobalOrStdName("snprintf") or // C99 defines snprintf
hasGlobalOrStdName("swprintf") or // The s version of wide-char printf is also always the n version
hasGlobalOrStdOrBslName([
"snprintf", // C99 defines snprintf
"swprintf" // The s version of wide-char printf is also always the n version
])
or
// Microsoft has _snprintf as well as several other variations
hasGlobalName("sprintf_s") or
hasGlobalName("snprintf_s") or
hasGlobalName("swprintf_s") or
hasGlobalName("_snprintf") or
hasGlobalName("_snprintf_s") or
hasGlobalName("_snprintf_l") or
hasGlobalName("_snprintf_s_l") or
hasGlobalName("_snwprintf") or
hasGlobalName("_snwprintf_s") or
hasGlobalName("_snwprintf_l") or
hasGlobalName("_snwprintf_s_l") or
hasGlobalName("_sprintf_s_l") or
hasGlobalName("_swprintf_l") or
hasGlobalName("_swprintf_s_l") or
hasGlobalName("g_snprintf") or
hasGlobalName("wnsprintf") or
hasGlobalName("__builtin___snprintf_chk")
hasGlobalName([
"sprintf_s", "snprintf_s", "swprintf_s", "_snprintf", "_snprintf_s", "_snprintf_l",
"_snprintf_s_l", "_snwprintf", "_snwprintf_s", "_snwprintf_l", "_snwprintf_s_l",
"_sprintf_s_l", "_swprintf_l", "_swprintf_s_l", "g_snprintf", "wnsprintf",
"__builtin___snprintf_chk"
])
) and
not exists(getDefinition().getFile().getRelativePath())
}
@@ -184,12 +158,7 @@ private class SnprintfImpl extends Snprintf {
}
override predicate returnsFullFormatLength() {
(
hasGlobalOrStdName("snprintf") or
hasGlobalName("g_snprintf") or
hasGlobalName("__builtin___snprintf_chk") or
hasGlobalName("snprintf_s")
) and
hasName(["snprintf", "g_snprintf", "__builtin___snprintf_chk", "snprintf_s"]) and
not exists(getDefinition().getFile().getRelativePath())
}
@@ -202,16 +171,10 @@ private class SnprintfImpl extends Snprintf {
private class StringCchPrintf extends FormattingFunction {
StringCchPrintf() {
this instanceof TopLevelFunction and
(
hasGlobalName("StringCchPrintf") or
hasGlobalName("StringCchPrintfEx") or
hasGlobalName("StringCchPrintf_l") or
hasGlobalName("StringCchPrintf_lEx") or
hasGlobalName("StringCbPrintf") or
hasGlobalName("StringCbPrintfEx") or
hasGlobalName("StringCbPrintf_l") or
hasGlobalName("StringCbPrintf_lEx")
) and
hasGlobalName([
"StringCchPrintf", "StringCchPrintfEx", "StringCchPrintf_l", "StringCchPrintf_lEx",
"StringCbPrintf", "StringCbPrintfEx", "StringCbPrintf_l", "StringCbPrintf_lEx"
]) and
not exists(getDefinition().getFile().getRelativePath())
}

View File

@@ -7,10 +7,9 @@ import semmle.code.cpp.models.interfaces.SideEffect
private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction,
SideEffectFunction {
PureStrFunction() {
hasGlobalOrStdName([
"atof", "atoi", "atol", "atoll", "strcasestr", "strchnul", "strchr", "strchrnul", "strstr",
"strpbrk", "strcmp", "strcspn", "strncmp", "strrchr", "strspn", "strtod", "strtof",
"strtol", "strtoll", "strtoq", "strtoul"
hasGlobalOrStdOrBslName([
atoi(), "strcasestr", "strchnul", "strchr", "strchrnul", "strstr", "strpbrk", "strrchr",
"strspn", strtol(), strrev(), strcmp(), strlwr(), strupr()
])
}
@@ -24,11 +23,16 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(ParameterIndex i |
input.isParameter(i) and
exists(getParameter(i))
or
input.isParameterDeref(i) and
getParameter(i).getUnspecifiedType() instanceof PointerType
(
input.isParameter(i) and
exists(getParameter(i))
or
input.isParameterDeref(i) and
getParameter(i).getUnspecifiedType() instanceof PointerType
) and
// Functions that end with _l also take a locale argument (always as the last argument),
// and we don't want taint from those arguments.
(not this.getName().matches("%\\_l") or exists(getParameter(i + 1)))
) and
(
output.isReturnValueDeref() and
@@ -60,10 +64,35 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
}
}
private string atoi() { result = ["atof", "atoi", "atol", "atoll"] }
private string strtol() { result = ["strtod", "strtof", "strtol", "strtoll", "strtoq", "strtoul"] }
private string strlwr() {
result = ["_strlwr", "_wcslwr", "_mbslwr", "_strlwr_l", "_wcslwr_l", "_mbslwr_l"]
}
private string strupr() {
result = ["_strupr", "_wcsupr", "_mbsupr", "_strupr_l", "_wcsupr_l", "_mbsupr_l"]
}
private string strrev() { result = ["_strrev", "_wcsrev", "_mbsrev", "_mbsrev_l"] }
private string strcmp() {
// NOTE: `strcoll` doesn't satisfy _all_ the definitions of purity: its behavior depends on
// `LC_COLLATE` (which is set by `setlocale`). Not sure this behavior worth including in the model, so
// for now we interpret the function as being pure.
result =
[
"strcmp", "strcspn", "strncmp", "strcoll", "strverscmp", "_mbsnbcmp", "_mbsnbcmp_l",
"_stricmp"
]
}
/** String standard `strlen` function, and related functions for computing string lengths. */
private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
StrLenFunction() {
hasGlobalOrStdName(["strlen", "strnlen", "wcslen"])
hasGlobalOrStdOrBslName(["strlen", "strnlen", "wcslen"])
or
hasGlobalName(["_mbslen", "_mbslen_l", "_mbstrlen", "_mbstrlen_l"])
}
@@ -96,7 +125,7 @@ private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFun
/** Pure functions. */
private class PureFunction extends TaintFunction, SideEffectFunction {
PureFunction() { hasGlobalOrStdName(["abs", "labs"]) }
PureFunction() { hasGlobalOrStdOrBslName(["abs", "labs"]) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(ParameterIndex i |
@@ -114,7 +143,12 @@ private class PureFunction extends TaintFunction, SideEffectFunction {
/** Pure raw-memory functions. */
private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunction,
SideEffectFunction {
PureMemFunction() { hasGlobalOrStdName(["memchr", "memrchr", "rawmemchr", "memcmp", "memmem"]) }
PureMemFunction() {
hasGlobalOrStdOrBslName([
"memchr", "__builtin_memchr", "memrchr", "rawmemchr", "memcmp", "__builtin_memcmp", "memmem"
]) or
this.hasGlobalName("memfrob")
}
override predicate hasArrayInput(int bufParam) {
getParameter(bufParam).getUnspecifiedType() instanceof PointerType
@@ -122,11 +156,15 @@ private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunctio
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(ParameterIndex i |
input.isParameter(i) and
exists(getParameter(i))
or
input.isParameterDeref(i) and
getParameter(i).getUnspecifiedType() instanceof PointerType
(
input.isParameter(i) and
exists(getParameter(i))
or
input.isParameterDeref(i) and
getParameter(i).getUnspecifiedType() instanceof PointerType
) and
// `memfrob` should not have taint from the size argument.
(not this.hasGlobalName("memfrob") or i = 0)
) and
(
output.isReturnValueDeref() and

View File

@@ -0,0 +1,88 @@
/**
* Provides implementation classes modeling `recv` and various similar
* functions. See `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.models.interfaces.SideEffect
/** The function `recv` and its assorted variants */
private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction,
RemoteFlowSourceFunction {
Recv() {
this.hasGlobalName([
"recv", // recv(socket, dest, len, flags)
"recvfrom", // recvfrom(socket, dest, len, flags, from, fromlen)
"recvmsg", // recvmsg(socket, msg, flags)
"read", // read(socket, dest, len)
"pread", // pread(socket, dest, len, offset)
"readv", // readv(socket, dest, len)
"preadv", // readv(socket, dest, len, offset)
"preadv2" // readv2(socket, dest, len, offset, flags)
])
}
override predicate parameterNeverEscapes(int index) {
this.getParameter(index).getUnspecifiedType() instanceof PointerType
}
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate parameterIsAlwaysReturned(int index) { none() }
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
not this.hasGlobalName("recvmsg") and
bufParam = 1 and
countParam = 2
}
override predicate hasArrayInput(int bufParam) { this.hasGlobalName("recvfrom") and bufParam = 4 }
override predicate hasArrayOutput(int bufParam) {
bufParam = 1
or
this.hasGlobalName("recvfrom") and bufParam = 4
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
this.hasGlobalName("recvfrom") and
(
i = 4 and buffer = true
or
i = 5 and buffer = false
)
or
this.hasGlobalName("recvmsg") and
i = 1 and
buffer = true
}
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 1 and buffer = true and mustWrite = false
or
this.hasGlobalName("recvfrom") and
(
i = 4 and buffer = true and mustWrite = false
or
i = 5 and buffer = false and mustWrite = false
)
}
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
(
output.isParameterDeref(1)
or
this.hasGlobalName("recvfrom") and output.isParameterDeref([4, 5])
) and
description = "Buffer read by " + this.getName()
}
}

View File

@@ -0,0 +1,63 @@
/**
* Provides implementation classes modeling `send` and various similar
* functions. See `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.models.interfaces.SideEffect
/** The function `send` and its assorted variants */
private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, RemoteFlowSinkFunction {
Send() {
this.hasGlobalName([
"send", // send(socket, buf, len, flags)
"sendto", // sendto(socket, buf, len, flags, to, tolen)
"sendmsg", // sendmsg(socket, msg, flags)
"write", // write(socket, buf, len)
"writev", // writev(socket, buf, len)
"pwritev", // pwritev(socket, buf, len, offset)
"pwritev2" // pwritev2(socket, buf, len, offset, flags)
])
}
override predicate parameterNeverEscapes(int index) {
this.getParameter(index).getUnspecifiedType() instanceof PointerType
}
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate parameterIsAlwaysReturned(int index) { none() }
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
not this.hasGlobalName("sendmsg") and
bufParam = 1 and
countParam = 2
}
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
none()
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = 1 and buffer = true
or
this.hasGlobalName("sendto") and i = 4 and buffer = false
or
this.hasGlobalName("sendmsg") and i = 1 and buffer = true
}
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
input.isParameterDeref(1) and description = "Buffer sent by " + this.getName()
}
}

View File

@@ -4,14 +4,14 @@ import semmle.code.cpp.models.interfaces.Taint
* The `std::shared_ptr` and `std::unique_ptr` template classes.
*/
private class UniqueOrSharedPtr extends Class {
UniqueOrSharedPtr() { this.hasQualifiedName("std", ["shared_ptr", "unique_ptr"]) }
UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) }
}
/**
* The `std::make_shared` and `std::make_unique` template functions.
*/
private class MakeUniqueOrShared extends TaintFunction {
MakeUniqueOrShared() { this.hasQualifiedName("std", ["make_shared", "make_unique"]) }
MakeUniqueOrShared() { this.hasQualifiedName(["bsl", "std"], ["make_shared", "make_unique"]) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// Exclude the specializations of `std::make_shared` and `std::make_unique` that allocate arrays

View File

@@ -0,0 +1,72 @@
/**
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
* functions. See `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.Function
import semmle.code.cpp.commons.Scanf
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function `sscanf`, `fscanf` and its assorted variants
*/
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
override predicate hasArrayWithNullTerminator(int bufParam) {
bufParam = this.(ScanfFunction).getFormatParameterIndex()
or
not this instanceof Fscanf and
bufParam = this.(ScanfFunction).getInputParameterIndex()
}
override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) }
private int getLengthParameterIndex() { result = this.(Snscanf).getInputLengthParameterIndex() }
private int getLocaleParameterIndex() {
this.getName().matches("%\\_l") and
(
if exists(getLengthParameterIndex())
then result = getLengthParameterIndex() + 2
else result = 2
)
}
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
output.isParameterDeref(any(int i | i >= getArgsStartPosition()))
}
override predicate parameterNeverEscapes(int index) {
index = [0 .. max(getACallToThisFunction().getNumberOfArguments())]
}
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate parameterIsAlwaysReturned(int index) { none() }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i >= getArgsStartPosition() and
buffer = true and
mustWrite = true
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
buffer = true and
i =
[
this.(ScanfFunction).getInputParameterIndex(),
this.(ScanfFunction).getFormatParameterIndex(), getLocaleParameterIndex()
]
}
}

View File

@@ -5,6 +5,41 @@
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Iterator
/**
* The `std::array` template class.
*/
private class Array extends Class {
Array() { this.hasQualifiedName(["std", "bsl"], "array") }
}
/**
* The `std::deque` template class.
*/
private class Deque extends Class {
Deque() { this.hasQualifiedName(["std", "bsl"], "deque") }
}
/**
* The `std::forward_list` template class.
*/
private class ForwardList extends Class {
ForwardList() { this.hasQualifiedName(["std", "bsl"], "forward_list") }
}
/**
* The `std::list` template class.
*/
private class List extends Class {
List() { this.hasQualifiedName(["std", "bsl"], "list") }
}
/**
* The `std::vector` template class.
*/
private class Vector extends Class {
Vector() { this.hasQualifiedName(["std", "bsl"], "vector") }
}
/**
* Additional model for standard container constructors that reference the
* value type of the container (that is, the `T` in `std::vector<T>`). For
@@ -15,7 +50,10 @@ import semmle.code.cpp.models.interfaces.Iterator
*/
private class StdSequenceContainerConstructor extends Constructor, TaintFunction {
StdSequenceContainerConstructor() {
this.getDeclaringType().hasQualifiedName("std", ["vector", "deque", "list", "forward_list"])
this.getDeclaringType() instanceof Vector or
this.getDeclaringType() instanceof Deque or
this.getDeclaringType() instanceof List or
this.getDeclaringType() instanceof ForwardList
}
/**
@@ -50,7 +88,10 @@ private class StdSequenceContainerConstructor extends Constructor, TaintFunction
* The standard container function `data`.
*/
private class StdSequenceContainerData extends TaintFunction {
StdSequenceContainerData() { this.hasQualifiedName("std", ["array", "vector"], "data") }
StdSequenceContainerData() {
this.getClassAndName("data") instanceof Array or
this.getClassAndName("data") instanceof Vector
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from container itself (qualifier) to return value
@@ -69,10 +110,10 @@ private class StdSequenceContainerData extends TaintFunction {
*/
private class StdSequenceContainerPush extends TaintFunction {
StdSequenceContainerPush() {
this.hasQualifiedName("std", "vector", "push_back") or
this.hasQualifiedName("std", "deque", ["push_back", "push_front"]) or
this.hasQualifiedName("std", "list", ["push_back", "push_front"]) or
this.hasQualifiedName("std", "forward_list", "push_front")
this.getClassAndName("push_back") instanceof Vector or
this.getClassAndName(["push_back", "push_front"]) instanceof Deque or
this.getClassAndName("push_front") instanceof ForwardList or
this.getClassAndName(["push_back", "push_front"]) instanceof List
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -87,11 +128,11 @@ private class StdSequenceContainerPush extends TaintFunction {
*/
private class StdSequenceContainerFrontBack extends TaintFunction {
StdSequenceContainerFrontBack() {
this.hasQualifiedName("std", "array", ["front", "back"]) or
this.hasQualifiedName("std", "vector", ["front", "back"]) or
this.hasQualifiedName("std", "deque", ["front", "back"]) or
this.hasQualifiedName("std", "list", ["front", "back"]) or
this.hasQualifiedName("std", "forward_list", "front")
this.getClassAndName(["front", "back"]) instanceof Array or
this.getClassAndName(["front", "back"]) instanceof Deque or
this.getClassAndName("front") instanceof ForwardList or
this.getClassAndName(["front", "back"]) instanceof List or
this.getClassAndName(["front", "back"]) instanceof Vector
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -106,8 +147,10 @@ private class StdSequenceContainerFrontBack extends TaintFunction {
*/
private class StdSequenceContainerInsert extends TaintFunction {
StdSequenceContainerInsert() {
this.hasQualifiedName("std", ["vector", "deque", "list"], "insert") or
this.hasQualifiedName("std", ["forward_list"], "insert_after")
this.getClassAndName("insert") instanceof Deque or
this.getClassAndName("insert") instanceof List or
this.getClassAndName("insert") instanceof Vector or
this.getClassAndName("insert_after") instanceof ForwardList
}
/**
@@ -143,7 +186,10 @@ private class StdSequenceContainerInsert extends TaintFunction {
*/
private class StdSequenceContainerAssign extends TaintFunction {
StdSequenceContainerAssign() {
this.hasQualifiedName("std", ["vector", "deque", "list", "forward_list"], "assign")
this.getClassAndName("assign") instanceof Deque or
this.getClassAndName("assign") instanceof ForwardList or
this.getClassAndName("assign") instanceof List or
this.getClassAndName("assign") instanceof Vector
}
/**
@@ -170,30 +216,14 @@ private class StdSequenceContainerAssign extends TaintFunction {
}
}
/**
* The standard container `swap` functions.
*/
private class StdSequenceContainerSwap extends TaintFunction {
StdSequenceContainerSwap() {
this.hasQualifiedName("std", ["array", "vector", "deque", "list", "forward_list"], "swap")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// container1.swap(container2)
input.isQualifierObject() and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isQualifierObject()
}
}
/**
* The standard container functions `at` and `operator[]`.
*/
private class StdSequenceContainerAt extends TaintFunction {
StdSequenceContainerAt() {
this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"])
this.getClassAndName(["at", "operator[]"]) instanceof Array or
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
this.getClassAndName(["at", "operator[]"]) instanceof Vector
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -211,12 +241,12 @@ private class StdSequenceContainerAt extends TaintFunction {
* The standard vector `emplace` function.
*/
class StdVectorEmplace extends TaintFunction {
StdVectorEmplace() { this.hasQualifiedName("std", "vector", "emplace") }
StdVectorEmplace() { this.getClassAndName("emplace") instanceof Vector }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from any parameter except the position iterator to qualifier and return value
// (here we assume taint flow from any constructor parameter to the constructed object)
input.isParameter([1 .. getNumberOfParameters() - 1]) and
input.isParameterDeref([1 .. getNumberOfParameters() - 1]) and
(
output.isQualifierObject() or
output.isReturnValue()
@@ -228,12 +258,12 @@ class StdVectorEmplace extends TaintFunction {
* The standard vector `emplace_back` function.
*/
class StdVectorEmplaceBack extends TaintFunction {
StdVectorEmplaceBack() { this.hasQualifiedName("std", "vector", "emplace_back") }
StdVectorEmplaceBack() { this.getClassAndName("emplace_back") instanceof Vector }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from any parameter to qualifier
// (here we assume taint flow from any constructor parameter to the constructed object)
input.isParameter([0 .. getNumberOfParameters() - 1]) and
input.isParameterDeref([0 .. getNumberOfParameters() - 1]) and
output.isQualifierObject()
}
}

View File

@@ -5,14 +5,18 @@
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Iterator
/**
* The `std::map` and `std::unordered_map` template classes.
*/
private class MapOrUnorderedMap extends Class {
MapOrUnorderedMap() { this.hasQualifiedName(["std", "bsl"], ["map", "unordered_map"]) }
}
/**
* Additional model for map constructors using iterator inputs.
*/
private class StdMapConstructor extends Constructor, TaintFunction {
StdMapConstructor() {
this.hasQualifiedName("std", "map", "map") or
this.hasQualifiedName("std", "unordered_map", "unordered_map")
}
StdMapConstructor() { this.getDeclaringType() instanceof MapOrUnorderedMap }
/**
* Gets the index of a parameter to this function that is an iterator.
@@ -37,7 +41,7 @@ private class StdMapConstructor extends Constructor, TaintFunction {
*/
private class StdMapInsert extends TaintFunction {
StdMapInsert() {
this.hasQualifiedName("std", ["map", "unordered_map"], ["insert", "insert_or_assign"])
this.getClassAndName(["insert", "insert_or_assign"]) instanceof MapOrUnorderedMap
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -55,9 +59,7 @@ private class StdMapInsert extends TaintFunction {
* The standard map `emplace` and `emplace_hint` functions.
*/
private class StdMapEmplace extends TaintFunction {
StdMapEmplace() {
this.hasQualifiedName("std", ["map", "unordered_map"], ["emplace", "emplace_hint"])
}
StdMapEmplace() { this.getClassAndName(["emplace", "emplace_hint"]) instanceof MapOrUnorderedMap }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from the last parameter (which may be the value part used to
@@ -79,7 +81,7 @@ private class StdMapEmplace extends TaintFunction {
* The standard map `try_emplace` function.
*/
private class StdMapTryEmplace extends TaintFunction {
StdMapTryEmplace() { this.hasQualifiedName("std", ["map", "unordered_map"], "try_emplace") }
StdMapTryEmplace() { this.getClassAndName("try_emplace") instanceof MapOrUnorderedMap }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from any parameter apart from the key to qualifier and return value
@@ -102,27 +104,11 @@ private class StdMapTryEmplace extends TaintFunction {
}
}
/**
* The standard map `swap` function.
*/
private class StdMapSwap extends TaintFunction {
StdMapSwap() { this.hasQualifiedName("std", ["map", "unordered_map"], "swap") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// container1.swap(container2)
input.isQualifierObject() and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isQualifierObject()
}
}
/**
* The standard map `merge` function.
*/
private class StdMapMerge extends TaintFunction {
StdMapMerge() { this.hasQualifiedName("std", ["map", "unordered_map"], "merge") }
StdMapMerge() { this.getClassAndName("merge") instanceof MapOrUnorderedMap }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// container1.merge(container2)
@@ -135,7 +121,7 @@ private class StdMapMerge extends TaintFunction {
* The standard map functions `at` and `operator[]`.
*/
private class StdMapAt extends TaintFunction {
StdMapAt() { this.hasQualifiedName("std", ["map", "unordered_map"], ["at", "operator[]"]) }
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to referenced return value
@@ -152,7 +138,7 @@ private class StdMapAt extends TaintFunction {
* The standard map `find` function.
*/
private class StdMapFind extends TaintFunction {
StdMapFind() { this.hasQualifiedName("std", ["map", "unordered_map"], "find") }
StdMapFind() { this.getClassAndName("find") instanceof MapOrUnorderedMap }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
@@ -164,7 +150,7 @@ private class StdMapFind extends TaintFunction {
* The standard map `erase` function.
*/
private class StdMapErase extends TaintFunction {
StdMapErase() { this.hasQualifiedName("std", ["map", "unordered_map"], "erase") }
StdMapErase() { this.getClassAndName("erase") instanceof MapOrUnorderedMap }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to iterator return value
@@ -179,8 +165,7 @@ private class StdMapErase extends TaintFunction {
*/
private class StdMapEqualRange extends TaintFunction {
StdMapEqualRange() {
this.hasQualifiedName("std", ["map", "unordered_map"],
["lower_bound", "upper_bound", "equal_range"])
this.getClassAndName(["lower_bound", "upper_bound", "equal_range"]) instanceof MapOrUnorderedMap
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {

View File

@@ -7,10 +7,16 @@ import semmle.code.cpp.models.interfaces.Taint
/**
* An instantiation of `std::pair<T1, T2>`.
*/
class StdPairClass extends ClassTemplateInstantiation {
StdPairClass() { getTemplate().hasQualifiedName("std", "pair") }
private class StdPair extends ClassTemplateInstantiation {
StdPair() { this.hasQualifiedName(["std", "bsl"], "pair") }
}
/**
* DEPRECATED: This is now called `StdPair` and is a private part of the
* library implementation.
*/
deprecated class StdPairClass = StdPair;
/**
* Any of the single-parameter constructors of `std::pair` that takes a reference to an
* instantiation of `std::pair`. These constructors allow conversion between pair types when the
@@ -18,9 +24,9 @@ class StdPairClass extends ClassTemplateInstantiation {
*/
class StdPairCopyishConstructor extends Constructor, TaintFunction {
StdPairCopyishConstructor() {
this.getDeclaringType() instanceof StdPairClass and
this.getDeclaringType() instanceof StdPair and
this.getNumberOfParameters() = 1 and
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdPairClass
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdPair
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -38,7 +44,7 @@ class StdPairCopyishConstructor extends Constructor, TaintFunction {
* Additional model for `std::pair` constructors.
*/
private class StdPairConstructor extends Constructor, TaintFunction {
StdPairConstructor() { this.hasQualifiedName("std", "pair", "pair") }
StdPairConstructor() { this.getDeclaringType() instanceof StdPair }
/**
* Gets the index of a parameter to this function that is a reference to
@@ -60,19 +66,3 @@ private class StdPairConstructor extends Constructor, TaintFunction {
)
}
}
/**
* The standard pair `swap` function.
*/
private class StdPairSwap extends TaintFunction {
StdPairSwap() { this.hasQualifiedName("std", "pair", "swap") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// container1.swap(container2)
input.isQualifierObject() and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isQualifierObject()
}
}

View File

@@ -5,14 +5,18 @@
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Iterator
/**
* An instantiation of `std::set` or `std::unordered_set`.
*/
private class StdSet extends ClassTemplateInstantiation {
StdSet() { this.hasQualifiedName(["std", "bsl"], ["set", "unordered_set"]) }
}
/**
* Additional model for set constructors using iterator inputs.
*/
private class StdSetConstructor extends Constructor, TaintFunction {
StdSetConstructor() {
this.hasQualifiedName("std", "set", "set") or
this.hasQualifiedName("std", "unordered_set", "unordered_set")
}
StdSetConstructor() { this.getDeclaringType() instanceof StdSet }
/**
* Gets the index of a parameter to this function that is an iterator.
@@ -36,7 +40,7 @@ private class StdSetConstructor extends Constructor, TaintFunction {
* The standard set `insert` and `insert_or_assign` functions.
*/
private class StdSetInsert extends TaintFunction {
StdSetInsert() { this.hasQualifiedName("std", ["set", "unordered_set"], "insert") }
StdSetInsert() { this.getClassAndName("insert") instanceof StdSet }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from last parameter to qualifier and return value
@@ -53,9 +57,7 @@ private class StdSetInsert extends TaintFunction {
* The standard set `emplace` and `emplace_hint` functions.
*/
private class StdSetEmplace extends TaintFunction {
StdSetEmplace() {
this.hasQualifiedName("std", ["set", "unordered_set"], ["emplace", "emplace_hint"])
}
StdSetEmplace() { this.getClassAndName(["emplace", "emplace_hint"]) instanceof StdSet }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from any parameter to qualifier and return value
@@ -72,27 +74,11 @@ private class StdSetEmplace extends TaintFunction {
}
}
/**
* The standard set `swap` functions.
*/
private class StdSetSwap extends TaintFunction {
StdSetSwap() { this.hasQualifiedName("std", ["set", "unordered_set"], "swap") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// container1.swap(container2)
input.isQualifierObject() and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isQualifierObject()
}
}
/**
* The standard set `merge` function.
*/
private class StdSetMerge extends TaintFunction {
StdSetMerge() { this.hasQualifiedName("std", ["set", "unordered_set"], "merge") }
StdSetMerge() { this.getClassAndName("merge") instanceof StdSet }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// container1.merge(container2)
@@ -105,7 +91,7 @@ private class StdSetMerge extends TaintFunction {
* The standard set `find` function.
*/
private class StdSetFind extends TaintFunction {
StdSetFind() { this.hasQualifiedName("std", ["set", "unordered_set"], "find") }
StdSetFind() { this.getClassAndName("find") instanceof StdSet }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
@@ -117,7 +103,7 @@ private class StdSetFind extends TaintFunction {
* The standard set `erase` function.
*/
private class StdSetErase extends TaintFunction {
StdSetErase() { this.hasQualifiedName("std", ["set", "unordered_set"], "erase") }
StdSetErase() { this.getClassAndName("erase") instanceof StdSet }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to iterator return value
@@ -132,8 +118,7 @@ private class StdSetErase extends TaintFunction {
*/
private class StdSetEqualRange extends TaintFunction {
StdSetEqualRange() {
this.hasQualifiedName("std", ["set", "unordered_set"],
["lower_bound", "upper_bound", "equal_range"])
this.getClassAndName(["lower_bound", "upper_bound", "equal_range"]) instanceof StdSet
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {

View File

@@ -9,10 +9,10 @@ import semmle.code.cpp.models.interfaces.Iterator
import semmle.code.cpp.models.interfaces.DataFlow
/**
* The `std::basic_string` template class.
* The `std::basic_string` template class instantiations.
*/
private class StdBasicString extends TemplateClass {
StdBasicString() { this.hasQualifiedName("std", "basic_string") }
private class StdBasicString extends ClassTemplateInstantiation {
StdBasicString() { this.hasQualifiedName(["std", "bsl"], "basic_string") }
}
/**
@@ -24,7 +24,7 @@ private class StdBasicString extends TemplateClass {
* ```
*/
private class StdStringConstructor extends Constructor, TaintFunction {
StdStringConstructor() { this.getDeclaringType().hasQualifiedName("std", "basic_string") }
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString }
/**
* Gets the index of a parameter to this function that is a string (or
@@ -69,7 +69,7 @@ private class StdStringConstructor extends Constructor, TaintFunction {
* The `std::string` function `c_str`.
*/
private class StdStringCStr extends TaintFunction {
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string itself (qualifier) to return value
@@ -82,7 +82,7 @@ private class StdStringCStr extends TaintFunction {
* The `std::string` function `data`.
*/
private class StdStringData extends TaintFunction {
StdStringData() { this.hasQualifiedName("std", "basic_string", "data") }
StdStringData() { this.getClassAndName("data") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string itself (qualifier) to return value
@@ -100,7 +100,7 @@ private class StdStringData extends TaintFunction {
* The `std::string` function `push_back`.
*/
private class StdStringPush extends TaintFunction {
StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") }
StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to qualifier
@@ -113,7 +113,7 @@ private class StdStringPush extends TaintFunction {
* The `std::string` functions `front` and `back`.
*/
private class StdStringFrontBack extends TaintFunction {
StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) }
StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from object to returned reference
@@ -123,12 +123,12 @@ private class StdStringFrontBack extends TaintFunction {
}
/**
* The `std::string` function `operator+`.
* The (non-member) `std::string` function `operator+`.
*/
private class StdStringPlus extends TaintFunction {
StdStringPlus() {
this.hasQualifiedName("std", "operator+") and
this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
this.hasQualifiedName(["std", "bsl"], "operator+") and
this.getUnspecifiedType() instanceof StdBasicString
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -148,7 +148,7 @@ private class StdStringPlus extends TaintFunction {
*/
private class StdStringAppend extends TaintFunction {
StdStringAppend() {
this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"])
this.getClassAndName(["operator+=", "append", "insert", "replace"]) instanceof StdBasicString
}
/**
@@ -190,7 +190,7 @@ private class StdStringAppend extends TaintFunction {
* The standard function `std::string.assign`.
*/
private class StdStringAssign extends TaintFunction {
StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") }
StdStringAssign() { this.getClassAndName("assign") instanceof StdBasicString }
/**
* Gets the index of a parameter to this function that is a string (or
@@ -230,7 +230,7 @@ private class StdStringAssign extends TaintFunction {
* The standard function `std::string.copy`.
*/
private class StdStringCopy extends TaintFunction {
StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") }
StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// copy(dest, num, pos)
@@ -243,7 +243,7 @@ private class StdStringCopy extends TaintFunction {
* The standard function `std::string.substr`.
*/
private class StdStringSubstr extends TaintFunction {
StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") }
StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// substr(pos, num)
@@ -252,30 +252,11 @@ private class StdStringSubstr extends TaintFunction {
}
}
/**
* The standard functions `std::string.swap` and `std::stringstream::swap`.
*/
private class StdStringSwap extends TaintFunction {
StdStringSwap() {
this.hasQualifiedName("std", "basic_string", "swap") or
this.hasQualifiedName("std", "basic_stringstream", "swap")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// str1.swap(str2)
input.isQualifierObject() and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isQualifierObject()
}
}
/**
* The `std::string` functions `at` and `operator[]`.
*/
private class StdStringAt extends TaintFunction {
StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to referenced return value
@@ -289,22 +270,25 @@ private class StdStringAt extends TaintFunction {
}
/**
* The `std::basic_istream` template class.
* The `std::basic_istream` template class instantiations.
*/
private class StdBasicIStream extends TemplateClass {
StdBasicIStream() { this.hasQualifiedName("std", "basic_istream") }
private class StdBasicIStream extends ClassTemplateInstantiation {
StdBasicIStream() { this.hasQualifiedName(["std", "bsl"], "basic_istream") }
}
/**
* The `std::istream` function `operator>>` (defined as a member function).
*/
private class StdIStreamIn extends DataFlowFunction, TaintFunction {
StdIStreamIn() { this.hasQualifiedName("std", "basic_istream", "operator>>") }
StdIStreamIn() { this.getClassAndName("operator>>") instanceof StdBasicIStream }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// returns reference to `*this`
input.isQualifierAddress() and
output.isReturnValue()
or
input.isQualifierObject() and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -323,15 +307,17 @@ private class StdIStreamIn extends DataFlowFunction, TaintFunction {
*/
private class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
StdIStreamInNonMember() {
this.hasQualifiedName("std", "operator>>") and
this.getUnspecifiedType().(ReferenceType).getBaseType() =
any(StdBasicIStream s).getAnInstantiation()
this.hasQualifiedName(["std", "bsl"], "operator>>") and
this.getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdBasicIStream
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// flow from first parameter to return value
input.isParameter(0) and
output.isReturnValue()
or
input.isParameterDeref(0) and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -350,7 +336,7 @@ private class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
*/
private class StdIStreamGet extends TaintFunction {
StdIStreamGet() {
this.hasQualifiedName("std", "basic_istream", ["get", "peek"]) and
this.getClassAndName(["get", "peek"]) instanceof StdBasicIStream and
this.getNumberOfParameters() = 0
}
@@ -366,7 +352,7 @@ private class StdIStreamGet extends TaintFunction {
*/
private class StdIStreamRead extends DataFlowFunction, TaintFunction {
StdIStreamRead() {
this.hasQualifiedName("std", "basic_istream", ["get", "read"]) and
this.getClassAndName(["get", "read"]) instanceof StdBasicIStream and
this.getNumberOfParameters() > 0
}
@@ -374,6 +360,9 @@ private class StdIStreamRead extends DataFlowFunction, TaintFunction {
// returns reference to `*this`
input.isQualifierAddress() and
output.isReturnValue()
or
input.isQualifierObject() and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -391,7 +380,7 @@ private class StdIStreamRead extends DataFlowFunction, TaintFunction {
* The `std::istream` function `readsome`.
*/
private class StdIStreamReadSome extends TaintFunction {
StdIStreamReadSome() { this.hasQualifiedName("std", "basic_istream", "readsome") }
StdIStreamReadSome() { this.getClassAndName("readsome") instanceof StdBasicIStream }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to first parameter
@@ -404,12 +393,15 @@ private class StdIStreamReadSome extends TaintFunction {
* The `std::istream` function `putback`.
*/
private class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
StdIStreamPutBack() { this.hasQualifiedName("std", "basic_istream", "putback") }
StdIStreamPutBack() { this.getClassAndName("putback") instanceof StdBasicIStream }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// returns reference to `*this`
input.isQualifierAddress() and
output.isReturnValue()
or
input.isQualifierObject() and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -437,12 +429,15 @@ private class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
* The `std::istream` function `getline`.
*/
private class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
StdIStreamGetLine() { this.hasQualifiedName("std", "basic_istream", "getline") }
StdIStreamGetLine() { this.getClassAndName("getline") instanceof StdBasicIStream }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// returns reference to `*this`
input.isQualifierAddress() and
output.isReturnValue()
or
input.isQualifierObject() and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -460,12 +455,15 @@ private class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
* The (non-member) function `std::getline`.
*/
private class StdGetLine extends DataFlowFunction, TaintFunction {
StdGetLine() { this.hasQualifiedName("std", "getline") }
StdGetLine() { this.hasQualifiedName(["std", "bsl"], "getline") }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// flow from first parameter to return value
input.isParameter(0) and
output.isReturnValue()
or
input.isParameterDeref(0) and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -480,10 +478,10 @@ private class StdGetLine extends DataFlowFunction, TaintFunction {
}
/**
* The `std::basic_ostream` template class.
* The `std::basic_ostream` template class instantiations.
*/
private class StdBasicOStream extends TemplateClass {
StdBasicOStream() { this.hasQualifiedName("std", "basic_ostream") }
private class StdBasicOStream extends ClassTemplateInstantiation {
StdBasicOStream() { this.hasQualifiedName(["std", "bsl"], "basic_ostream") }
}
/**
@@ -491,12 +489,17 @@ private class StdBasicOStream extends TemplateClass {
* `put` and `write`.
*/
private class StdOStreamOut extends DataFlowFunction, TaintFunction {
StdOStreamOut() { this.hasQualifiedName("std", "basic_ostream", ["operator<<", "put", "write"]) }
StdOStreamOut() {
this.getClassAndName(["operator<<", "put", "write"]) instanceof StdBasicOStream
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// returns reference to `*this`
input.isQualifierAddress() and
output.isReturnValue()
or
input.isQualifierObject() and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -525,15 +528,17 @@ private class StdOStreamOut extends DataFlowFunction, TaintFunction {
*/
private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
StdOStreamOutNonMember() {
this.hasQualifiedName("std", "operator<<") and
this.getUnspecifiedType().(ReferenceType).getBaseType() =
any(StdBasicOStream s).getAnInstantiation()
this.hasQualifiedName(["std", "bsl"], "operator<<") and
this.getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdBasicOStream
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// flow from first parameter to return value
input.isParameter(0) and
output.isReturnValue()
or
input.isParameterDeref(0) and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -551,14 +556,19 @@ private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
}
}
/**
* The `std::basic_stringstream` template class instantiations.
*/
private class StdBasicStringStream extends ClassTemplateInstantiation {
StdBasicStringStream() { this.hasQualifiedName(["std", "bsl"], "basic_stringstream") }
}
/**
* Additional model for `std::stringstream` constructors that take a string
* input parameter.
*/
private class StdStringStreamConstructor extends Constructor, TaintFunction {
StdStringStreamConstructor() {
this.getDeclaringType().hasQualifiedName("std", "basic_stringstream")
}
StdStringStreamConstructor() { this.getDeclaringType() instanceof StdBasicStringStream }
/**
* Gets the index of a parameter to this function that is a string.
@@ -582,7 +592,7 @@ private class StdStringStreamConstructor extends Constructor, TaintFunction {
* The `std::stringstream` function `str`.
*/
private class StdStringStreamStr extends TaintFunction {
StdStringStreamStr() { this.hasQualifiedName("std", "basic_stringstream", "str") }
StdStringStreamStr() { this.getClassAndName("str") instanceof StdBasicStringStream }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to return value (if any)
@@ -595,21 +605,33 @@ private class StdStringStreamStr extends TaintFunction {
}
}
/**
* The `std::basic_ios` template class instantiations.
*/
private class StdBasicIOS extends ClassTemplateInstantiation {
StdBasicIOS() { this.hasQualifiedName(["std", "bsl"], "basic_ios") }
}
/**
* A `std::` stream function that does not require a model, except that it
* returns a reference to `*this` and thus could be used in a chain.
*/
private class StdStreamFunction extends DataFlowFunction, TaintFunction {
StdStreamFunction() {
this.hasQualifiedName("std", "basic_istream", ["ignore", "unget", "seekg"]) or
this.hasQualifiedName("std", "basic_ostream", ["seekp", "flush"]) or
this.hasQualifiedName("std", "basic_ios", "copyfmt")
this.getClassAndName(["ignore", "unget", "seekg"]) instanceof StdBasicIStream
or
this.getClassAndName(["seekp", "flush"]) instanceof StdBasicOStream
or
this.getClassAndName("copyfmt") instanceof StdBasicIOS
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// returns reference to `*this`
input.isQualifierAddress() and
output.isReturnValue()
or
input.isQualifierObject() and
output.isReturnValueDeref()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {

View File

@@ -13,15 +13,20 @@ import semmle.code.cpp.models.interfaces.SideEffect
*/
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction {
StrcatFunction() {
exists(string name | name = getName() |
name = "strcat" or // strcat(dst, src)
name = "strncat" or // strncat(dst, src, max_amount)
name = "wcscat" or // wcscat(dst, src)
name = "_mbscat" or // _mbscat(dst, src)
name = "wcsncat" or // wcsncat(dst, src, max_amount)
name = "_mbsncat" or // _mbsncat(dst, src, max_amount)
name = "_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
)
this.hasGlobalOrStdOrBslName([
"strcat", // strcat(dst, src)
"strncat", // strncat(dst, src, max_amount)
"wcscat", // wcscat(dst, src)
"wcsncat" // wcsncat(dst, src, max_amount)
])
or
this.hasGlobalName([
"_mbscat", // _mbscat(dst, src)
"_mbsncat", // _mbsncat(dst, src, max_amount)
"_mbsncat_l", // _mbsncat_l(dst, src, max_amount, locale)
"_mbsnbcat", // _mbsnbcat(dest, src, count)
"_mbsnbcat_l" // _mbsnbcat_l(dest, src, count, locale)
])
}
/**
@@ -45,20 +50,13 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(string name | name = getName() |
(
name = "strncat" or
name = "wcsncat" or
name = "_mbsncat" or
name = "_mbsncat_l"
) and
input.isParameter(2) and
output.isParameterDeref(0)
or
name = "_mbsncat_l" and
input.isParameter(3) and
output.isParameterDeref(0)
)
getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and
input.isParameter(2) and
output.isParameterDeref(0)
or
getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
input.isParameter(3) and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isParameterDeref(0)

View File

@@ -13,25 +13,36 @@ import semmle.code.cpp.models.interfaces.SideEffect
*/
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction {
StrcpyFunction() {
getName() =
[
this.hasGlobalOrStdOrBslName([
"strcpy", // strcpy(dst, src)
"wcscpy", // wcscpy(dst, src)
"_mbscpy", // _mbscpy(dst, src)
"strncpy", // strncpy(dst, src, max_amount)
"_strncpy_l", // _strncpy_l(dst, src, max_amount, locale)
"wcsncpy", // wcsncpy(dst, src, max_amount)
"strxfrm", // strxfrm(dest, src, max_amount)
"wcsxfrm" // wcsxfrm(dest, src, max_amount)
])
or
this.hasGlobalName([
"_mbscpy", // _mbscpy(dst, src)
"_strncpy_l", // _strncpy_l(dst, src, max_amount, locale)
"_wcsncpy_l", // _wcsncpy_l(dst, src, max_amount, locale)
"_mbsncpy", // _mbsncpy(dst, src, max_amount)
"_mbsncpy_l"
] // _mbsncpy_l(dst, src, max_amount, locale)
"_mbsncpy_l", // _mbsncpy_l(dst, src, max_amount, locale)
"_strxfrm_l", // _strxfrm_l(dest, src, max_amount, locale)
"wcsxfrm_l", // _strxfrm_l(dest, src, max_amount, locale)
"_mbsnbcpy", // _mbsnbcpy(dest, src, max_amount)
"stpcpy", // stpcpy(dest, src)
"stpncpy" // stpcpy(dest, src, max_amount)
])
or
getName() =
[
"strcpy_s", // strcpy_s(dst, max_amount, src)
"wcscpy_s", // wcscpy_s(dst, max_amount, src)
"_mbscpy_s"
] and // _mbscpy_s(dst, max_amount, src)
(
this.hasGlobalOrStdName([
"strcpy_s", // strcpy_s(dst, max_amount, src)
"wcscpy_s" // wcscpy_s(dst, max_amount, src)
])
or
this.hasGlobalName("_mbscpy_s") // _mbscpy_s(dst, max_amount, src)
) and
// exclude the 2-parameter template versions
// that find the size of a fixed size destination buffer.
getNumberOfParameters() = 3
@@ -40,9 +51,7 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
/**
* Holds if this is one of the `strcpy_s` variants.
*/
private predicate isSVariant() {
exists(string name | name = getName() | name.suffix(name.length() - 2) = "_s")
}
private predicate isSVariant() { getName().matches("%\\_s") }
/**
* Gets the index of the parameter that is the maximum size of the copy (in characters).
@@ -50,10 +59,10 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
int getParamSize() {
if isSVariant()
then result = 1
else
if exists(getName().indexOf("ncpy"))
then result = 2
else none()
else (
getName().matches(["%ncpy%", "%nbcpy%", "%xfrm%"]) and
result = 2
)
}
/**

View File

@@ -0,0 +1,49 @@
/**
* Provides implementation classes modeling `_strinc`, `_strdec` and their variants.
* See `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The function `_strinc`, `_strdec` and their variants.
*/
private class Strcrement extends ArrayFunction, TaintFunction, SideEffectFunction {
Strcrement() {
this.hasGlobalName([
"_strinc", // _strinc(source, locale)
"_wcsinc", // _strinc(source, locale)
"_mbsinc", // _strinc(source)
"_mbsinc_l", // _strinc(source, locale)
"_strdec", // _strdec(start, source)
"_wcsdec", // _wcsdec(start, source)
"_mbsdec", // _mbsdec(start, source)
"_mbsdec_l" // _mbsdec_l(start, source, locale)
])
}
override predicate hasArrayWithNullTerminator(int bufParam) {
// Match all parameters that are not locales.
this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType
}
override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(int index | hasArrayInput(index) |
input.isParameter(index) and output.isReturnValue()
or
input.isParameterDeref(index) and output.isReturnValueDeref()
)
}
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
hasArrayInput(i) and buffer = true
}
}

View File

@@ -13,25 +13,14 @@ import semmle.code.cpp.models.interfaces.Taint
*/
private class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction {
StrdupFunction() {
exists(string name |
hasGlobalName(name) and
(
// strdup(str)
name = "strdup"
or
// wcsdup(str)
name = "wcsdup"
or
// _strdup(str)
name = "_strdup"
or
// _wcsdup(str)
name = "_wcsdup"
or
// _mbsdup(str)
name = "_mbsdup"
)
)
hasGlobalName([
// --- C library allocation
"strdup", // strdup(str)
"wcsdup", // wcsdup(str)
"_strdup", // _strdup(str)
"_wcsdup", // _wcsdup(str)
"_mbsdup" // _mbsdup(str)
])
}
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
@@ -51,8 +40,8 @@ private class StrndupFunction extends AllocationFunction, ArrayFunction, DataFlo
StrndupFunction() {
exists(string name |
hasGlobalName(name) and
// strndup(str, maxlen)
name = "strndup"
// --- C library allocation
name = "strndup" // strndup(str, maxlen)
)
}

View File

@@ -0,0 +1,38 @@
/**
* Provides implementation classes modeling `strnextc` and various similar functions.
* See `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The function `strnextc` and its variants.
*/
private class Strnextc extends TaintFunction, ArrayFunction, AliasFunction, SideEffectFunction {
Strnextc() { this.hasGlobalName(["_strnextc", "_wcsnextc", "_mbsnextc", "_mbsnextc_l"]) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(0) and output.isReturnValue()
}
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
override predicate parameterNeverEscapes(int index) { index = 0 }
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
override predicate parameterIsAlwaysReturned(int index) { none() }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = 0 and buffer = true
}
}

View File

@@ -0,0 +1,62 @@
/**
* Provides implementation classes modeling `strset` and various similar
* functions. See `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.Function
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function `strset` and its assorted variants
*/
private class StrsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
SideEffectFunction {
StrsetFunction() {
hasGlobalName([
"strset", "_strset", "_strset_l", "_wcsset", "_wcsset_l", "_mbsset", "_mbsset_l",
"_mbsnbset", "_mbsnbset_l", "_strnset", "_strnset_l", "_wcsnset", "_wcsnset_l", "_mbsnset",
"_mbsnset_l"
])
}
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// flow from the character that overrides the string
input.isParameter(1) and
(
output.isReturnValueDeref()
or
output.isParameterDeref(0)
)
or
// flow from the input string to the output string
input.isParameter(0) and
output.isReturnValue()
}
override predicate parameterNeverEscapes(int index) { none() }
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 0 and buffer = true and mustWrite = true
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = 0 and buffer = true
}
}

Some files were not shown because too many files have changed in this diff Show More