diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.qhelp b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.qhelp index 191e819..5093129 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.qhelp +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.qhelp @@ -6,20 +6,29 @@

Integer variables may be implicitly casted to a type of different size and signedness. If the variable is casted to a type of smaller bit-size or different signedness without a proper bound checking, -then the casting may silently truncate the variable's value or make it semantically meaningless. +then the casting may silently change the variable's value or make it semantically meaningless. -This query finds implicit casts that cannot be proven to be safe. +Since implicit casts are introduced by the compiler, developers may be not aware of them and the compiled code +may behave incorrectly aka may have bugs. + +This query finds implicit casts that cannot be proven to be safe. +Safe means that the input value is known to fit into destination type aka the value won't change.

-

Either change variables types to avoid implicit conversions or verify that converting highlighted variables is always safe.

+

+Either adjust types of problematic variables to avoid implicit conversions, +make the code validate that converting the variables is safe, +or add explicit conversions that would make the compiler avoid introducing implicit ones. +

-

In this example, the call to malloc_wrapper may silently truncate large variable, and so the allocated buffer will be of smaller size than the test function expects.

+

In this example, the call to malloc_wrapper may silently truncate large variable +so that the allocated buffer will be of smaller size than the test function expects.

diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 3a6ce65..a6b3515 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -1,96 +1,331 @@ /** - * @name Unsafe implicit integer conversion + * @name Find all problematic implicit casts + * @description Find all implicit casts that may be problematic. That is, casts that may result in unexpected truncation, reinterpretation or widening of values. + * @kind path-problem * @id tob/cpp/unsafe-implicit-conversions - * @description Finds implicit integer casts that may overflow or be truncated, with false positive reduction via Value Range Analysis - * @kind problem - * @tags security experimental - * @problem.severity warning - * @precision low - * @security-severity 4.0 - * @group security - **/ + * @tags security + * @problem.severity error + * @precision high + */ import cpp -private import experimental.semmle.code.cpp.rangeanalysis.ExtendedRangeAnalysis private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils +private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +private import experimental.semmle.code.cpp.rangeanalysis.ExtendedRangeAnalysis +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition +private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.TaintTracking +private import semmle.code.cpp.security.FlowSources +/** + * Models standard I/O functions that return a length value bounded by their size argument + * with possible -1 error return value + */ +private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { + LenApproxFunc() { + this.getTarget().hasName(["recvfrom", "recv", "sendto", "send", "read", "write", "readv"]) + } -predicate safeLowerBound(Expr cast, IntegralType toType) { - exists(float lowerB | - lowerB = lowerBound(cast) - and lowerB >= typeLowerBound(toType) + override float getLowerBounds() { result = -1 } + + override float getUpperBounds() { result = getFullyConvertedUpperBounds(this.getArgument(2)) } + + override predicate dependsOnChild(Expr child) { child = this.getArgument(2) } +} + +/* + * Silent findings that require passing large strings to strlen to be exploitable. + * Remove this class to report unsafe implicit conversions from large strlen results + */ + +private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall { + StrlenFunAssumption() { this.getTarget().hasName("strlen") } + + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = 536870912 } + + override predicate dependsOnChild(Expr child) { none() } +} + +/** + * Determines if a function's address is taken in the codebase. + * This indicates that the function may be called while + * the call is not in the FunctionCall class. + */ +predicate addressIsTaken(Function f) { + exists(FunctionCall call | call.getAnArgument().getFullyConverted() = f.getAnAccess()) + or + exists(Expr e | + e.getFullyConverted() = f.getAnAccess() and + e.getType() instanceof FunctionPointerType + ) + or + exists(Variable v | + v.getInitializer().getExpr().getFullyConverted() = f.getAnAccess() and + v.getType() instanceof FunctionPointerType + ) + or + exists(ArrayAggregateLiteral arrayInit | + arrayInit.getAnElementExpr(_).getFullyConverted() = f.getAnAccess() + ) + or + exists(ReturnStmt ret | ret.getExpr().getFullyConverted() = f.getAnAccess()) +} + +/** + * Propagates argument range information from function calls to function parameters + */ +class ConstrainArgs extends SimpleRangeAnalysisDefinition { + private Function func; + private Parameter param; + private FunctionCall call; + + ConstrainArgs() { + param.getFunction() = func and + call.getTarget() = func and + call.getEnclosingFunction() != func and + param.getType().getUnspecifiedType() instanceof IntegralType and + this = param.getFunction().getEntryPoint() and + not addressIsTaken(func) + } + + override predicate hasRangeInformationFor(StackVariable v) { v = param } + + override float getLowerBounds(StackVariable v) { + v = param and + result = getFullyConvertedLowerBounds(call.getArgument(param.getIndex())) + } + + override float getUpperBounds(StackVariable v) { + v = param and + result = getFullyConvertedUpperBounds(call.getArgument(param.getIndex())) + } + + override predicate dependsOnExpr(StackVariable v, Expr e) { + v = param and + e = call.getArgument(param.getIndex()) + } +} + +/** + * Helper to extract left and right operands from bitwise OR operations + */ +predicate getLeftRightOrOperands(Expr orExpr, Expr l, Expr r) { + l = orExpr.(BitwiseOrExpr).getLeftOperand() and + r = orExpr.(BitwiseOrExpr).getRightOperand() + or + l = orExpr.(AssignOrExpr).getLValue() and + r = orExpr.(AssignOrExpr).getRValue() +} + +/** + * Provides range analysis for bitwise OR operations with non-negative constants + */ +private class ConstantBitwiseOrExprRange extends SimpleRangeAnalysisExpr { + ConstantBitwiseOrExprRange() { + exists(Expr l, Expr r | getLeftRightOrOperands(this, l, r) | + // No operand can be a negative constant + not (evaluateConstantExpr(l) < 0 or evaluateConstantExpr(r) < 0) + ) + } + + Expr getLeftOperand() { getLeftRightOrOperands(this, result, _) } + + Expr getRightOperand() { getLeftRightOrOperands(this, _, result) } + + override float getLowerBounds() { + // If an operand can have negative values, the lower bound is unconstrained. + // Otherwise, the upper bound is the sum of upper bounds. + exists(float lLower, float rLower | + lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and + rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and + ( + (lLower < 0 or rLower < 0) and + result = exprMinVal(this) + or + result = lLower.maximum(rLower) + ) + ) + } + + override float getUpperBounds() { + // If an operand can have negative values, the upper bound is unconstrained. + // Otherwise, the upper bound is the greatest upper bound. + exists(float lLower, float lUpper, float rLower, float rUpper | + lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and + lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and + rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and + rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and + ( + (lLower < 0 or rLower < 0) and + result = exprMaxVal(this) + or + result = rUpper + lUpper + ) ) + } + + override predicate dependsOnChild(Expr child) { + child = this.getLeftOperand() or child = this.getRightOperand() + } +} + +/** + * Checks if an expression has a safe lower bound for conversion to the given type + * using both SimpleRangeAnalysis and IR-based RangeAnalysis + */ +predicate safeLowerBound(Expr cast, IntegralType toType) { + exists(float lowerB | + lowerB = lowerBound(cast) and + lowerB >= typeLowerBound(toType) + ) + or + // comment the exists formula below to speed up the query + exists(Instruction instr, Bound b, int delta | + not exists(float knownValue | knownValue = cast.getValue().toFloat()) and + instr.getUnconvertedResultExpression() = cast and + boundedInstruction(instr, b, delta, false, _) and // false = lower bound + lowerBound(b.getInstruction().getUnconvertedResultExpression().getExplicitlyConverted()) + delta >= + typeLowerBound(toType) + ) } +/** + * Checks if an expression has a safe upper bound for conversion to the given type + * using both SimpleRangeAnalysis and IR-based RangeAnalysis + */ predicate safeUpperBound(Expr cast, IntegralType toType) { - exists(float upperB | - upperB = upperBound(cast) - and upperB <= typeUpperBound(toType) - ) + exists(float upperB | + upperB = upperBound(cast) and + upperB <= typeUpperBound(toType) + ) + or + // comment the exists formula below to speed up the query + exists(Instruction instr, Bound b, int delta | + not exists(float knownValue | knownValue = cast.getValue().toFloat()) and + instr.getUnconvertedResultExpression() = cast and + boundedInstruction(instr, b, delta, true, _) and // true = upper bound + upperBound(b.getInstruction().getUnconvertedResultExpression().getExplicitlyConverted()) + delta <= + typeUpperBound(toType) + ) } +/** + * Checks if an expression has both safe lower and upper bounds for conversion to the given type + */ predicate safeBounds(Expr cast, IntegralType toType) { - safeLowerBound(cast, toType) and safeUpperBound(cast, toType) + safeLowerBound(cast, toType) and safeUpperBound(cast, toType) } +/* + * Taint tracking from user-controlled inputs to implicit conversions + */ + +module UserInputToImplicitConversionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(RemoteFlowSourceFunction remoteFlow | remoteFlow = source.asExpr().(Call).getTarget()) + or + exists(LocalFlowSourceFunction localFlow | localFlow = source.asExpr().(Call).getTarget()) + or + source instanceof RemoteFlowSource + or + source instanceof LocalFlowSource + } + + predicate isSink(DataFlow::Node sink) { + exists(IntegralConversion cast | + cast.isImplicit() and + cast.getExpr() = sink.asExpr() + ) + } +} -from IntegralConversion cast, IntegralType fromType, IntegralType toType +module UserInputToImplicitConversionConfigFlow = + TaintTracking::Global; + +import UserInputToImplicitConversionConfigFlow::PathGraph + +from + IntegralConversion cast, IntegralType fromType, IntegralType toType, Expr castExpr, + string problemType, UserInputToImplicitConversionConfigFlow::PathNode source, + UserInputToImplicitConversionConfigFlow::PathNode sink where - cast.isImplicit() - and fromType = cast.getExpr().getExplicitlyConverted().getUnspecifiedType() - and toType = cast.getUnspecifiedType() - and not toType instanceof BoolType - - and ( - // truncation - fromType.getSize() > toType.getSize() - or - // reinterpretation - ( - fromType.getSize() = toType.getSize() - and - ( - (fromType.isUnsigned() and toType.isSigned()) - or - (fromType.isSigned() and toType.isUnsigned()) - ) - ) - or - // widening - ( - fromType.getSize() < toType.getSize() and fromType.isSigned() and toType.isUnsigned() - ) + cast.getExpr() = castExpr and + // only implicit conversions + cast.isImplicit() and + // the cast expression has attached all explicit and implicit casts; we skip all conversions up to the last explicit casts + fromType = castExpr.getExplicitlyConverted().getUnspecifiedType() and + toType = cast.getUnspecifiedType() and + // skip same type casts and casts to bools + fromType != toType and + not toType instanceof BoolType and + // limit findings to only possibly problematic cases + ( + // truncation + problemType = "truncation" and + fromType.getSize() > toType.getSize() and + not safeBounds(castExpr, toType) + or + // reinterpretation + problemType = "reinterpretation" and + fromType.getSize() = toType.getSize() and + ( + fromType.isUnsigned() and toType.isSigned() and not safeUpperBound(castExpr, toType) + or + fromType.isSigned() and toType.isUnsigned() and not safeLowerBound(castExpr, toType) ) - - // skip if value is in safe range - and not safeBounds(cast.getExpr(), toType) - - and not ( - // skip conversions in arithmetic operations - fromType.getSize() <= toType.getSize() // should always hold - and exists(BinaryArithmeticOperation arithmetic | - (arithmetic instanceof AddExpr or arithmetic instanceof SubExpr or arithmetic instanceof MulExpr) - and arithmetic.getAnOperand().getConversion*() = cast - ) + or + // widening + fromType.getSize() < toType.getSize() and + ( + problemType = "widening" and + fromType.isSigned() and + toType.isUnsigned() and + not safeLowerBound(castExpr, toType) + or + // unsafe promotion + problemType = "promotion with bitwise complement" and + exists(ComplementExpr complement | complement.getOperand().getConversion*() = cast) ) - - and not ( - // skip some conversions in equality operations - fromType.getSize() <= toType.getSize() - and fromType.isSigned() // should always hold - and exists(EqualityOperation eq, Expr castHandSide, Expr otherHandSide | - castHandSide = eq.getAnOperand() - and otherHandSide = eq.getAnOperand() - and castHandSide != otherHandSide - and castHandSide.getConversion*() = cast - and otherHandSide.getValue().toFloat() < typeUpperBound(toType) + typeLowerBound(fromType) - ) + ) and + // skip conversions in some arithmetic operations + not ( + fromType.getSize() <= toType.getSize() and + exists(BinaryArithmeticOperation arithmetic | + ( + arithmetic instanceof AddExpr or + arithmetic instanceof SubExpr or + arithmetic instanceof MulExpr + ) and + arithmetic.getAnOperand().getConversion*() = cast ) - - // skip unused function - and ( - exists(FunctionCall fc | fc.getTarget() = cast.getEnclosingFunction()) - or - exists(FunctionAccess fc | fc.getTarget() = cast.getEnclosingFunction()) + ) and + // skip some conversions in some equality operations + not ( + fromType.getSize() <= toType.getSize() and + exists(EqualityOperation eq, Expr firstHandSide, Expr secondHandSide | + firstHandSide = eq.getAnOperand() and + secondHandSide = eq.getAnOperand() and + firstHandSide != secondHandSide and + firstHandSide.getConversion*() = cast and + upperBound(secondHandSide) < (typeUpperBound(toType) + typeLowerBound(fromType)) ) -select cast, "Implicit cast from " + fromType + " to " + toType + "; bounds are [" + lowerBound(cast.getExpr())+ "; " + upperBound(cast.getExpr()) + "]" + ) and + // skip unused functions + ( + exists(FunctionCall fc | fc.getTarget() = cast.getEnclosingFunction()) + or + exists(FunctionAccess fc | fc.getTarget() = cast.getEnclosingFunction()) + or + cast.getEnclosingFunction().getName() = "main" + or + addressIsTaken(cast.getEnclosingFunction()) + ) and + // tainted by user-provided data + castExpr = sink.getNode().asExpr() and + UserInputToImplicitConversionConfigFlow::flowPath(source, sink) +select sink.getNode(), source, sink, + "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ") in $@", castExpr, + castExpr.toString() diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected index d75ac2c..e5382b2 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected @@ -1,23 +1,245 @@ -| test.cpp:72:17:72:21 | (int)... | Implicit cast from unsigned long to int; bounds are [4294967297; 4294967297] | -| test.cpp:73:20:73:24 | (int)... | Implicit cast from unsigned long to int; bounds are [4294967297; 4294967297] | -| test.cpp:74:23:74:27 | (int)... | Implicit cast from unsigned long to int; bounds are [4294967297; 4294967297] | -| test.cpp:75:28:75:32 | (int)... | Implicit cast from unsigned long to int; bounds are [4294967297; 4294967297] | -| test.cpp:80:36:80:40 | (int)... | Implicit cast from unsigned long to int; bounds are [0; 18446744073709551616] | -| test.cpp:88:17:88:21 | (int)... | Implicit cast from unsigned long to int; bounds are [4294967297; 4294967297] | -| test.cpp:94:17:94:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int; bounds are [4294967297; 4294967297] | -| test.cpp:100:17:100:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int; bounds are [4294967297; 4294967297] | -| test.cpp:106:17:106:21 | (int)... | Implicit cast from long to int; bounds are [4294967297; 4294967297] | -| test.cpp:114:17:114:17 | (int)... | Implicit cast from unsigned long to int; bounds are [0; 18446744073709551616] | -| test.cpp:121:17:121:21 | (int)... | Implicit cast from unsigned int to int; bounds are [0; 4294967295] | -| test.cpp:121:28:121:28 | (int)... | Implicit cast from unsigned int to int; bounds are [0; 4294967295] | -| test.cpp:127:17:127:21 | (unsigned short)... | Implicit cast from unsigned long to unsigned short; bounds are [4294967297; 4294967297] | -| test.cpp:133:17:133:21 | (int)... | Implicit cast from unsigned long to int; bounds are [4294967297; 4294967297] | -| test.cpp:140:17:140:18 | (unsigned short)... | Implicit cast from unsigned long to unsigned short; bounds are [0; 18446744073709551616] | -| test.cpp:146:17:146:21 | (int)... | Implicit cast from unsigned int to int; bounds are [4294967295; 4294967295] | -| test.cpp:151:17:151:26 | (int)... | Implicit cast from unsigned int to int; bounds are [2147484985; 2147484985] | -| test.cpp:157:17:157:21 | (unsigned int)... | Implicit cast from long to unsigned int; bounds are [-1; -1] | -| test.cpp:163:17:163:50 | (uint16_t)... | Implicit cast from int to unsigned short; bounds are [-51956; -51956] | -| test.cpp:264:12:264:65 | (unsigned int)... | Implicit cast from int to unsigned int; bounds are [-2147483648; 2147483647] | -| test.cpp:270:16:270:31 | (size_t)... | Implicit cast from int to unsigned long; bounds are [-2147483648; 2147483647] | -| test.cpp:271:14:271:14 | (int)... | Implicit cast from unsigned long to int; bounds are [0; 18446744073709551616] | -| test.cpp:289:18:289:18 | (int)... | Implicit cast from unsigned long to int; bounds are [0; 18446744073709551616] | \ No newline at end of file +edges +| test.cpp:24:8:24:17 | *get_number | test.cpp:78:22:78:31 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:79:5:79:35 | ... += ... | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:95:22:95:31 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:101:22:101:31 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:107:20:107:29 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:113:21:113:41 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:127:26:127:51 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:134:22:134:31 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:140:22:140:31 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:146:22:146:31 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:153:26:153:51 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:159:17:159:28 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:164:21:164:41 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:170:17:170:33 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:171:17:171:56 | ... - ... | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:171:17:171:56 | ... - ... | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:176:15:176:33 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:177:22:177:47 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:183:26:183:53 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:189:13:189:29 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:195:19:195:41 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:210:13:210:29 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:285:17:285:33 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:287:17:287:53 | ... - ... | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:353:26:353:53 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:380:5:380:40 | ... = ... | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:380:9:380:40 | ... - ... | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:416:13:416:22 | call to get_number | provenance | | +| test.cpp:24:8:24:17 | *get_number | test.cpp:418:25:418:34 | call to get_number | provenance | | +| test.cpp:26:13:26:16 | read output argument | test.cpp:27:12:27:14 | ret | provenance | | +| test.cpp:27:12:27:14 | ret | test.cpp:24:8:24:17 | *get_number | provenance | | +| test.cpp:71:26:71:29 | size | test.cpp:72:12:72:15 | size | provenance | | +| test.cpp:78:22:78:31 | call to get_number | test.cpp:78:22:78:31 | call to get_number | provenance | | +| test.cpp:78:22:78:31 | call to get_number | test.cpp:79:5:79:35 | ... += ... | provenance | | +| test.cpp:79:5:79:35 | ... += ... | test.cpp:80:17:80:21 | large | provenance | | +| test.cpp:79:5:79:35 | ... += ... | test.cpp:81:20:81:24 | large | provenance | | +| test.cpp:79:5:79:35 | ... += ... | test.cpp:82:23:82:27 | large | provenance | | +| test.cpp:79:5:79:35 | ... += ... | test.cpp:83:28:83:32 | large | provenance | | +| test.cpp:87:21:87:25 | large | test.cpp:88:36:88:40 | large | provenance | | +| test.cpp:95:22:95:31 | call to get_number | test.cpp:95:22:95:31 | call to get_number | provenance | | +| test.cpp:95:22:95:31 | call to get_number | test.cpp:96:17:96:21 | large | provenance | | +| test.cpp:101:22:101:31 | call to get_number | test.cpp:101:22:101:31 | call to get_number | provenance | | +| test.cpp:101:22:101:31 | call to get_number | test.cpp:102:17:102:21 | large | provenance | | +| test.cpp:107:20:107:29 | call to get_number | test.cpp:107:20:107:29 | call to get_number | provenance | | +| test.cpp:107:20:107:29 | call to get_number | test.cpp:108:17:108:21 | large | provenance | | +| test.cpp:113:21:113:41 | call to get_number | test.cpp:113:21:113:41 | call to get_number | provenance | | +| test.cpp:113:21:113:41 | call to get_number | test.cpp:114:17:114:21 | large | provenance | | +| test.cpp:127:26:127:51 | call to get_number | test.cpp:127:26:127:51 | call to get_number | provenance | | +| test.cpp:127:26:127:51 | call to get_number | test.cpp:129:17:129:21 | large | provenance | | +| test.cpp:134:22:134:31 | call to get_number | test.cpp:134:22:134:31 | call to get_number | provenance | | +| test.cpp:134:22:134:31 | call to get_number | test.cpp:135:17:135:21 | large | provenance | | +| test.cpp:134:22:134:31 | call to get_number | test.cpp:135:17:135:21 | large | provenance | | +| test.cpp:135:17:135:21 | large | test.cpp:135:17:135:21 | large | provenance | | +| test.cpp:140:22:140:31 | call to get_number | test.cpp:140:22:140:31 | call to get_number | provenance | | +| test.cpp:140:22:140:31 | call to get_number | test.cpp:141:17:141:21 | large | provenance | | +| test.cpp:140:22:140:31 | call to get_number | test.cpp:141:17:141:21 | large | provenance | | +| test.cpp:141:17:141:21 | large | test.cpp:141:17:141:21 | large | provenance | | +| test.cpp:146:22:146:31 | call to get_number | test.cpp:146:22:146:31 | call to get_number | provenance | | +| test.cpp:146:22:146:31 | call to get_number | test.cpp:147:19:147:24 | *& ... | provenance | | +| test.cpp:147:19:147:24 | *& ... | test.cpp:148:17:148:18 | * ... | provenance | | +| test.cpp:147:19:147:24 | *& ... | test.cpp:148:17:148:18 | * ... | provenance | | +| test.cpp:148:17:148:18 | * ... | test.cpp:148:17:148:18 | * ... | provenance | | +| test.cpp:153:26:153:51 | call to get_number | test.cpp:153:26:153:51 | call to get_number | provenance | | +| test.cpp:153:26:153:51 | call to get_number | test.cpp:154:17:154:21 | large | provenance | | +| test.cpp:164:21:164:41 | call to get_number | test.cpp:164:21:164:41 | call to get_number | provenance | | +| test.cpp:164:21:164:41 | call to get_number | test.cpp:165:17:165:21 | large | provenance | | +| test.cpp:170:17:170:33 | call to get_number | test.cpp:170:17:170:33 | call to get_number | provenance | | +| test.cpp:170:17:170:33 | call to get_number | test.cpp:171:17:171:56 | ... - ... | provenance | | +| test.cpp:170:17:170:33 | call to get_number | test.cpp:171:17:171:56 | ... - ... | provenance | | +| test.cpp:171:17:171:56 | ... - ... | test.cpp:71:26:71:29 | size | provenance | | +| test.cpp:176:15:176:33 | call to get_number | test.cpp:176:15:176:33 | call to get_number | provenance | | +| test.cpp:176:15:176:33 | call to get_number | test.cpp:178:24:178:24 | a | provenance | | +| test.cpp:176:15:176:33 | call to get_number | test.cpp:178:24:178:28 | ... & ... | provenance | | +| test.cpp:177:22:177:47 | call to get_number | test.cpp:177:22:177:47 | call to get_number | provenance | | +| test.cpp:177:22:177:47 | call to get_number | test.cpp:178:24:178:28 | ... & ... | provenance | | +| test.cpp:183:26:183:53 | call to get_number | test.cpp:183:26:183:53 | call to get_number | provenance | | +| test.cpp:183:26:183:53 | call to get_number | test.cpp:184:18:184:20 | val | provenance | | +| test.cpp:189:13:189:29 | call to get_number | test.cpp:189:13:189:29 | call to get_number | provenance | | +| test.cpp:189:13:189:29 | call to get_number | test.cpp:190:9:190:9 | x | provenance | | +| test.cpp:195:19:195:41 | call to get_number | test.cpp:195:19:195:41 | call to get_number | provenance | | +| test.cpp:195:19:195:41 | call to get_number | test.cpp:196:9:196:9 | x | provenance | | +| test.cpp:200:17:200:17 | a | test.cpp:202:9:202:9 | a | provenance | | +| test.cpp:210:13:210:29 | call to get_number | test.cpp:210:13:210:29 | call to get_number | provenance | | +| test.cpp:210:13:210:29 | call to get_number | test.cpp:211:14:211:14 | b | provenance | | +| test.cpp:218:17:218:17 | a | test.cpp:220:9:220:9 | a | provenance | | +| test.cpp:285:17:285:33 | call to get_number | test.cpp:285:17:285:33 | call to get_number | provenance | | +| test.cpp:285:17:285:33 | call to get_number | test.cpp:286:17:286:31 | large | provenance | | +| test.cpp:285:17:285:33 | call to get_number | test.cpp:287:17:287:53 | ... - ... | provenance | | +| test.cpp:286:17:286:31 | large | test.cpp:71:26:71:29 | size | provenance | | +| test.cpp:287:17:287:53 | ... - ... | test.cpp:71:26:71:29 | size | provenance | | +| test.cpp:353:26:353:53 | call to get_number | test.cpp:353:26:353:53 | call to get_number | provenance | | +| test.cpp:353:26:353:53 | call to get_number | test.cpp:354:35:354:37 | val | provenance | | +| test.cpp:375:31:375:31 | a | test.cpp:376:11:376:11 | a | provenance | | +| test.cpp:375:49:375:49 | b | test.cpp:380:5:380:40 | ... = ... | provenance | | +| test.cpp:375:49:375:49 | b | test.cpp:380:9:380:9 | b | provenance | | +| test.cpp:375:49:375:49 | b | test.cpp:380:9:380:40 | ... - ... | provenance | | +| test.cpp:380:5:380:40 | ... = ... | test.cpp:381:9:381:9 | b | provenance | | +| test.cpp:380:5:380:40 | ... = ... | test.cpp:381:9:381:9 | b | provenance | | +| test.cpp:416:5:416:24 | ... = ... | test.cpp:421:11:421:15 | large | provenance | | +| test.cpp:416:13:416:22 | call to get_number | test.cpp:416:5:416:24 | ... = ... | provenance | | +| test.cpp:418:25:418:34 | call to get_number | test.cpp:418:25:418:34 | call to get_number | provenance | | +| test.cpp:418:25:418:34 | call to get_number | test.cpp:439:12:439:26 | somenumber | provenance | | +| test.cpp:418:25:418:34 | call to get_number | test.cpp:441:12:441:26 | somenumber | provenance | | +| test.cpp:418:25:418:34 | call to get_number | test.cpp:461:15:461:40 | somenumber | provenance | | +| test.cpp:418:25:418:34 | call to get_number | test.cpp:461:43:461:68 | somenumber | provenance | | +| test.cpp:421:11:421:15 | large | test.cpp:87:21:87:25 | large | provenance | | +| test.cpp:439:12:439:26 | somenumber | test.cpp:200:17:200:17 | a | provenance | | +| test.cpp:441:12:441:26 | somenumber | test.cpp:218:17:218:17 | a | provenance | | +| test.cpp:461:15:461:40 | somenumber | test.cpp:375:31:375:31 | a | provenance | | +| test.cpp:461:43:461:68 | somenumber | test.cpp:375:49:375:49 | b | provenance | | +nodes +| test.cpp:24:8:24:17 | *get_number | semmle.label | *get_number | +| test.cpp:26:13:26:16 | read output argument | semmle.label | read output argument | +| test.cpp:27:12:27:14 | ret | semmle.label | ret | +| test.cpp:71:26:71:29 | size | semmle.label | size | +| test.cpp:72:12:72:15 | size | semmle.label | size | +| test.cpp:78:22:78:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:78:22:78:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:79:5:79:35 | ... += ... | semmle.label | ... += ... | +| test.cpp:80:17:80:21 | large | semmle.label | large | +| test.cpp:81:20:81:24 | large | semmle.label | large | +| test.cpp:82:23:82:27 | large | semmle.label | large | +| test.cpp:83:28:83:32 | large | semmle.label | large | +| test.cpp:87:21:87:25 | large | semmle.label | large | +| test.cpp:88:36:88:40 | large | semmle.label | large | +| test.cpp:95:22:95:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:95:22:95:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:96:17:96:21 | large | semmle.label | large | +| test.cpp:101:22:101:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:101:22:101:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:102:17:102:21 | large | semmle.label | large | +| test.cpp:107:20:107:29 | call to get_number | semmle.label | call to get_number | +| test.cpp:107:20:107:29 | call to get_number | semmle.label | call to get_number | +| test.cpp:108:17:108:21 | large | semmle.label | large | +| test.cpp:113:21:113:41 | call to get_number | semmle.label | call to get_number | +| test.cpp:113:21:113:41 | call to get_number | semmle.label | call to get_number | +| test.cpp:114:17:114:21 | large | semmle.label | large | +| test.cpp:127:26:127:51 | call to get_number | semmle.label | call to get_number | +| test.cpp:127:26:127:51 | call to get_number | semmle.label | call to get_number | +| test.cpp:129:17:129:21 | large | semmle.label | large | +| test.cpp:134:22:134:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:134:22:134:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:135:17:135:21 | large | semmle.label | large | +| test.cpp:135:17:135:21 | large | semmle.label | large | +| test.cpp:135:17:135:21 | large | semmle.label | large | +| test.cpp:140:22:140:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:140:22:140:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:141:17:141:21 | large | semmle.label | large | +| test.cpp:141:17:141:21 | large | semmle.label | large | +| test.cpp:141:17:141:21 | large | semmle.label | large | +| test.cpp:146:22:146:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:146:22:146:31 | call to get_number | semmle.label | call to get_number | +| test.cpp:147:19:147:24 | *& ... | semmle.label | *& ... | +| test.cpp:148:17:148:18 | * ... | semmle.label | * ... | +| test.cpp:148:17:148:18 | * ... | semmle.label | * ... | +| test.cpp:148:17:148:18 | * ... | semmle.label | * ... | +| test.cpp:153:26:153:51 | call to get_number | semmle.label | call to get_number | +| test.cpp:153:26:153:51 | call to get_number | semmle.label | call to get_number | +| test.cpp:154:17:154:21 | large | semmle.label | large | +| test.cpp:159:17:159:28 | call to get_number | semmle.label | call to get_number | +| test.cpp:164:21:164:41 | call to get_number | semmle.label | call to get_number | +| test.cpp:164:21:164:41 | call to get_number | semmle.label | call to get_number | +| test.cpp:165:17:165:21 | large | semmle.label | large | +| test.cpp:170:17:170:33 | call to get_number | semmle.label | call to get_number | +| test.cpp:170:17:170:33 | call to get_number | semmle.label | call to get_number | +| test.cpp:171:17:171:56 | ... - ... | semmle.label | ... - ... | +| test.cpp:171:17:171:56 | ... - ... | semmle.label | ... - ... | +| test.cpp:176:15:176:33 | call to get_number | semmle.label | call to get_number | +| test.cpp:176:15:176:33 | call to get_number | semmle.label | call to get_number | +| test.cpp:177:22:177:47 | call to get_number | semmle.label | call to get_number | +| test.cpp:177:22:177:47 | call to get_number | semmle.label | call to get_number | +| test.cpp:178:24:178:24 | a | semmle.label | a | +| test.cpp:178:24:178:28 | ... & ... | semmle.label | ... & ... | +| test.cpp:183:26:183:53 | call to get_number | semmle.label | call to get_number | +| test.cpp:183:26:183:53 | call to get_number | semmle.label | call to get_number | +| test.cpp:184:18:184:20 | val | semmle.label | val | +| test.cpp:189:13:189:29 | call to get_number | semmle.label | call to get_number | +| test.cpp:189:13:189:29 | call to get_number | semmle.label | call to get_number | +| test.cpp:190:9:190:9 | x | semmle.label | x | +| test.cpp:195:19:195:41 | call to get_number | semmle.label | call to get_number | +| test.cpp:195:19:195:41 | call to get_number | semmle.label | call to get_number | +| test.cpp:196:9:196:9 | x | semmle.label | x | +| test.cpp:200:17:200:17 | a | semmle.label | a | +| test.cpp:202:9:202:9 | a | semmle.label | a | +| test.cpp:210:13:210:29 | call to get_number | semmle.label | call to get_number | +| test.cpp:210:13:210:29 | call to get_number | semmle.label | call to get_number | +| test.cpp:211:14:211:14 | b | semmle.label | b | +| test.cpp:218:17:218:17 | a | semmle.label | a | +| test.cpp:220:9:220:9 | a | semmle.label | a | +| test.cpp:285:17:285:33 | call to get_number | semmle.label | call to get_number | +| test.cpp:285:17:285:33 | call to get_number | semmle.label | call to get_number | +| test.cpp:286:17:286:31 | large | semmle.label | large | +| test.cpp:287:17:287:53 | ... - ... | semmle.label | ... - ... | +| test.cpp:353:26:353:53 | call to get_number | semmle.label | call to get_number | +| test.cpp:353:26:353:53 | call to get_number | semmle.label | call to get_number | +| test.cpp:354:35:354:37 | val | semmle.label | val | +| test.cpp:375:31:375:31 | a | semmle.label | a | +| test.cpp:375:49:375:49 | b | semmle.label | b | +| test.cpp:376:11:376:11 | a | semmle.label | a | +| test.cpp:380:5:380:40 | ... = ... | semmle.label | ... = ... | +| test.cpp:380:5:380:40 | ... = ... | semmle.label | ... = ... | +| test.cpp:380:9:380:9 | b | semmle.label | b | +| test.cpp:380:9:380:40 | ... - ... | semmle.label | ... - ... | +| test.cpp:381:9:381:9 | b | semmle.label | b | +| test.cpp:416:5:416:24 | ... = ... | semmle.label | ... = ... | +| test.cpp:416:13:416:22 | call to get_number | semmle.label | call to get_number | +| test.cpp:418:25:418:34 | call to get_number | semmle.label | call to get_number | +| test.cpp:418:25:418:34 | call to get_number | semmle.label | call to get_number | +| test.cpp:421:11:421:15 | large | semmle.label | large | +| test.cpp:439:12:439:26 | somenumber | semmle.label | somenumber | +| test.cpp:441:12:441:26 | somenumber | semmle.label | somenumber | +| test.cpp:461:15:461:40 | somenumber | semmle.label | somenumber | +| test.cpp:461:43:461:68 | somenumber | semmle.label | somenumber | +subpaths +#select +| test.cpp:80:17:80:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:80:17:80:21 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:80:17:80:21 | large | large | +| test.cpp:81:20:81:24 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:81:20:81:24 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:81:20:81:24 | large | large | +| test.cpp:82:23:82:27 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:82:23:82:27 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:82:23:82:27 | large | large | +| test.cpp:83:28:83:32 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:83:28:83:32 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:83:28:83:32 | large | large | +| test.cpp:88:36:88:40 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:88:36:88:40 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:88:36:88:40 | large | large | +| test.cpp:96:17:96:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:96:17:96:21 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:96:17:96:21 | large | large | +| test.cpp:102:17:102:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:102:17:102:21 | large | Implicit cast from unsigned long to unsigned int (truncation) in $@ | test.cpp:102:17:102:21 | large | large | +| test.cpp:108:17:108:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:108:17:108:21 | large | Implicit cast from unsigned long to unsigned int (truncation) in $@ | test.cpp:108:17:108:21 | large | large | +| test.cpp:114:17:114:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:114:17:114:21 | large | Implicit cast from long to int (truncation) in $@ | test.cpp:114:17:114:21 | large | large | +| test.cpp:129:17:129:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:129:17:129:21 | large | Implicit cast from unsigned int to int (reinterpretation) in $@ | test.cpp:129:17:129:21 | large | large | +| test.cpp:135:17:135:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:135:17:135:21 | large | Implicit cast from unsigned long to unsigned short (truncation) in $@ | test.cpp:135:17:135:21 | large | large | +| test.cpp:135:17:135:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:135:17:135:21 | large | Implicit cast from unsigned long to unsigned short (truncation) in $@ | test.cpp:135:17:135:21 | large | large | +| test.cpp:141:17:141:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:141:17:141:21 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:141:17:141:21 | large | large | +| test.cpp:141:17:141:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:141:17:141:21 | large | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:141:17:141:21 | large | large | +| test.cpp:148:17:148:18 | * ... | test.cpp:26:13:26:16 | read output argument | test.cpp:148:17:148:18 | * ... | Implicit cast from unsigned long to unsigned short (truncation) in $@ | test.cpp:148:17:148:18 | * ... | * ... | +| test.cpp:148:17:148:18 | * ... | test.cpp:26:13:26:16 | read output argument | test.cpp:148:17:148:18 | * ... | Implicit cast from unsigned long to unsigned short (truncation) in $@ | test.cpp:148:17:148:18 | * ... | * ... | +| test.cpp:154:17:154:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:154:17:154:21 | large | Implicit cast from unsigned int to int (reinterpretation) in $@ | test.cpp:154:17:154:21 | large | large | +| test.cpp:159:17:159:28 | call to get_number | test.cpp:26:13:26:16 | read output argument | test.cpp:159:17:159:28 | call to get_number | Implicit cast from unsigned long to int (truncation) in $@ | test.cpp:159:17:159:26 | call to get_number | call to get_number | +| test.cpp:165:17:165:21 | large | test.cpp:26:13:26:16 | read output argument | test.cpp:165:17:165:21 | large | Implicit cast from long to unsigned int (truncation) in $@ | test.cpp:165:17:165:21 | large | large | +| test.cpp:171:17:171:56 | ... - ... | test.cpp:26:13:26:16 | read output argument | test.cpp:171:17:171:56 | ... - ... | Implicit cast from int to unsigned short (truncation) in $@ | test.cpp:171:17:171:56 | ... - ... | ... - ... | +| test.cpp:178:24:178:24 | a | test.cpp:26:13:26:16 | read output argument | test.cpp:178:24:178:24 | a | Implicit cast from short to unsigned int (widening) in $@ | test.cpp:178:24:178:24 | a | a | +| test.cpp:178:24:178:28 | ... & ... | test.cpp:26:13:26:16 | read output argument | test.cpp:178:24:178:28 | ... & ... | Implicit cast from unsigned int to unsigned short (truncation) in $@ | test.cpp:178:24:178:28 | ... & ... | ... & ... | +| test.cpp:184:18:184:20 | val | test.cpp:26:13:26:16 | read output argument | test.cpp:184:18:184:20 | val | Implicit cast from unsigned short to int (promotion with bitwise complement) in $@ | test.cpp:184:18:184:20 | val | val | +| test.cpp:190:9:190:9 | x | test.cpp:26:13:26:16 | read output argument | test.cpp:190:9:190:9 | x | Implicit cast from int to unsigned long (widening) in $@ | test.cpp:190:9:190:9 | x | x | +| test.cpp:196:9:196:9 | x | test.cpp:26:13:26:16 | read output argument | test.cpp:196:9:196:9 | x | Implicit cast from long long to unsigned long long (reinterpretation) in $@ | test.cpp:196:9:196:9 | x | x | +| test.cpp:202:9:202:9 | a | test.cpp:26:13:26:16 | read output argument | test.cpp:202:9:202:9 | a | Implicit cast from int to unsigned int (reinterpretation) in $@ | test.cpp:202:9:202:9 | a | a | +| test.cpp:211:14:211:14 | b | test.cpp:26:13:26:16 | read output argument | test.cpp:211:14:211:14 | b | Implicit cast from int to unsigned int (reinterpretation) in $@ | test.cpp:211:14:211:14 | b | b | +| test.cpp:220:9:220:9 | a | test.cpp:26:13:26:16 | read output argument | test.cpp:220:9:220:9 | a | Implicit cast from int to unsigned long long (widening) in $@ | test.cpp:220:9:220:9 | a | a | +| test.cpp:354:35:354:37 | val | test.cpp:26:13:26:16 | read output argument | test.cpp:354:35:354:37 | val | Implicit cast from unsigned short to int (promotion with bitwise complement) in $@ | test.cpp:354:35:354:37 | val | val | diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp index 0f985b8..97e62ca 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp @@ -5,19 +5,27 @@ typedef unsigned long uint64_t; typedef long int64_t; typedef unsigned long size_t; + typedef long ssize_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; void *malloc(size_t); void *memset(void*, int, size_t); void free(void*); + void puts(char*); + ssize_t read(int, void*, size_t); #else #include #include #include #include + #include #endif - +size_t get_number() { + size_t ret; + read(0, &ret, sizeof(size_t)); + return ret; +} void* broken_malloc(int); int test_func_1(int size); @@ -67,8 +75,8 @@ int test_func_9(uint16_t size) { // Test simple cases void test1() { - uint64_t large = 0x000000001; - large += 0x100000000; + uint64_t large = get_number(); + large += (uint64_t)get_number(); test_func_1(large); test_func_2(1, large); test_func_3(1, 1, large); @@ -84,85 +92,136 @@ void test2(uint64_t large) { // Test hardcoded value void test3() { - uint64_t large = (uint64_t)0x100000001; + uint64_t large = get_number(); test_func_1(large); } // Test unsigned void test4() { - uint64_t large = 0x100000001; + uint64_t large = get_number(); test_func_5(large); } // Test size_t void test5() { - size_t large = 0x100000001; + size_t large = get_number(); test_func_5(large); } // Test int64_t void test6() { - int64_t large = 0x100000001; + int64_t large = (int64_t)get_number(); test_func_1(large); } // Test reference void test7() { - uint64_t large = 0x100000001; + uint64_t large = get_number(); uint64_t& x = large; uint64_t& y = x; - test_func_1(y); + test_func_1(y); // TODO: our taint tracking does not work with references } // Test two args bug void test8() { - unsigned int large = 0xffffffff; + unsigned int large = (unsigned int)get_number(); unsigned int& x = large; - test_func_3(large, 22, x); + test_func_3(large, 22, x); // TODO for x: our taint tracking does not work with references } // Test passing by reference void test9() { - uint64_t large = 0x100000001; + uint64_t large = get_number(); test_func_6(large); } // Test passing by reference second void test10() { - uint64_t large = 0x100000001; + uint64_t large = get_number(); test_func_7(large); } // Test pointer derederence void test11() { - uint64_t large = 0x100000001; + uint64_t large = get_number(); uint64_t *x = &large; test_func_6(*x); } // Test sign void test12() { - unsigned int large = 0xffffffff; + unsigned int large = (unsigned int)get_number(); test_func_1(large); } // Test large overflow void test13() { - test_func_1(0x80000539); + test_func_1(get_number()); } // Test negative to unsigned void test14() { - int64_t large = -1; + int64_t large = (int64_t)get_number(); test_func_5(large); } // Implicit type promotion void test15() { - int large = 10; - test_func_9((uint16_t)large - (uint16_t)0xcafe); + int large = (int)get_number(); + test_func_9((uint16_t)large - (uint16_t)get_number()); +} + +// Implicit widening in usual arithmetic conversions +void test16() { + short a = (short)get_number(); + unsigned int b = (unsigned int)get_number(); + unsigned short c = a & b; +} + +// Implicit type promotion with binary complement +void test17() { + unsigned short val = (unsigned short)get_number(); + int val2 = (~val) >> 3; +} + +// Implicit casts in comparisons - widening +void test18() { + int x = (int)get_number(); + if (x > sizeof(int)) { puts("That's why."); } } +// Implicit casts in comparisons - reinterpretation +void test19() { + long long x = (long long)get_number(); + if (x > sizeof(int)) { puts("That's why."); } +} + +// Implicit cast in comparison, int -> unsigned int +void test20(int a) { + const unsigned int b = (unsigned int)get_number(); + if (a != b) { // negative a may wrap to b + puts("here"); + return; + } +} + +// Implicit cast in comparison, int -> unsigned int +void test21(unsigned int a) { + int b = (int)get_number(); + if (a != b) { // b may wrap to a + puts("here"); + return; + } +} + +// Implicit cast in comparison, int -> unsigned long long +void test22(int a) { + unsigned long long b = (unsigned long long)get_number(); + if (a != b) { // negative a may wrap to b + puts("here"); + return; + } +} /* * Tests for False Positives @@ -175,69 +234,69 @@ void test_fp1(uint64_t large) { // Test with value known at compile time to fit in 32 bits void test_fp2() { - const uint64_t const_large_ok_value = 0x1; - uint64_t const_large_ok_value2 = 2147483647; - test_func_1(const_large_ok_value); - test_func_1(const_large_ok_value2); + const uint64_t const_large_ok_value = (uint64_t)get_number(); + uint64_t const_large_ok_value2 = get_number(); + test_func_1((int)const_large_ok_value); + test_func_1((int)const_large_ok_value2); } // Test reference with valid type void test_fp3() { - int x; + int x = (int)get_number(); int& y = x; test_func_1(y); } // Test passing by reference with valid type void test_fp4() { - unsigned short x; + unsigned short x = (unsigned short)get_number(); unsigned short& y = x; test_func_6(y); } // Test pointer derederence with explicit cast void test_fp5() { - uint64_t large = 0x100000001; + uint64_t large = get_number(); uint64_t *x = &large; test_func_6((unsigned short)(*x)); } // Test passing by reference with shorter type void test_fp6() { - unsigned short x; + unsigned short x = (unsigned short)get_number(); unsigned short& y = x; test_func_7(y); } // Test sign false-positive void test_fp7() { - unsigned int large = 0x7fffffff; - test_func_1(large); // ok, fits in signed int + unsigned int large = (unsigned int)get_number(); + test_func_1((int)large); // ok, explicit cast } // Test large but valid false-positive void test_fp8() { - uint64_t large = 0x7fffffffffffffff; - test_func_8(large); // ok, fits in int64_t + uint64_t large = get_number(); + test_func_8((int64_t)large); // ok, explicit cast } // Test explicit cast false-positive void test_fp9() { - int large = 0x0eadbeef; + int large = (int)get_number(); test_func_9((uint16_t)large); // ok, explicit cast - test_func_9((uint16_t)(large - 10)); // ok, explicit cast + test_func_9((uint16_t)(large - (int)get_number())); // ok, explicit cast } // Test arithmetic void test_fp10(int argc) { - uint64_t large = 0x01; - large += 0xcafe; - large = large - 0xde; - large = large & 0xfff; + uint64_t large = get_number(); + large += (uint64_t)get_number(); + large = large - (uint64_t)get_number(); + large = large & (uint64_t)get_number(); if (argc == 2) - test_func_8(large); // ok, fits in int64_t + test_func_8((int64_t)large); // ok, explicit cast else - test_func_8(large + 20); // ok, fits in int64_t + test_func_8((int64_t)(large + (uint64_t)get_number())); // ok, explicit cast } #define CA 16 @@ -261,38 +320,102 @@ static inline int complex_two_possible(bool switchx) } unsigned int complex_const(void) { - return complex_two_possible(true) + CA + CB + max_int(CC, 16); + return complex_two_possible(true) + CA + CB; // TODO } int test_fp11(int argc, size_t c) { int a = argc * 2; - size_t b = max_int(1500, a); - int h2 = c; + size_t b = (size_t)max_int((int)get_number(), a); + int h2 = (int)c; if (h2 > 0) { - c = 44; - int w = c; + c = (size_t)get_number(); + int w = (int)c; } if (argc > 1) - b += 10; - b += 100; + b += (size_t)get_number(); + b += (size_t)get_number(); c = 0; c += complex_const(); - c += 4; - c += 10; - c += 1 + 1; - c = (c + 3) & ~3 | 0xf; + c += (size_t)get_number(); + c += (size_t)get_number(); + c += (size_t)get_number(); + c = (c + (size_t)get_number()) & ~((size_t)get_number()) | (size_t)get_number(); b += c; - int result = b; // ok, b's upper bound is known + int result = (int)b; return result; } +void test_fp12() { + unsigned short val = (unsigned short)get_number(); + int val2 = (unsigned short) (~val) >> 3; // TODO: exclude explicit conversions +} + +void test_fp13() { + unsigned short val = (unsigned short)get_number(); + int val2 = -(int)val; +} + +void test_fp14() { + uint64_t large = get_number(); + test_func_1((int)large); + test_func_1(static_cast(large)); + test_func_1(int(large)); +} + +void test_fp15() { + short a = (short)get_number(); + unsigned int b = (unsigned int)get_number(); + unsigned short c = (unsigned short)((unsigned int)a & b); +} + +void test_fp16(unsigned short a, unsigned short b) { + if ( (a-(unsigned short)get_number()) < 0) { // promotion to int, possibly unexpected but we are not reporting such issues + puts("called"); + } + + b = b - (unsigned short)get_number(); + if (b < 0) { // no unexpected promotion + puts("not called"); + } +} + +// Safe implicit cast in comparison, int -> unsigned int +void test_fp17(int a) { + unsigned int b = (unsigned int)get_number(); + if (a != (int)b) { // explicit cast to avoid implicit conversion + puts("here"); + return; + } +} + +// Safe implicit cast in comparison, unsigned int -> long long +void test_fp18(unsigned int a) { + long long b = (long long)get_number(); + if ((long long)a != b) { // explicit cast to avoid implicit conversion + puts("here"); + return; + } +} + +// Safe implicit cast in comparison, int -> unsigned long long +void test_fp19(int a) { + unsigned long long b = (unsigned long long)get_number(); + if ((unsigned long long)a != b) { // explicit cast to avoid implicit conversion + puts("here"); + return; + } +} + + int main(int argc, char **argv) { uint64_t large; - large = 0x100000001; + large = get_number(); + + size_t somenumber = get_number(); test1(); test2(large); @@ -309,6 +432,13 @@ int main(int argc, char **argv) { test13(); test14(); test15(); + test16(); + test17(); + test18(); + test19(); + test20((int)somenumber); + test21((unsigned int)somenumber); + test22((int)somenumber); test_fp1(large); test_fp2(); @@ -319,11 +449,19 @@ int main(int argc, char **argv) { test_fp7(); test_fp8(); test_fp9(); - test_fp10(argc); - - // reported, because Value Range Analysis limitations - test_fp11(argc, 22); - test_fp11(argc, argc); + test_fp10((int)somenumber); + + test_fp11((int)somenumber, (size_t)get_number()); + test_fp11((int)somenumber, somenumber); + + test_fp12(); + test_fp13(); + test_fp14(); + test_fp15(); + test_fp16((unsigned short)somenumber, (unsigned short)somenumber); + test_fp17((int)somenumber); + test_fp18((unsigned int)somenumber); + test_fp19((int)somenumber); return 0; }