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;
}