Merge remote-tracking branch 'upstream/master' into SimpleRangeAnalysis-use-after-cast

This commit is contained in:
Jonas Jensen
2019-04-01 09:10:57 +02:00
87 changed files with 1770 additions and 821 deletions

View File

@@ -12,6 +12,7 @@
* readability
*/
import cpp
private import semmle.code.cpp.commons.Exclusions
private import semmle.code.cpp.rangeanalysis.PointlessComparison
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import UnsignedGEZero
@@ -31,6 +32,7 @@ from
where
not cmp.isInMacroExpansion() and
not cmp.isFromTemplateInstantiation(_) and
not functionContainsDisabledCode(cmp.getEnclosingFunction()) and
reachablePointlessComparison(cmp, left, right, value, ss) and
// a comparison between an enum and zero is always valid because whether

View File

@@ -11,6 +11,7 @@
* external/cwe/cwe-561
*/
import cpp
private import semmle.code.cpp.commons.Exclusions
class PureExprInVoidContext extends ExprInVoidContext {
PureExprInVoidContext() { this.isPure() }
@@ -23,71 +24,29 @@ predicate accessInInitOfForStmt(Expr e) {
s.getExpr() = e)
}
/**
* Holds if the preprocessor branch `pbd` is on line `pbdStartLine` in file `file`.
*/
predicate pbdLocation(PreprocessorBranchDirective pbd, string file, int pbdStartLine) {
pbd.getLocation().hasLocationInfo(file, pbdStartLine, _, _, _)
}
/**
* Holds if the body of the function `f` is on lines `fBlockStartLine` to `fBlockEndLine` in file `file`.
*/
predicate functionLocation(Function f, string file, int fBlockStartLine, int fBlockEndLine) {
f.getBlock().getLocation().hasLocationInfo(file, fBlockStartLine, _, fBlockEndLine, _)
}
/**
* Holds if the function `f`, or a function called by it, contains
* code excluded by the preprocessor.
*/
predicate containsDisabledCode(Function f) {
// `f` contains a preprocessor branch that was not taken
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine |
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
pbdLocation(pbd, file, pbdStartLine) and
pbdStartLine <= fBlockEndLine and
pbdStartLine >= fBlockStartLine and
(
pbd.(PreprocessorBranch).wasNotTaken() or
// an else either was not taken, or it's corresponding branch
// was not taken.
pbd instanceof PreprocessorElse
)
) or
predicate functionContainsDisabledCodeRecursive(Function f) {
functionContainsDisabledCode(f) or
// recurse into function calls
exists(FunctionCall fc |
fc.getEnclosingFunction() = f and
containsDisabledCode(fc.getTarget())
functionContainsDisabledCodeRecursive(fc.getTarget())
)
}
/**
* Holds if the function `f`, or a function called by it, is inside a
* preprocessor branch that may have code in another arm
*/
predicate definedInIfDef(Function f) {
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int pbdEndLine, int fBlockStartLine, int fBlockEndLine |
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
pbdLocation(pbd, file, pbdStartLine) and
pbdLocation(pbd.getNext(), file, pbdEndLine) and
pbdStartLine <= fBlockStartLine and
pbdEndLine >= fBlockEndLine and
// pbd is a preprocessor branch where multiple branches exist
(
pbd.getNext() instanceof PreprocessorElse or
pbd instanceof PreprocessorElse or
pbd.getNext() instanceof PreprocessorElif or
pbd instanceof PreprocessorElif
)
) or
predicate functionDefinedInIfDefRecursive(Function f) {
functionDefinedInIfDef(f) or
// recurse into function calls
exists(FunctionCall fc |
fc.getEnclosingFunction() = f and
definedInIfDef(fc.getTarget())
functionDefinedInIfDefRecursive(fc.getTarget())
)
}
@@ -121,8 +80,8 @@ where // EQExprs are covered by CompareWhereAssignMeant.ql
not parent instanceof PureExprInVoidContext and
not peivc.getEnclosingFunction().isCompilerGenerated() and
not peivc.getType() instanceof UnknownType and
not containsDisabledCode(peivc.(FunctionCall).getTarget()) and
not definedInIfDef(peivc.(FunctionCall).getTarget()) and
not functionContainsDisabledCodeRecursive(peivc.(FunctionCall).getTarget()) and
not functionDefinedInIfDefRecursive(peivc.(FunctionCall).getTarget()) and
if peivc instanceof FunctionCall then
exists(Function target |
target = peivc.(FunctionCall).getTarget() and

View File

@@ -14,15 +14,20 @@
import cpp
import semmle.code.cpp.security.TaintTracking
from Expr source, Expr tainted, BinaryArithmeticOperation oper,
SizeofOperator sizeof, string taintCause
where tainted(source, tainted)
and oper.getAnOperand() = tainted
and oper.getOperator() = "*"
and oper.getAnOperand() = sizeof
and oper != tainted
and sizeof.getValue().toInt() > 1
and isUserInput(source, taintCause)
select
oper, "This allocation size is derived from $@ and might overflow",
source, "user input (" + taintCause + ")"
predicate taintedAllocSize(Expr e, Expr source, string taintCause) {
(
isAllocationExpr(e) or
any(MulExpr me | me.getAChild() instanceof SizeofOperator) = e
) and
exists(Expr tainted |
tainted = e.getAChild() and
tainted.getType().getUnspecifiedType() instanceof IntegralType and
isUserInput(source, taintCause) and
tainted(source, tainted)
)
}
from Expr e, Expr source, string taintCause
where taintedAllocSize(e, source, taintCause)
select e, "This allocation size is derived from $@ and might overflow", source,
"user input (" + taintCause + ")"

View File

@@ -68,6 +68,9 @@ some are after the final <code>#endif</code>. All three of these things must be
<li>
<a href="http://www.cplusplus.com/forum/articles/10627/">Headers and Includes: Why and How</a>
</li>
<li>
<a href="https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html">The Multiple-Include Optimization</a>
</li>
</references>

View File

@@ -302,7 +302,13 @@ class File extends Container, @file {
predicate compiledAsMicrosoft() {
exists(Compilation c |
c.getAFileCompiled() = this and
c.getAnArgument() = "--microsoft"
(
c.getAnArgument() = "--microsoft" or
c.getAnArgument().toLowerCase().replaceAll("\\", "/").matches("%/cl.exe")
)
) or exists(File parent |
parent.compiledAsMicrosoft() and
parent.getAnIncludedFile() = this
)
}

View File

@@ -39,7 +39,16 @@ predicate allocationFunction(Function f)
name = "MmAllocateNodePagesForMdlEx" or
name = "MmMapLockedPagesWithReservedMapping" or
name = "MmMapLockedPages" or
name = "MmMapLockedPagesSpecifyCache"
name = "MmMapLockedPagesSpecifyCache" or
name = "LocalAlloc" or
name = "LocalReAlloc" or
name = "GlobalAlloc" or
name = "GlobalReAlloc" or
name = "HeapAlloc" or
name = "HeapReAlloc" or
name = "VirtualAlloc" or
name = "CoTaskMemAlloc" or
name = "CoTaskMemRealloc"
)
)
}
@@ -81,7 +90,17 @@ predicate freeFunction(Function f, int argNum)
(name = "MmFreeMappingAddress" and argNum = 0) or
(name = "MmFreePagesFromMdl" and argNum = 0) or
(name = "MmUnmapReservedMapping" and argNum = 0) or
(name = "MmUnmapLockedPages" and argNum = 0)
(name = "MmUnmapLockedPages" and argNum = 0) or
(name = "LocalFree" and argNum = 0) or
(name = "GlobalFree" and argNum = 0) or
(name = "HeapFree" and argNum = 2) or
(name = "VirtualFree" and argNum = 0) or
(name = "CoTaskMemFree" and argNum = 0) or
(name = "SysFreeString" and argNum = 0) or
(name = "LocalReAlloc" and argNum = 0) or
(name = "GlobalReAlloc" and argNum = 0) or
(name = "HeapReAlloc" and argNum = 2) or
(name = "CoTaskMemRealloc" and argNum = 0)
)
)
}

View File

@@ -0,0 +1,60 @@
/**
* Common predicates used to exclude results from a query based on heuristics.
*/
import cpp
/**
* Holds if the preprocessor branch `pbd` is on line `pbdStartLine` in file `file`.
*/
private predicate pbdLocation(PreprocessorBranchDirective pbd, string file, int pbdStartLine) {
pbd.getLocation().hasLocationInfo(file, pbdStartLine, _, _, _)
}
/**
* Holds if the body of the function `f` is on lines `fBlockStartLine` to `fBlockEndLine` in file `file`.
*/
private predicate functionLocation(Function f, string file, int fBlockStartLine, int fBlockEndLine) {
f.getBlock().getLocation().hasLocationInfo(file, fBlockStartLine, _, fBlockEndLine, _)
}
/**
* Holds if the function `f` is inside a preprocessor branch that may have code in another arm.
*/
predicate functionDefinedInIfDef(Function f) {
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int pbdEndLine, int fBlockStartLine,
int fBlockEndLine |
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
pbdLocation(pbd, file, pbdStartLine) and
pbdLocation(pbd.getNext(), file, pbdEndLine) and
pbdStartLine <= fBlockStartLine and
pbdEndLine >= fBlockEndLine and
// pbd is a preprocessor branch where multiple branches exist
(
pbd.getNext() instanceof PreprocessorElse or
pbd instanceof PreprocessorElse or
pbd.getNext() instanceof PreprocessorElif or
pbd instanceof PreprocessorElif
)
)
}
/**
* Holds if the function `f` contains code excluded by the preprocessor.
*/
predicate functionContainsDisabledCode(Function f) {
// `f` contains a preprocessor branch that was not taken
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine |
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
pbdLocation(pbd, file, pbdStartLine) and
pbdStartLine <= fBlockEndLine and
pbdStartLine >= fBlockStartLine and
(
pbd.(PreprocessorBranch).wasNotTaken() or
// an else either was not taken, or it's corresponding branch
// was not taken.
pbd instanceof PreprocessorElse
)
)
}

View File

@@ -32,19 +32,19 @@ class AttributeFormattingFunction extends FormattingFunction {
* A standard function such as `vprintf` that has a format parameter
* and a variable argument list of type `va_arg`.
*/
predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex, boolean wide) {
predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex) {
f.getName().regexpMatch("_?_?va?[fs]?n?w?printf(_s)?(_p)?(_l)?")
and (
if f.getName().matches("%\\_l")
then formatParamIndex = f.getNumberOfParameters() - 3
else formatParamIndex = f.getNumberOfParameters() - 2
) and if f.getName().matches("%w%") then wide = true else wide = false
)
}
private
predicate callsVariadicFormatter(Function f, int formatParamIndex, boolean wide) {
predicate callsVariadicFormatter(Function f, int formatParamIndex) {
exists(FunctionCall fc, int i |
variadicFormatter(fc.getTarget(), i, wide)
variadicFormatter(fc.getTarget(), i)
and fc.getEnclosingFunction() = f
and fc.getArgument(i) = f.getParameter(formatParamIndex).getAnAccess()
)
@@ -54,11 +54,11 @@ predicate callsVariadicFormatter(Function f, int formatParamIndex, boolean wide)
* Holds if `f` is a function such as `vprintf` that has a format parameter
* (at `formatParamIndex`) and a variable argument list of type `va_arg`.
*/
predicate variadicFormatter(Function f, int formatParamIndex, boolean wide) {
primitiveVariadicFormatter(f, formatParamIndex, wide)
predicate variadicFormatter(Function f, int formatParamIndex) {
primitiveVariadicFormatter(f, formatParamIndex)
or (
not f.isVarargs()
and callsVariadicFormatter(f, formatParamIndex, wide)
and callsVariadicFormatter(f, formatParamIndex)
)
}
@@ -68,12 +68,10 @@ predicate variadicFormatter(Function f, int formatParamIndex, boolean wide) {
*/
class UserDefinedFormattingFunction extends FormattingFunction {
UserDefinedFormattingFunction() {
isVarargs() and callsVariadicFormatter(this, _, _)
isVarargs() and callsVariadicFormatter(this, _)
}
override int getFormatParameterIndex() { callsVariadicFormatter(this, result, _) }
override predicate isWideCharDefault() { callsVariadicFormatter(this, _, true) }
override int getFormatParameterIndex() { callsVariadicFormatter(this, result) }
}
/**
@@ -674,8 +672,8 @@ class FormatLiteral extends Literal {
/**
* Gets the char type required by the nth conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
* - the `%S` format character reverses wideness.
* (e.g. `char` for `printf`, `char` or `wchar_t` for `wprintf`).
* - the `%C` format character reverses wideness.
* - the size prefixes 'l'/'w' and 'h' override the type character
* to wide or single-byte characters respectively.
*/
@@ -721,8 +719,8 @@ class FormatLiteral extends Literal {
/**
* Gets the string type required by the nth conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
* - the `%S` format character reverses wideness.
* (e.g. `char *` for `printf`, `char *` or `wchar_t *` for `wprintf`).
* - the `%S` format character reverses wideness on some platforms.
* - the size prefixes 'l'/'w' and 'h' override the type character
* to wide or single-byte characters respectively.
*/

View File

@@ -22,7 +22,7 @@ private Type stripTopLevelSpecifiersOnly(Type t) {
*/
Type getAFormatterWideType() {
exists(FormattingFunction ff |
result = stripTopLevelSpecifiersOnly(ff.getDefaultCharType()) and
result = stripTopLevelSpecifiersOnly(ff.getFormatCharType()) and
result.getSize() != 1
)
}
@@ -46,6 +46,14 @@ abstract class FormattingFunction extends Function {
/** Gets the position at which the format parameter occurs. */
abstract int getFormatParameterIndex();
/**
* Holds if this `FormattingFunction` is in a context that supports
* Microsoft rules and extensions.
*/
predicate isMicrosoft() {
getFile().compiledAsMicrosoft()
}
/**
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
* a `char *` (either way, `%S` will have the opposite meaning).
@@ -55,11 +63,10 @@ abstract class FormattingFunction extends Function {
deprecated predicate isWideCharDefault() { none() }
/**
* Gets the default character type expected for `%s` by this function. Typically
* `char` or `wchar_t`.
* Gets the character type used in the format string for this function.
*/
Type getDefaultCharType() {
result =
Type getFormatCharType() {
result =
stripTopLevelSpecifiersOnly(
stripTopLevelSpecifiersOnly(
getParameter(getFormatParameterIndex()).getType().getUnderlyingType()
@@ -67,19 +74,33 @@ abstract class FormattingFunction extends Function {
)
}
/**
* Gets the default character type expected for `%s` by this function. Typically
* `char` or `wchar_t`.
*/
Type getDefaultCharType() {
(
isMicrosoft() and
result = getFormatCharType()
) or (
not isMicrosoft() and
result instanceof PlainCharType
)
}
/**
* Gets the non-default character type expected for `%S` by this function. Typically
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
* which is correct for a particular function.
*/
Type getNonDefaultCharType() {
(
getDefaultCharType().getSize() = 1 and
result = getAFormatterWideTypeOrDefault()
) or (
getDefaultCharType().getSize() > 1 and
result instanceof PlainCharType
)
(
getDefaultCharType().getSize() = 1 and
result = getWideCharType()
) or (
not getDefaultCharType().getSize() = 1 and
result instanceof PlainCharType
)
}
/**
@@ -89,10 +110,12 @@ abstract class FormattingFunction extends Function {
*/
Type getWideCharType() {
(
result = getDefaultCharType() or
result = getNonDefaultCharType()
) and
result.getSize() > 1
result = getFormatCharType() and
result.getSize() > 1
) or (
not getFormatCharType().getSize() > 1 and
result = getAFormatterWideTypeOrDefault() // may have more than one result
)
}
/**

View File

@@ -0,0 +1,41 @@
import cpp
private import semmle.code.cpp.rangeanalysis.RangeSSA
/**
* Holds if `guard` won't return the value `polarity` when either
* operand is NaN.
*/
predicate nanExcludingComparison(ComparisonOperation guard, boolean polarity) {
polarity = true and
(
guard instanceof LTExpr or
guard instanceof LEExpr or
guard instanceof GTExpr or
guard instanceof GEExpr or
guard instanceof EQExpr
)
or
polarity = false and
guard instanceof NEExpr
}
/**
* Holds if `v` is a use of an SSA definition in `def` which cannot be NaN,
* by virtue of the guard in `def`.
*/
private predicate excludesNan(RangeSsaDefinition def, VariableAccess v) {
exists(VariableAccess inCond, ComparisonOperation guard, boolean branch, LocalScopeVariable lsv |
def.isGuardPhi(inCond, guard, branch) and
inCond.getTarget() = lsv and
v = def.getAUse(lsv) and
guard.getAnOperand() = inCond and
nanExcludingComparison(guard, branch)
)
}
/**
* A variable access which cannot be NaN.
*/
class NonNanVariableAccess extends VariableAccess {
NonNanVariableAccess() { excludesNan(_, this) }
}

View File

@@ -45,6 +45,7 @@ import cpp
private import RangeAnalysisUtils
import RangeSSA
import SimpleRangeAnalysisCached
private import NanAnalysis
/**
* This fixed set of lower bounds is used when the lower bounds of an
@@ -993,6 +994,25 @@ predicate unanalyzableDefBounds(
ub = varMaxVal(v)
}
/**
* Holds if in the `branch` branch of a guard `guard` involving `v`,
* we know that `v` is not NaN, and therefore it is safe to make range
* inferences about `v`.
*/
bindingset[guard, v, branch]
predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boolean branch) {
v.getType().getUnspecifiedType() instanceof IntegralType
or
v.getType().getUnspecifiedType() instanceof FloatingPointType and v instanceof NonNanVariableAccess
or
// The reason the following case is here is to ensure that when we say
// `if (x > 5) { ...then... } else { ...else... }`
// it is ok to conclude that `x > 5` in the `then`, (though not safe
// to conclude that x <= 5 in `else`) even if we had no prior
// knowledge of `x` not being `NaN`.
nanExcludingComparison(guard, branch)
}
/**
* If the guard is a comparison of the form `p*v + q <CMP> r`, then this
* predicate uses the bounds information for `r` to compute a lower bound
@@ -1004,10 +1024,12 @@ predicate lowerBoundFromGuard(
) {
exists (float childLB, RelationStrictness strictness
| boundFromGuard(guard, v, childLB, true, strictness, branch)
| if (strictness = Nonstrict() or
not (v.getType().getUnspecifiedType() instanceof IntegralType))
then lb = childLB
else lb = childLB+1)
| if nonNanGuardedVariable(guard, v, branch)
then (if (strictness = Nonstrict() or
not (v.getType().getUnspecifiedType() instanceof IntegralType))
then lb = childLB
else lb = childLB+1)
else lb = varMinVal(v.getTarget()))
}
/**
@@ -1021,10 +1043,12 @@ predicate upperBoundFromGuard(
) {
exists (float childUB, RelationStrictness strictness
| boundFromGuard(guard, v, childUB, false, strictness, branch)
| if (strictness = Nonstrict() or
not (v.getType().getUnspecifiedType() instanceof IntegralType))
then ub = childUB
else ub = childUB-1)
| if nonNanGuardedVariable(guard, v, branch)
then (if (strictness = Nonstrict() or
not (v.getType().getUnspecifiedType() instanceof IntegralType))
then ub = childUB
else ub = childUB-1)
else ub = varMaxVal(v.getTarget()))
}
/**

View File

@@ -4,7 +4,7 @@ import external.ExternalArtifact
predicate printfLikeFunction(Function func, int formatArg) {
(formatArg = func.(FormattingFunction).getFormatParameterIndex() and not func instanceof UserDefinedFormattingFunction)
or
primitiveVariadicFormatter(func, formatArg, _)
primitiveVariadicFormatter(func, formatArg)
or
exists(ExternalData data |
// TODO Do this \ to / conversion in the toolchain?

View File

@@ -245,9 +245,14 @@ predicate insideFunctionValueMoveTo(Element src, Element dest)
and format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) = argFormat
and (argFormat = "s" or argFormat = "S" or argFormat = "@"))
// Expressions computed from tainted data are also tainted
or (exists (FunctionCall call | dest = call and isPureFunction(call.getTarget().getName()) |
call.getAnArgument() = src
and forall(Expr arg | arg = call.getAnArgument() | arg = src or predictable(arg))))
or exists(FunctionCall call | dest = call and isPureFunction(call.getTarget().getName()) |
call.getAnArgument() = src and
forall(Expr arg | arg = call.getAnArgument() | arg = src or predictable(arg)) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getTarget().getName() = "strlen"
)
or exists(Element a, Element b |
moveToDependingOnSide(a, b) and
if insideValueSource(a) then

View File

@@ -315,3 +315,30 @@ int signedness_cast2(signed char c) {
}
return 0;
}
int nan1(double x) {
if (x < 0.0) {
return 100;
}
else if (x >= 0.0) { // GOOD [x could be NaN]
return 200;
}
else {
return 300;
}
}
int nan2(double x) {
if (x == x) {
// If x compares with anything at all, it's not NaN
if (x < 0.0) {
return 100;
}
else if (x >= 0.0) { // BAD [Always true]
return 200;
}
else {
return 300;
}
}
}

View File

@@ -36,5 +36,6 @@
| PointlessComparison.c:273:9:273:18 | ... > ... | Comparison is always false because c <= 0. |
| PointlessComparison.c:283:13:283:19 | ... >= ... | Comparison is always true because c >= 11. |
| PointlessComparison.c:294:9:294:16 | ... >= ... | Comparison is always false because ui1 <= 0. |
| PointlessComparison.c:337:14:337:21 | ... >= ... | Comparison is always true because x >= 0. |
| RegressionTests.cpp:57:7:57:22 | ... <= ... | Comparison is always true because * ... <= 4294967295. |
| Templates.cpp:9:10:9:24 | ... <= ... | Comparison is always true because local <= 32767. |

View File

@@ -66,3 +66,17 @@ int regression_test_01(unsigned long bb) {
return 1;
}
}
int containsIfDef(int x) {
int result = 0;
if (x > 0) {
result = 1;
}
#if _CONDITION
if (x < 0) {
result = -1;
}
#endif
return result >= 0;
}

View File

@@ -1,10 +1,14 @@
| tests.cpp:18:15:18:22 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:19:15:19:22 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:25:17:25:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:31:17:31:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:33:36:33:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
| tests.cpp:38:36:38:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:27:17:27:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:29:17:29:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
| tests.cpp:34:36:34:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:37:36:37:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
| tests.cpp:42:37:42:44 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:43:37:43:44 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:45:37:45:43 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
| tests.cpp:47:37:47:44 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |

View File

@@ -1,3 +1,3 @@
| tests.cpp:8:5:8:10 | printf | char | char16_t, wchar_t | char16_t, wchar_t |
| tests.cpp:9:5:9:11 | wprintf | wchar_t | char | wchar_t |
| tests.cpp:10:5:10:12 | swprintf | char16_t | char | char16_t |
| tests.cpp:8:5:8:10 | printf | char | char | char16_t, wchar_t | char16_t, wchar_t |
| tests.cpp:9:5:9:11 | wprintf | wchar_t | char | wchar_t | wchar_t |
| tests.cpp:10:5:10:12 | swprintf | char16_t | char | char16_t | char16_t |

View File

@@ -3,6 +3,7 @@ import cpp
from FormattingFunction f
select
f,
concat(f.getFormatCharType().toString(), ", "),
concat(f.getDefaultCharType().toString(), ", "),
concat(f.getNonDefaultCharType().toString(), ", "),
concat(f.getWideCharType().toString(), ", ")

View File

@@ -22,19 +22,27 @@ void tests() {
printf("%S", u"Hello"); // GOOD
printf("%S", L"Hello"); // GOOD
wprintf(L"%s", "Hello"); // BAD: expecting wchar_t
wprintf(L"%s", u"Hello"); // BAD: expecting wchar_t
wprintf(L"%s", L"Hello"); // GOOD
wprintf(L"%s", "Hello"); // GOOD
wprintf(L"%s", u"Hello"); // BAD: expecting char
wprintf(L"%s", L"Hello"); // BAD: expecting char
wprintf(L"%S", "Hello"); // GOOD
wprintf(L"%S", u"Hello"); // BAD: expecting char
wprintf(L"%S", L"Hello"); // BAD: expecting char
wprintf(L"%S", "Hello"); // BAD: expecting wchar_t
wprintf(L"%S", u"Hello"); // BAD: expecting wchar_t
wprintf(L"%S", L"Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // BAD: expecting char16_t
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char16_t
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // BAD: expecting char16_t
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char16_t
swprintf(buffer, BUF_SIZE, u"%hs", "Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%hs", u"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%hs", L"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%ls", "Hello"); // BAD: expecting char16_t
swprintf(buffer, BUF_SIZE, u"%ls", u"Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%ls", L"Hello"); // BAD: expecting char16_t
}

View File

@@ -1,2 +1,3 @@
| printf.cpp:33:31:33:37 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
| printf.cpp:45:29:45:35 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
| printf.cpp:52:29:52:35 | test | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |

View File

@@ -1,2 +1,2 @@
| printf.cpp:15:5:15:12 | swprintf | char16_t | char | char16_t |
| printf.cpp:15:5:15:12 | swprintf | char | char16_t | char16_t |
| printf.cpp:26:5:26:11 | sprintf | char | char16_t | char16_t |

View File

@@ -30,7 +30,7 @@ int sprintf(char *dest, char *format, ...);
void test1() {
WCHAR string[20];
swprintf(string, u"test %s", u"test"); // GOOD
swprintf(string, u"test %s", u"test"); // BAD: `char16_t` string parameter read as `char` string
}
void test2() {

View File

@@ -11,6 +11,8 @@
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:126:18:126:19 | wc | This argument should be of type 'char *' but is of type 'wchar_t *' |
| printf1.h:127:18:127:18 | c | This argument should be of type 'wchar_t *' but is of type 'char *' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

View File

@@ -1,5 +1,5 @@
| common.h:12:12:12:17 | printf | char | wchar_t | wchar_t |
| common.h:15:12:15:18 | wprintf | wchar_t | char | wchar_t |
| common.h:15:12:15:18 | wprintf | char | wchar_t | wchar_t |
| format.h:4:13:4:17 | error | char | wchar_t | wchar_t |
| real_world.h:8:12:8:18 | fprintf | char | wchar_t | wchar_t |
| real_world.h:33:6:33:12 | msg_out | char | wchar_t | wchar_t |

View File

@@ -119,3 +119,11 @@ void test_chars(char c, wchar_t wc, wint_t wt)
wprintf(L"%C", wc); // GOOD (converts to wint_t)
wprintf(L"%C", wt); // GOOD
}
void test_ws(char *c, wchar_t *wc)
{
wprintf(L"%s", c); // GOOD
wprintf(L"%s", wc); // BAD
wprintf(L"%S", c); // BAD
wprintf(L"%S", wc); // GOOD
}

View File

@@ -17,8 +17,11 @@
| printf1.h:74:19:74:22 | C_ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:75:19:75:28 | sizeof(<expr>) | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:84:23:84:35 | ... - ... | This argument should be of type 'ssize_t' but is of type 'long long' |
| printf1.h:125:18:125:18 | c | This argument should be of type '__wchar_t *' but is of type 'char *' |
| printf1.h:128:18:128:19 | wc | This argument should be of type 'char *' but is of type '__wchar_t *' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |
| real_world.h:64:22:64:24 | & ... | This argument should be of type 'short *' but is of type 'signed int *' |
| wide_string.h:25:18:25:20 | c | This argument should be of type 'char' but is of type 'char *' |
| wide_string.h:29:19:29:22 | c | This argument should be of type 'wchar_t' but is of type '__wchar_t *' |

View File

@@ -119,3 +119,11 @@ void test_chars(char c, wchar_t wc, wint_t wt)
wprintf(L"%C", wc); // BAD [NOT DETECTED]
wprintf(L"%C", wt); // BAD [NOT DETECTED]
}
void test_ws(char *c, wchar_t *wc, wint_t *wt)
{
wprintf(L"%s", c); // BAD
wprintf(L"%s", wc); // GOOD
wprintf(L"%S", c); // GOOD
wprintf(L"%S", wc); // BAD
}

View File

@@ -26,5 +26,5 @@ void test_wchar4(char c, const char cc, wchar_t wc, const wchar_t wcc) {
printf("%wc", wc); // GOOD
printf("%wc", wcc); // GOOD
printf("%wc", L'c'); // GOOD
printf("%wc", L"c"); // BAD [NOT DETECTED]
printf("%wc", L"c"); // BAD
}

View File

@@ -0,0 +1,6 @@
| test.cpp:42:31:42:36 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:48:25:48:30 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:49:17:49:30 | new[] | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
| test.cpp:55:11:55:24 | new[] | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-190/TaintedAllocationSize.ql

View File

@@ -0,0 +1,58 @@
// Associated with CWE-190: Integer Overflow or Wraparound. http://cwe.mitre.org/data/definitions/190.html
typedef unsigned long size_t;
typedef struct {} FILE;
void *malloc(size_t size);
void *realloc(void *ptr, size_t size);
int atoi(const char *nptr);
struct MyStruct
{
char data[256];
};
namespace std
{
template<class charT> struct char_traits;
template <class charT, class traits = char_traits<charT> >
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
public:
basic_istream<charT,traits>& operator>>(int& n);
};
typedef basic_istream<char> istream;
extern istream cin;
}
int getTainted() {
int i;
std::cin >> i;
return i;
}
int main(int argc, char **argv) {
int tainted = atoi(argv[1]);
MyStruct *arr1 = (MyStruct *)malloc(sizeof(MyStruct)); // GOOD
MyStruct *arr2 = (MyStruct *)malloc(tainted); // BAD
MyStruct *arr3 = (MyStruct *)malloc(tainted * sizeof(MyStruct)); // BAD
MyStruct *arr4 = (MyStruct *)malloc(getTainted() * sizeof(MyStruct)); // BAD [NOT DETECTED]
MyStruct *arr5 = (MyStruct *)malloc(sizeof(MyStruct) + tainted); // BAD [NOT DETECTED]
int size = tainted * 8;
char *chars1 = (char *)malloc(size); // BAD
char *chars2 = new char[size]; // BAD
char *chars3 = new char[8]; // GOOD
arr1 = (MyStruct *)realloc(arr1, sizeof(MyStruct) * tainted); // BAD
size = 8;
chars3 = new char[size]; // GOOD [FALSE POSITIVE]
return 0;
}

View File

@@ -8,6 +8,3 @@
| test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:11:29:11:32 | argv | User-provided value |
| test.c:44:7:44:10 | len2 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:41:17:41:20 | argv | User-provided value |
| test.c:54:7:54:10 | len3 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:51:17:51:20 | argv | User-provided value |
| test.c:74:7:74:10 | len5 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:71:19:71:22 | argv | User-provided value |
| test.c:84:7:84:10 | len6 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:81:19:81:22 | argv | User-provided value |
| test.c:94:7:94:10 | len7 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:91:19:91:22 | argv | User-provided value |

View File

@@ -71,7 +71,7 @@ int main(int argc, char** argv) {
len5 = strlen(argv[1]);
while (len5)
{
len5--; // GOOD: can't underflow [FALSE POSITIVE]
len5--; // GOOD: can't underflow
}
}
@@ -81,7 +81,7 @@ int main(int argc, char** argv) {
len6 = strlen(argv[1]);
while (len6 != 0)
{
len6--; // GOOD: can't underflow [FALSE POSITIVE]
len6--; // GOOD: can't underflow
}
}
@@ -91,7 +91,7 @@ int main(int argc, char** argv) {
len7 = strlen(argv[1]);
while ((len7) && (1))
{
len7--; // GOOD: can't underflow [FALSE POSITIVE]
len7--; // GOOD: can't underflow
}
}