mirror of
https://github.com/github/codeql.git
synced 2026-05-13 18:59:27 +02:00
Merge branch 'main' into rdmarsh2/cpp/operand-reuse
This commit is contained in:
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(_, _)
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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."
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 + "%")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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."
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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."
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
// ..
|
||||
}
|
||||
@@ -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 &)</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>
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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."
|
||||
@@ -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.
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -50,5 +50,5 @@ class CStyleComment extends Comment {
|
||||
* ```
|
||||
*/
|
||||
class CppStyleComment extends Comment {
|
||||
CppStyleComment() { this.getContents().prefix(2) = "//" }
|
||||
CppStyleComment() { this.getContents().matches("//%") }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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() + "\")" }
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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&` to `const C&`.
|
||||
* `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
|
||||
* ```
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(), ", ")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
88
cpp/ql/src/semmle/code/cpp/models/implementations/Recv.qll
Normal file
88
cpp/ql/src/semmle/code/cpp/models/implementations/Recv.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
63
cpp/ql/src/semmle/code/cpp/models/implementations/Send.qll
Normal file
63
cpp/ql/src/semmle/code/cpp/models/implementations/Send.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
72
cpp/ql/src/semmle/code/cpp/models/implementations/Sscanf.qll
Normal file
72
cpp/ql/src/semmle/code/cpp/models/implementations/Sscanf.qll
Normal 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()
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
62
cpp/ql/src/semmle/code/cpp/models/implementations/Strset.qll
Normal file
62
cpp/ql/src/semmle/code/cpp/models/implementations/Strset.qll
Normal 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
Reference in New Issue
Block a user