From 7748f8fa7b7ca903fdbe1cb5c73a7d56f2ca1a99 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 4 Apr 2025 13:20:21 +0200 Subject: [PATCH 01/15] add bitwise complement --- .../UnsafeImplicitConversions.ql | 40 ++++++++++++--- .../UnsafeImplicitConversions.expected | 49 +++++++++--------- .../UnsafeImplicitConversions/test.cpp | 51 +++++++++++++++++-- 3 files changed, 107 insertions(+), 33 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 3a6ce65..4160758 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -34,7 +34,7 @@ predicate safeBounds(Expr cast, IntegralType toType) { } -from IntegralConversion cast, IntegralType fromType, IntegralType toType +from IntegralConversion cast, IntegralType fromType, IntegralType toType, boolean checkBounds, string problemType where cast.isImplicit() and fromType = cast.getExpr().getExplicitlyConverted().getUnspecifiedType() @@ -43,27 +43,53 @@ where and ( // truncation - fromType.getSize() > toType.getSize() + ( + problemType = "truncation" + and fromType.getSize() > toType.getSize() + and checkBounds = true + ) or // reinterpretation ( - fromType.getSize() = toType.getSize() + problemType = "reinterpretation" + and fromType.getSize() = toType.getSize() and ( (fromType.isUnsigned() and toType.isSigned()) or (fromType.isSigned() and toType.isUnsigned()) ) + and checkBounds = true ) or // widening ( - fromType.getSize() < toType.getSize() and fromType.isSigned() and toType.isUnsigned() + fromType.getSize() < toType.getSize() + and + ( + ( + problemType = "widening" + and fromType.isSigned() and toType.isUnsigned() + and checkBounds = true + ) + or + // unsafe promotion + ( + problemType = "promotion with bitwise complement" + and exists(ComplementExpr complement | + complement.getOperand().getConversion*() = cast + ) + and checkBounds = false + ) + ) ) ) - // skip if value is in safe range - and not safeBounds(cast.getExpr(), toType) + // skip if value is in safe range, except for ~ (complement) + and ( + checkBounds = true implies + not safeBounds(cast.getExpr(), toType) + ) and not ( // skip conversions in arithmetic operations @@ -93,4 +119,4 @@ where or exists(FunctionAccess fc | fc.getTarget() = cast.getEnclosingFunction()) ) -select cast, "Implicit cast from " + fromType + " to " + toType + "; bounds are [" + lowerBound(cast.getExpr())+ "; " + upperBound(cast.getExpr()) + "]" +select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + "), bounds are [" + lowerBound(cast.getExpr())+ "; " + upperBound(cast.getExpr()) + "]" diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected index d75ac2c..5e7e998 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected @@ -1,23 +1,26 @@ -| 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 +| test.cpp:72:17:72:21 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:73:20:73:24 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:74:23:74:27 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:75:28:75:32 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:80:36:80:40 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | +| test.cpp:88:17:88:21 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:94:17:94:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:100:17:100:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:106:17:106:21 | (int)... | Implicit cast from long to int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:114:17:114:17 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | +| test.cpp:121:17:121:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [0; 4294967295] | +| test.cpp:121:28:121:28 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [0; 4294967295] | +| test.cpp:127:17:127:21 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:133:17:133:21 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | +| test.cpp:140:17:140:18 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation), bounds are [0; 18446744073709551616] | +| test.cpp:146:17:146:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [4294967295; 4294967295] | +| test.cpp:151:17:151:26 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [2147484985; 2147484985] | +| test.cpp:157:17:157:21 | (unsigned int)... | Implicit cast from long to unsigned int (truncation), bounds are [-1; -1] | +| test.cpp:163:17:163:50 | (uint16_t)... | Implicit cast from int to unsigned short (truncation), bounds are [-51956; -51956] | +| test.cpp:170:24:170:24 | (unsigned int)... | Implicit cast from short to unsigned int (widening), bounds are [-2; -2] | +| test.cpp:176:18:176:20 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement), bounds are [19; 19] | +| test.cpp:277:12:277:65 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation), bounds are [-2147483648; 2147483647] | +| test.cpp:283:16:283:31 | (size_t)... | Implicit cast from int to unsigned long (widening), bounds are [-2147483648; 2147483647] | +| test.cpp:284:14:284:14 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | +| test.cpp:302:18:302:18 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | +| test.cpp:308:35:308:37 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement), bounds are [19; 19] | diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp index 0f985b8..1d12a09 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp @@ -163,6 +163,19 @@ void test15() { test_func_9((uint16_t)large - (uint16_t)0xcafe); } +// Implicit widening in usual arithmetic conversions +void test16() { + short a = -2; + unsigned int b = 0x13; + unsigned short c = a & b; +} + +// Implicit type promotion with binary complement +void test17() { + unsigned short val = 0x13; + int val2 = (~val) >> 3; +} + /* * Tests for False Positives @@ -267,8 +280,8 @@ unsigned int complex_const(void) { 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 = max_int(1500, a); // ok but reported (Value Range Analysis limitation) + int h2 = c; // ok but reported (Value Range Analysis limitation) if (h2 > 0) { c = 44; int w = c; @@ -286,10 +299,35 @@ int test_fp11(int argc, size_t c) { c = (c + 3) & ~3 | 0xf; b += c; - int result = b; // ok, b's upper bound is known + int result = b; // ok but reported (Value Range Analysis limitation) return result; } +void test_fp12() { + unsigned short val = 0x13; + int val2 = (unsigned short) (~val) >> 3; // TODO: exclude explicit conversions +} + +void test_fp13() { + unsigned short val = 0x13; + int val2 = -val; +} + +void test_fp14() { + uint64_t large = (uint64_t)0x100000001; + test_func_1((int)large); + test_func_1(static_cast(large)); + test_func_1(int{large}); +} + +void test_fp15() { + short a = -2; + unsigned int b = 0x13; + unsigned short c = (unsigned int)a & b; +} + + + int main(int argc, char **argv) { uint64_t large; large = 0x100000001; @@ -309,6 +347,8 @@ int main(int argc, char **argv) { test13(); test14(); test15(); + test16(); + test17(); test_fp1(large); test_fp2(); @@ -324,6 +364,11 @@ int main(int argc, char **argv) { // reported, because Value Range Analysis limitations test_fp11(argc, 22); test_fp11(argc, argc); + + test_fp12(); + test_fp13(); + test_fp14(); + test_fp15(); return 0; } From f9205fe7a3d455b8930c147bb7ce147701e3373e Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Wed, 9 Apr 2025 18:04:54 +0200 Subject: [PATCH 02/15] polished implicit conversions --- .../UnsafeImplicitConversions.ql | 379 +++++++++++++----- .../UnsafeImplicitConversions.expected | 49 ++- .../UnsafeImplicitConversions/test.cpp | 11 +- 3 files changed, 307 insertions(+), 132 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 4160758..dd43781 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -1,122 +1,301 @@ /** - * @name Unsafe implicit integer conversion - * @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 + * @name Find all problematic implicit casts + * @description Find all implicit casts that may be problematic. That is, may result in unexpected truncation, reinterpretation or widening of values. * @kind problem - * @tags security experimental + * @id tob/cpp/unsafe-implicit-conversions + * @tags security * @problem.severity warning - * @precision low - * @security-severity 4.0 - * @group security - **/ + * @precision medium + */ 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.ValueNumbering +private class BufLenFunc extends SimpleRangeAnalysisExpr, FunctionCall { + BufLenFunc() { + this.getTarget() + .getName() + .matches([ + "buf_len", "buf_reverse_capacity", "buf_forward_capacity", "buf_forward_capacity_total" + ]) + } -predicate safeLowerBound(Expr cast, IntegralType toType) { - exists(float lowerB | - lowerB = lowerBound(cast) - and lowerB >= typeLowerBound(toType) - ) + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = typeUpperBound(this.getExpectedReturnType()) } + + override predicate dependsOnChild(Expr child) { none() } } -predicate safeUpperBound(Expr cast, IntegralType toType) { - exists(float upperB | - upperB = upperBound(cast) - and upperB <= typeUpperBound(toType) - ) +private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { + LenApproxFunc() { + this.getTarget().hasName(["recvfrom", "recv", "sendto", "send", "read", "write"]) + } + + override float getLowerBounds() { result = -1 } + + override float getUpperBounds() { result = getFullyConvertedUpperBounds(this.getArgument(2)) } + + override predicate dependsOnChild(Expr child) { child = this.getArgument(2) } } -predicate safeBounds(Expr cast, IntegralType toType) { - safeLowerBound(cast, toType) and safeUpperBound(cast, toType) +private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall { + StrlenFunAssumption() { this.getTarget().hasName("strlen") } + + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = 536870911 } + + override predicate dependsOnChild(Expr child) { none() } } +private class OpenSSLFunc extends SimpleRangeAnalysisExpr, FunctionCall { + OpenSSLFunc() { + this.getTarget() + .getName() + .matches([ + "EVP_CIPHER_get_block_size", "cipher_ctx_block_size", "EVP_CIPHER_CTX_get_block_size", + "EVP_CIPHER_block_size", "HMAC_size", "hmac_ctx_size", "EVP_MAC_CTX_get_mac_size", + "EVP_CIPHER_CTX_mode", "EVP_CIPHER_CTX_get_mode", "EVP_CIPHER_iv_length", + "cipher_ctx_iv_length", "EVP_CIPHER_key_length", "EVP_MD_size", "EVP_MD_get_size", + "cipher_kt_iv_size", "cipher_kt_block_size", "EVP_PKEY_get_size", "EVP_PKEY_get_bits", + "EVP_PKEY_get_security_bits" + ]) + } -from IntegralConversion cast, IntegralType fromType, IntegralType toType, boolean checkBounds, string problemType -where - cast.isImplicit() - and fromType = cast.getExpr().getExplicitlyConverted().getUnspecifiedType() - and toType = cast.getUnspecifiedType() - and not toType instanceof BoolType - - and ( - // truncation - ( - problemType = "truncation" - and fromType.getSize() > toType.getSize() - and checkBounds = true - ) - or - // reinterpretation - ( - problemType = "reinterpretation" - and fromType.getSize() = toType.getSize() - and - ( - (fromType.isUnsigned() and toType.isSigned()) - or - (fromType.isSigned() and toType.isUnsigned()) - ) - and checkBounds = true - ) - or - // widening - ( - fromType.getSize() < toType.getSize() - and - ( - ( - problemType = "widening" - and fromType.isSigned() and toType.isUnsigned() - and checkBounds = true - ) - or - // unsafe promotion - ( - problemType = "promotion with bitwise complement" - and exists(ComplementExpr complement | - complement.getOperand().getConversion*() = cast - ) - and checkBounds = false - ) - ) - ) + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = 32768 } + + override predicate dependsOnChild(Expr child) { none() } +} + +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()) +} + +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()) + } +} + +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() +} + +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) ) - - // skip if value is in safe range, except for ~ (complement) - and ( - checkBounds = true implies - not safeBounds(cast.getExpr(), toType) + } + + 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) + ) ) + } - 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 - ) + 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() + } +} - 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) - ) +predicate safeLowerBound(Expr cast, IntegralType toType) { + exists(float lowerB | + lowerB = lowerBound(cast) and + lowerB >= typeLowerBound(toType) + ) + or + 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()) + delta >= + typeLowerBound(toType) + ) +} + +predicate safeUpperBound(Expr cast, IntegralType toType) { + exists(float upperB | + upperB = upperBound(cast) and + upperB <= typeUpperBound(toType) + ) + or + 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()) + delta <= + typeUpperBound(toType) + ) +} + +predicate safeBounds(Expr cast, IntegralType toType) { + safeLowerBound(cast, toType) and safeUpperBound(cast, toType) +} + +from + IntegralConversion cast, IntegralType fromType, IntegralType toType, Expr castExpr, + string problemType +where + cast.isImplicit() and + fromType = cast.getExpr().getExplicitlyConverted().getUnspecifiedType() and + toType = cast.getUnspecifiedType() and + fromType != toType and + not toType instanceof BoolType and + cast.getExpr() = castExpr and + ( + // 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 unused function - and ( - exists(FunctionCall fc | fc.getTarget() = cast.getEnclosingFunction()) - or - exists(FunctionAccess fc | fc.getTarget() = cast.getEnclosingFunction()) + 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 conversions in arithmetic operations + fromType.getSize() <= toType.getSize() and // should always hold + exists(BinaryArithmeticOperation arithmetic | + ( + arithmetic instanceof AddExpr or + arithmetic instanceof SubExpr or + arithmetic instanceof MulExpr + ) and + arithmetic.getAnOperand().getConversion*() = cast + ) + ) and + not ( + // skip some conversions in equality operations + fromType.getSize() <= toType.getSize() and + fromType.isSigned() and // should always hold + 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) ) -select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + "), bounds are [" + lowerBound(cast.getExpr())+ "; " + upperBound(cast.getExpr()) + "]" + ) and + // skip unused function + ( + 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 + // skip casts in call to msg + not exists(MacroInvocation msgCall | + msgCall.getMacro().hasName("msg") and + msgCall.getStmt().getAChild*() = cast.getEnclosingStmt() + ) and + // not interesting file + not cast.getLocation().getFile().getBaseName().matches("options.c") +select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected index 5e7e998..4fea74b 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected @@ -1,26 +1,23 @@ -| test.cpp:72:17:72:21 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:73:20:73:24 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:74:23:74:27 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:75:28:75:32 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:80:36:80:40 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | -| test.cpp:88:17:88:21 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:94:17:94:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:100:17:100:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:106:17:106:21 | (int)... | Implicit cast from long to int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:114:17:114:17 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | -| test.cpp:121:17:121:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [0; 4294967295] | -| test.cpp:121:28:121:28 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [0; 4294967295] | -| test.cpp:127:17:127:21 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:133:17:133:21 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [4294967297; 4294967297] | -| test.cpp:140:17:140:18 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation), bounds are [0; 18446744073709551616] | -| test.cpp:146:17:146:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [4294967295; 4294967295] | -| test.cpp:151:17:151:26 | (int)... | Implicit cast from unsigned int to int (reinterpretation), bounds are [2147484985; 2147484985] | -| test.cpp:157:17:157:21 | (unsigned int)... | Implicit cast from long to unsigned int (truncation), bounds are [-1; -1] | -| test.cpp:163:17:163:50 | (uint16_t)... | Implicit cast from int to unsigned short (truncation), bounds are [-51956; -51956] | -| test.cpp:170:24:170:24 | (unsigned int)... | Implicit cast from short to unsigned int (widening), bounds are [-2; -2] | -| test.cpp:176:18:176:20 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement), bounds are [19; 19] | -| test.cpp:277:12:277:65 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation), bounds are [-2147483648; 2147483647] | -| test.cpp:283:16:283:31 | (size_t)... | Implicit cast from int to unsigned long (widening), bounds are [-2147483648; 2147483647] | -| test.cpp:284:14:284:14 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | -| test.cpp:302:18:302:18 | (int)... | Implicit cast from unsigned long to int (truncation), bounds are [0; 18446744073709551616] | -| test.cpp:308:35:308:37 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement), bounds are [19; 19] | +| test.cpp:72:17:72:21 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:73:20:73:24 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:74:23:74:27 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:75:28:75:32 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:80:36:80:40 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:88:17:88:21 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:94:17:94:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | +| test.cpp:100:17:100:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | +| test.cpp:106:17:106:21 | (int)... | Implicit cast from long to int (truncation) | +| test.cpp:114:17:114:17 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:121:17:121:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:121:28:121:28 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:127:17:127:21 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | +| test.cpp:133:17:133:21 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:140:17:140:18 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | +| test.cpp:146:17:146:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:151:17:151:26 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:157:17:157:21 | (unsigned int)... | Implicit cast from long to unsigned int (truncation) | +| test.cpp:163:17:163:50 | (uint16_t)... | Implicit cast from int to unsigned short (truncation) | +| test.cpp:170:24:170:24 | (unsigned int)... | Implicit cast from short to unsigned int (widening) | +| test.cpp:176:18:176:20 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | +| test.cpp:277:12:277:47 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | +| test.cpp:308:35:308:37 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp index 1d12a09..1a8495c 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp @@ -274,14 +274,14 @@ 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); // ok but reported (Value Range Analysis limitation) - int h2 = c; // ok but reported (Value Range Analysis limitation) + size_t b = max_int(1500, a); + int h2 = c; if (h2 > 0) { c = 44; int w = c; @@ -299,7 +299,7 @@ int test_fp11(int argc, size_t c) { c = (c + 3) & ~3 | 0xf; b += c; - int result = b; // ok but reported (Value Range Analysis limitation) + int result = b; return result; } @@ -361,9 +361,8 @@ int main(int argc, char **argv) { test_fp9(); test_fp10(argc); - // reported, because Value Range Analysis limitations test_fp11(argc, 22); - test_fp11(argc, argc); + test_fp11(argc, (size_t)argc); test_fp12(); test_fp13(); From 0ef9be673435264e6f26ba5b856a198d8370d1c4 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 10 Apr 2025 15:49:48 +0200 Subject: [PATCH 03/15] docstrings for implicit conversions, final version --- .../UnsafeImplicitConversions.qhelp | 17 +++- .../UnsafeImplicitConversions.ql | 97 +++++++++---------- 2 files changed, 59 insertions(+), 55 deletions(-) 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 dd43781..9345d50 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -1,6 +1,6 @@ /** * @name Find all problematic implicit casts - * @description Find all implicit casts that may be problematic. That is, may result in unexpected truncation, reinterpretation or widening of values. + * @description Find all implicit casts that may be problematic. That is, casts that may result in unexpected truncation, reinterpretation or widening of values. * @kind problem * @id tob/cpp/unsafe-implicit-conversions * @tags security @@ -17,22 +17,10 @@ private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.ValueNumbering -private class BufLenFunc extends SimpleRangeAnalysisExpr, FunctionCall { - BufLenFunc() { - this.getTarget() - .getName() - .matches([ - "buf_len", "buf_reverse_capacity", "buf_forward_capacity", "buf_forward_capacity_total" - ]) - } - - override float getLowerBounds() { result = 0 } - - override float getUpperBounds() { result = typeUpperBound(this.getExpectedReturnType()) } - - override predicate dependsOnChild(Expr child) { none() } -} - +/** + * 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"]) @@ -45,6 +33,11 @@ private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { override predicate dependsOnChild(Expr child) { child = this.getArgument(2) } } +/* + * Uncomment the class below to silent findings that require large strings + * to be passed to strlen to be exploitable. + */ +/* private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall { StrlenFunAssumption() { this.getTarget().hasName("strlen") } @@ -54,28 +47,13 @@ private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall override predicate dependsOnChild(Expr child) { none() } } +*/ -private class OpenSSLFunc extends SimpleRangeAnalysisExpr, FunctionCall { - OpenSSLFunc() { - this.getTarget() - .getName() - .matches([ - "EVP_CIPHER_get_block_size", "cipher_ctx_block_size", "EVP_CIPHER_CTX_get_block_size", - "EVP_CIPHER_block_size", "HMAC_size", "hmac_ctx_size", "EVP_MAC_CTX_get_mac_size", - "EVP_CIPHER_CTX_mode", "EVP_CIPHER_CTX_get_mode", "EVP_CIPHER_iv_length", - "cipher_ctx_iv_length", "EVP_CIPHER_key_length", "EVP_MD_size", "EVP_MD_get_size", - "cipher_kt_iv_size", "cipher_kt_block_size", "EVP_PKEY_get_size", "EVP_PKEY_get_bits", - "EVP_PKEY_get_security_bits" - ]) - } - - override float getLowerBounds() { result = 0 } - - override float getUpperBounds() { result = 32768 } - - 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 @@ -96,6 +74,9 @@ predicate addressIsTaken(Function f) { 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; @@ -128,6 +109,9 @@ class ConstrainArgs extends SimpleRangeAnalysisDefinition { } } +/** + * 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() @@ -136,6 +120,9 @@ predicate getLeftRightOrOperands(Expr orExpr, Expr l, Expr r) { 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) | @@ -185,6 +172,10 @@ private class ConstantBitwiseOrExprRange extends SimpleRangeAnalysisExpr { } } +/** + * 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 @@ -200,6 +191,10 @@ predicate safeLowerBound(Expr cast, IntegralType 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 @@ -215,6 +210,9 @@ predicate safeUpperBound(Expr cast, IntegralType 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) } @@ -223,12 +221,16 @@ from IntegralConversion cast, IntegralType fromType, IntegralType toType, Expr castExpr, string problemType where + cast.getExpr() = castExpr and + // only implicit conversions cast.isImplicit() and - fromType = cast.getExpr().getExplicitlyConverted().getUnspecifiedType() 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 - cast.getExpr() = castExpr and + // limit findings to only possibly problematic cases ( // truncation problemType = "truncation" and @@ -257,9 +259,9 @@ where exists(ComplementExpr complement | complement.getOperand().getConversion*() = cast) ) ) and + // skip conversions in some arithmetic operations not ( - // skip conversions in arithmetic operations - fromType.getSize() <= toType.getSize() and // should always hold + fromType.getSize() <= toType.getSize() and exists(BinaryArithmeticOperation arithmetic | ( arithmetic instanceof AddExpr or @@ -269,8 +271,8 @@ where arithmetic.getAnOperand().getConversion*() = cast ) ) and + // skip some conversions in some equality operations not ( - // skip some conversions in equality operations fromType.getSize() <= toType.getSize() and fromType.isSigned() and // should always hold exists(EqualityOperation eq, Expr castHandSide, Expr otherHandSide | @@ -290,12 +292,5 @@ where cast.getEnclosingFunction().getName() = "main" or addressIsTaken(cast.getEnclosingFunction()) - ) and - // skip casts in call to msg - not exists(MacroInvocation msgCall | - msgCall.getMacro().hasName("msg") and - msgCall.getStmt().getAChild*() = cast.getEnclosingStmt() - ) and - // not interesting file - not cast.getLocation().getFile().getBaseName().matches("options.c") + ) select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" From c80989cc2bb60457726ae9f36e45a3f859b449e1 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 10 Apr 2025 17:18:42 +0200 Subject: [PATCH 04/15] add flow tracking --- .../UnsafeImplicitConversions.ql | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 9345d50..9ede138 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -16,6 +16,8 @@ private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysi private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.ValueNumbering +import semmle.code.cpp.dataflow.new.TaintTracking +import semmle.code.cpp.models.interfaces.FlowSource /** * Models standard I/O functions that return a length value bounded by their size argument @@ -37,17 +39,18 @@ private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { * Uncomment the class below to silent findings that require large strings * to be passed to strlen to be exploitable. */ -/* -private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall { - StrlenFunAssumption() { this.getTarget().hasName("strlen") } - - override float getLowerBounds() { result = 0 } - - override float getUpperBounds() { result = 536870911 } - override predicate dependsOnChild(Expr child) { none() } -} -*/ +/* + * private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall { + * StrlenFunAssumption() { this.getTarget().hasName("strlen") } + * + * override float getLowerBounds() { result = 0 } + * + * override float getUpperBounds() { result = 536870911 } + * + * override predicate dependsOnChild(Expr child) { none() } + * } + */ /** * Determines if a function's address is taken in the codebase. @@ -217,6 +220,33 @@ predicate safeBounds(Expr cast, IntegralType toType) { safeLowerBound(cast, toType) and safeUpperBound(cast, toType) } +/** + * Taint tracking from user-controlled inputs to implicit conversions + * UNUSED: uncomment the code below (near "select") to use + */ +module UnsafeUserInputConversionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(RemoteFlowSourceFunction remoteFlow | + remoteFlow = source.asExpr().(Call).getTarget() and + remoteFlow.hasRemoteFlowSource(_, _) + ) + or + exists(LocalFlowSourceFunction localFlow | + localFlow = source.asExpr().(Call).getTarget() and + localFlow.hasLocalFlowSource(_, _) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(IntegralConversion cast | + cast.isImplicit() and + cast.getExpr() = sink.asExpr() + ) + } +} + +module UnsafeUserInputConversionFlow = TaintTracking::Global; + from IntegralConversion cast, IntegralType fromType, IntegralType toType, Expr castExpr, string problemType @@ -293,4 +323,11 @@ where or addressIsTaken(cast.getEnclosingFunction()) ) + // Uncomment to report conversions with untrusted inputs only + /* + and exists(DataFlow::Node source, DataFlow::Node sink | + cast.getExpr() = sink.asExpr() and + UnsafeUserInputConversionFlow::flow(source, sink) + ) + */ select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" From 6381e7b6651dad2709e61dbb2586df436ea12917 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 10 Apr 2025 17:19:47 +0200 Subject: [PATCH 05/15] formatting --- .../UnsafeImplicitConversions.ql | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 9ede138..addcf2b 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -323,11 +323,12 @@ where or addressIsTaken(cast.getEnclosingFunction()) ) - // Uncomment to report conversions with untrusted inputs only - /* - and exists(DataFlow::Node source, DataFlow::Node sink | - cast.getExpr() = sink.asExpr() and - UnsafeUserInputConversionFlow::flow(source, sink) - ) - */ +// Uncomment to report conversions with untrusted inputs only +/* + * and exists(DataFlow::Node source, DataFlow::Node sink | + * cast.getExpr() = sink.asExpr() and + * UnsafeUserInputConversionFlow::flow(source, sink) + * ) + */ + select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" From a85adc561a123ec817a42f708914bab19b75d705 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 11 Apr 2025 10:52:09 +0200 Subject: [PATCH 06/15] add comment --- .../UnsafeImplicitConversions/UnsafeImplicitConversions.ql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index addcf2b..13499a5 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -184,6 +184,7 @@ predicate safeLowerBound(Expr cast, IntegralType toType) { lowerB = lowerBound(cast) and lowerB >= typeLowerBound(toType) ) + // comment the exists formula below to speed up the query or exists(Instruction instr, Bound b, int delta | not exists(float knownValue | knownValue = cast.getValue().toFloat()) and @@ -203,6 +204,7 @@ predicate safeUpperBound(Expr cast, IntegralType toType) { upperB = upperBound(cast) and upperB <= typeUpperBound(toType) ) + // comment the exists formula below to speed up the query or exists(Instruction instr, Bound b, int delta | not exists(float knownValue | knownValue = cast.getValue().toFloat()) and From 9f335ec59923169a2d127042e4d1896b911bbc93 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 11 Apr 2025 12:56:17 +0200 Subject: [PATCH 07/15] fix conversions in some equality operations --- .../UnsafeImplicitConversions.ql | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 13499a5..283a51b 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -40,18 +40,14 @@ private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { * to be passed to strlen to be exploitable. */ -/* - * private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall { - * StrlenFunAssumption() { this.getTarget().hasName("strlen") } - * - * override float getLowerBounds() { result = 0 } - * - * override float getUpperBounds() { result = 536870911 } - * - * override predicate dependsOnChild(Expr child) { none() } - * } - */ - +// 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 @@ -184,8 +180,8 @@ predicate safeLowerBound(Expr cast, IntegralType toType) { lowerB = lowerBound(cast) and lowerB >= typeLowerBound(toType) ) - // comment the exists formula below to speed up the query 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 @@ -204,8 +200,8 @@ predicate safeUpperBound(Expr cast, IntegralType toType) { upperB = upperBound(cast) and upperB <= typeUpperBound(toType) ) - // comment the exists formula below to speed up the query 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 @@ -224,7 +220,7 @@ predicate safeBounds(Expr cast, IntegralType toType) { /** * Taint tracking from user-controlled inputs to implicit conversions - * UNUSED: uncomment the code below (near "select") to use + * UNUSED: uncomment the code near "select" statement at the bottom to use */ module UnsafeUserInputConversionConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { @@ -306,13 +302,12 @@ where // skip some conversions in some equality operations not ( fromType.getSize() <= toType.getSize() and - fromType.isSigned() and // should always hold - 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) + 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)) ) ) and // skip unused function @@ -326,11 +321,8 @@ where addressIsTaken(cast.getEnclosingFunction()) ) // Uncomment to report conversions with untrusted inputs only -/* - * and exists(DataFlow::Node source, DataFlow::Node sink | - * cast.getExpr() = sink.asExpr() and - * UnsafeUserInputConversionFlow::flow(source, sink) - * ) - */ - +// and exists(DataFlow::Node source, DataFlow::Node sink | +// cast.getExpr() = sink.asExpr() and +// UnsafeUserInputConversionFlow::flow(source, sink) +// ) select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" From e1c10691ae512f466a509bbaa00898aa95b637a0 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 11 Apr 2025 12:56:32 +0200 Subject: [PATCH 08/15] add more tests --- .../UnsafeImplicitConversions.expected | 51 ++++++----- .../UnsafeImplicitConversions/test.cpp | 85 +++++++++++++++++++ 2 files changed, 113 insertions(+), 23 deletions(-) diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected index 4fea74b..adc8017 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected @@ -1,23 +1,28 @@ -| test.cpp:72:17:72:21 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:73:20:73:24 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:74:23:74:27 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:75:28:75:32 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:80:36:80:40 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:88:17:88:21 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:94:17:94:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | -| test.cpp:100:17:100:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | -| test.cpp:106:17:106:21 | (int)... | Implicit cast from long to int (truncation) | -| test.cpp:114:17:114:17 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:121:17:121:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:121:28:121:28 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:127:17:127:21 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | -| test.cpp:133:17:133:21 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:140:17:140:18 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | -| test.cpp:146:17:146:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:151:17:151:26 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:157:17:157:21 | (unsigned int)... | Implicit cast from long to unsigned int (truncation) | -| test.cpp:163:17:163:50 | (uint16_t)... | Implicit cast from int to unsigned short (truncation) | -| test.cpp:170:24:170:24 | (unsigned int)... | Implicit cast from short to unsigned int (widening) | -| test.cpp:176:18:176:20 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | -| test.cpp:277:12:277:47 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | -| test.cpp:308:35:308:37 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | +| test.cpp:73:17:73:21 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:74:20:74:24 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:75:23:75:27 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:76:28:76:32 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:81:36:81:40 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:89:17:89:21 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:95:17:95:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | +| test.cpp:101:17:101:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | +| test.cpp:107:17:107:21 | (int)... | Implicit cast from long to int (truncation) | +| test.cpp:115:17:115:17 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:122:17:122:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:122:28:122:28 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:128:17:128:21 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | +| test.cpp:134:17:134:21 | (int)... | Implicit cast from unsigned long to int (truncation) | +| test.cpp:141:17:141:18 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | +| test.cpp:147:17:147:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:152:17:152:26 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | +| test.cpp:158:17:158:21 | (unsigned int)... | Implicit cast from long to unsigned int (truncation) | +| test.cpp:164:17:164:50 | (uint16_t)... | Implicit cast from int to unsigned short (truncation) | +| test.cpp:171:24:171:24 | (unsigned int)... | Implicit cast from short to unsigned int (widening) | +| test.cpp:177:18:177:20 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | +| test.cpp:183:9:183:9 | (unsigned long)... | Implicit cast from int to unsigned long (widening) | +| test.cpp:189:9:189:9 | (unsigned long long)... | Implicit cast from long long to unsigned long long (reinterpretation) | +| test.cpp:195:9:195:9 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | +| test.cpp:204:14:204:14 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | +| test.cpp:213:9:213:9 | (unsigned long long)... | Implicit cast from int to unsigned long long (widening) | +| test.cpp:316:12:316:47 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | +| test.cpp:347:35:347:37 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp index 1a8495c..f1c1922 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp @@ -10,6 +10,7 @@ void *malloc(size_t); void *memset(void*, int, size_t); void free(void*); + void puts(char*); #else #include #include @@ -176,6 +177,44 @@ void test17() { int val2 = (~val) >> 3; } +// Implicit casts in comparisons - widening +void test18() { + int x = -7; + if (x > sizeof(int)) { puts("That's why."); } +} + +// Implicit casts in comparisons - reinterpretation +void test19() { + long long x = -7; + if (x > sizeof(int)) { puts("That's why."); } +} + +// Implicit cast in comparison, int -> unsigned int +void test20(int a) { + const unsigned int b = 0xffff3502; + 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 = -2; + 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 = 0xfffffffff0000000; + if (a != b) { // negative a may wrap to b + puts("here"); + return; + } +} /* * Tests for False Positives @@ -326,6 +365,43 @@ void test_fp15() { unsigned short c = (unsigned int)a & b; } +void test_fp16(unsigned short a, unsigned short b) { + if ( (a-5) < 0) { // promotion to int, possibly unexpected but we are not reporting such issues + puts("called"); + } + + b = b - 5; + 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 = 0xcafe; + if (a != b) { // negative a cannot wrap to c + puts("here"); + return; + } +} + +// Safe implicit cast in comparison, unsigned int -> long long +void test_fp18(unsigned int a) { + long long b = -13; + if (a != b) { // large a cannot wrap to b + puts("here"); + return; + } +} + +// Safe implicit cast in comparison, int -> unsigned long long +void test_fp19(int a) { + unsigned long long b = 0xffffff1f80000000; + if (a != b) { // large a cannot wrap to b + puts("here"); + return; + } +} int main(int argc, char **argv) { @@ -349,6 +425,11 @@ int main(int argc, char **argv) { test15(); test16(); test17(); + test18(); + test19(); + test20((int)argc); + test21((unsigned int)argc); + test22((int)argc); test_fp1(large); test_fp2(); @@ -368,6 +449,10 @@ int main(int argc, char **argv) { test_fp13(); test_fp14(); test_fp15(); + test_fp16((unsigned short)argc, (unsigned short)argv[0]); + test_fp17((int)argc); + test_fp18((unsigned int)argc); + test_fp19((int)argc); return 0; } From e5e5986cf933c6366889db932d0a2586b3931e6e Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Mon, 14 Apr 2025 11:23:42 +0200 Subject: [PATCH 09/15] fix typos in tests/query --- .../UnsafeImplicitConversions.ql | 50 +++++++++---------- .../UnsafeImplicitConversions/test.cpp | 4 +- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 283a51b..e35b901 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -218,33 +218,31 @@ predicate safeBounds(Expr cast, IntegralType toType) { safeLowerBound(cast, toType) and safeUpperBound(cast, toType) } -/** +/* * Taint tracking from user-controlled inputs to implicit conversions - * UNUSED: uncomment the code near "select" statement at the bottom to use + * UNUSED: uncomment code below and the code near "select" statement at the bottom of the file */ -module UnsafeUserInputConversionConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - exists(RemoteFlowSourceFunction remoteFlow | - remoteFlow = source.asExpr().(Call).getTarget() and - remoteFlow.hasRemoteFlowSource(_, _) - ) - or - exists(LocalFlowSourceFunction localFlow | - localFlow = source.asExpr().(Call).getTarget() and - localFlow.hasLocalFlowSource(_, _) - ) - } - - predicate isSink(DataFlow::Node sink) { - exists(IntegralConversion cast | - cast.isImplicit() and - cast.getExpr() = sink.asExpr() - ) - } -} - -module UnsafeUserInputConversionFlow = TaintTracking::Global; +// module UnsafeUserInputConversionConfig implements DataFlow::ConfigSig { +// predicate isSource(DataFlow::Node source) { +// exists(RemoteFlowSourceFunction remoteFlow | +// remoteFlow = source.asExpr().(Call).getTarget() and +// remoteFlow.hasRemoteFlowSource(_, _) +// ) +// or +// exists(LocalFlowSourceFunction localFlow | +// localFlow = source.asExpr().(Call).getTarget() and +// localFlow.hasLocalFlowSource(_, _) +// ) +// } +// predicate isSink(DataFlow::Node sink) { +// exists(IntegralConversion cast | +// cast.isImplicit() and +// cast.getExpr() = sink.asExpr() +// ) +// } +// } +// module UnsafeUserInputConversionFlow = TaintTracking::Global; from IntegralConversion cast, IntegralType fromType, IntegralType toType, Expr castExpr, string problemType @@ -320,9 +318,9 @@ where or addressIsTaken(cast.getEnclosingFunction()) ) -// Uncomment to report conversions with untrusted inputs only +// UNUSED: Uncomment to report conversions with untrusted inputs only // and exists(DataFlow::Node source, DataFlow::Node sink | -// cast.getExpr() = sink.asExpr() and +// castExpr = sink.asExpr() and // UnsafeUserInputConversionFlow::flow(source, sink) // ) select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp index f1c1922..dd94739 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp @@ -356,7 +356,7 @@ void test_fp14() { uint64_t large = (uint64_t)0x100000001; test_func_1((int)large); test_func_1(static_cast(large)); - test_func_1(int{large}); + test_func_1(int(large)); } void test_fp15() { @@ -449,7 +449,7 @@ int main(int argc, char **argv) { test_fp13(); test_fp14(); test_fp15(); - test_fp16((unsigned short)argc, (unsigned short)argv[0]); + test_fp16((unsigned short)argc, (unsigned short)argc); test_fp17((int)argc); test_fp18((unsigned int)argc); test_fp19((int)argc); From 9d3bad1c30e6cf5038a916bb8b410b5432515861 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 17 Apr 2025 13:16:28 +0200 Subject: [PATCH 10/15] fix user data tracking --- .../UnsafeImplicitConversions.ql | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index e35b901..29caabe 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -1,11 +1,11 @@ /** * @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 problem + * @kind path-problem * @id tob/cpp/unsafe-implicit-conversions * @tags security - * @problem.severity warning - * @precision medium + * @problem.severity error + * @precision high */ import cpp @@ -15,9 +15,8 @@ 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.ValueNumbering -import semmle.code.cpp.dataflow.new.TaintTracking -import semmle.code.cpp.models.interfaces.FlowSource +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 @@ -25,7 +24,7 @@ import semmle.code.cpp.models.interfaces.FlowSource */ private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { LenApproxFunc() { - this.getTarget().hasName(["recvfrom", "recv", "sendto", "send", "read", "write"]) + this.getTarget().hasName(["recvfrom", "recv", "sendto", "send", "read", "write", "readv"]) } override float getLowerBounds() { result = -1 } @@ -36,18 +35,19 @@ private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { } /* - * Uncomment the class below to silent findings that require large strings - * to be passed to strlen to be exploitable. + * 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() } +} -// 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 @@ -220,32 +220,33 @@ predicate safeBounds(Expr cast, IntegralType toType) { /* * Taint tracking from user-controlled inputs to implicit conversions - * UNUSED: uncomment code below and the code near "select" statement at the bottom of the file */ +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() + ) + } +} + +module UserInputToImplicitConversionConfigFlow = TaintTracking::Global; +import UserInputToImplicitConversionConfigFlow::PathGraph -// module UnsafeUserInputConversionConfig implements DataFlow::ConfigSig { -// predicate isSource(DataFlow::Node source) { -// exists(RemoteFlowSourceFunction remoteFlow | -// remoteFlow = source.asExpr().(Call).getTarget() and -// remoteFlow.hasRemoteFlowSource(_, _) -// ) -// or -// exists(LocalFlowSourceFunction localFlow | -// localFlow = source.asExpr().(Call).getTarget() and -// localFlow.hasLocalFlowSource(_, _) -// ) -// } -// predicate isSink(DataFlow::Node sink) { -// exists(IntegralConversion cast | -// cast.isImplicit() and -// cast.getExpr() = sink.asExpr() -// ) -// } -// } -// module UnsafeUserInputConversionFlow = TaintTracking::Global; from IntegralConversion cast, IntegralType fromType, IntegralType toType, Expr castExpr, - string problemType + string problemType, UserInputToImplicitConversionConfigFlow::PathNode source, + UserInputToImplicitConversionConfigFlow::PathNode sink where cast.getExpr() = castExpr and // only implicit conversions @@ -308,7 +309,7 @@ where upperBound(secondHandSide) < (typeUpperBound(toType) + typeLowerBound(fromType)) ) ) and - // skip unused function + // skip unused functions ( exists(FunctionCall fc | fc.getTarget() = cast.getEnclosingFunction()) or @@ -317,10 +318,9 @@ where cast.getEnclosingFunction().getName() = "main" or addressIsTaken(cast.getEnclosingFunction()) - ) -// UNUSED: Uncomment to report conversions with untrusted inputs only -// and exists(DataFlow::Node source, DataFlow::Node sink | -// castExpr = sink.asExpr() and -// UnsafeUserInputConversionFlow::flow(source, sink) -// ) -select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" + ) 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 + ")" From 33c05e6cb881a8177ca42f551223f71ea08a0df3 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 17 Apr 2025 13:16:51 +0200 Subject: [PATCH 11/15] formatting --- .../UnsafeImplicitConversions/UnsafeImplicitConversions.ql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 29caabe..a4accba 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -38,6 +38,7 @@ private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall { * 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") } @@ -221,6 +222,7 @@ predicate safeBounds(Expr cast, IntegralType 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()) @@ -240,7 +242,9 @@ module UserInputToImplicitConversionConfig implements DataFlow::ConfigSig { } } -module UserInputToImplicitConversionConfigFlow = TaintTracking::Global; +module UserInputToImplicitConversionConfigFlow = + TaintTracking::Global; + import UserInputToImplicitConversionConfigFlow::PathGraph from From 1d90d8c7bee4eda364ab3558afcfdbc569bc2ca1 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Wed, 14 May 2025 12:53:27 +0200 Subject: [PATCH 12/15] fix select message --- .../UnsafeImplicitConversions/UnsafeImplicitConversions.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index a4accba..899cd75 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -327,4 +327,5 @@ where castExpr = sink.getNode().asExpr() and UserInputToImplicitConversionConfigFlow::flowPath(source, sink) select sink.getNode(), source, sink, - "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")" + "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ") in $@", castExpr, + castExpr.toString() From 6d7e2eab5eca9f564b89e297531b1ecd3aac08b7 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Mon, 9 Jun 2025 12:34:15 +0200 Subject: [PATCH 13/15] fix tests for local flows --- .../UnsafeImplicitConversions/test.cpp | 173 +++++++++--------- 1 file changed, 91 insertions(+), 82 deletions(-) diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp index dd94739..517e9f0 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp @@ -5,20 +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); @@ -68,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); @@ -85,31 +92,31 @@ 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); @@ -117,81 +124,81 @@ void test7() { // 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 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 = -2; - unsigned int b = 0x13; + 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 = 0x13; + unsigned short val = (unsigned short)get_number(); int val2 = (~val) >> 3; } // Implicit casts in comparisons - widening void test18() { - int x = -7; + int x = (int)get_number(); if (x > sizeof(int)) { puts("That's why."); } } // Implicit casts in comparisons - reinterpretation void test19() { - long long x = -7; + 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 = 0xffff3502; + const unsigned int b = (unsigned int)get_number(); if (a != b) { // negative a may wrap to b puts("here"); return; @@ -200,7 +207,7 @@ void test20(int a) { // Implicit cast in comparison, int -> unsigned int void test21(unsigned int a) { - int b = -2; + int b = (int)get_number(); if (a != b) { // b may wrap to a puts("here"); return; @@ -209,7 +216,7 @@ void test21(unsigned int a) { // Implicit cast in comparison, int -> unsigned long long void test22(int a) { - unsigned long long b = 0xfffffffff0000000; + unsigned long long b = (unsigned long long)get_number(); if (a != b) { // negative a may wrap to b puts("here"); return; @@ -227,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 @@ -319,58 +326,58 @@ unsigned int complex_const(void) { 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; + int result = (int)b; return result; } void test_fp12() { - unsigned short val = 0x13; + unsigned short val = (unsigned short)get_number(); int val2 = (unsigned short) (~val) >> 3; // TODO: exclude explicit conversions } void test_fp13() { - unsigned short val = 0x13; - int val2 = -val; + unsigned short val = (unsigned short)get_number(); + int val2 = -(int)val; } void test_fp14() { - uint64_t large = (uint64_t)0x100000001; + 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 = -2; - unsigned int b = 0x13; - unsigned short c = (unsigned int)a & b; + 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-5) < 0) { // promotion to int, possibly unexpected but we are not reporting such issues + if ( (a-(unsigned short)get_number()) < 0) { // promotion to int, possibly unexpected but we are not reporting such issues puts("called"); } - b = b - 5; + b = b - (unsigned short)get_number(); if (b < 0) { // no unexpected promotion puts("not called"); } @@ -378,8 +385,8 @@ void test_fp16(unsigned short a, unsigned short b) { // Safe implicit cast in comparison, int -> unsigned int void test_fp17(int a) { - unsigned int b = 0xcafe; - if (a != b) { // negative a cannot wrap to c + unsigned int b = (unsigned int)get_number(); + if (a != (int)b) { // explicit cast to avoid implicit conversion puts("here"); return; } @@ -387,8 +394,8 @@ void test_fp17(int a) { // Safe implicit cast in comparison, unsigned int -> long long void test_fp18(unsigned int a) { - long long b = -13; - if (a != b) { // large a cannot wrap to b + long long b = (long long)get_number(); + if ((long long)a != b) { // explicit cast to avoid implicit conversion puts("here"); return; } @@ -396,8 +403,8 @@ void test_fp18(unsigned int a) { // Safe implicit cast in comparison, int -> unsigned long long void test_fp19(int a) { - unsigned long long b = 0xffffff1f80000000; - if (a != b) { // large a cannot wrap to b + unsigned long long b = (unsigned long long)get_number(); + if ((unsigned long long)a != b) { // explicit cast to avoid implicit conversion puts("here"); return; } @@ -406,7 +413,9 @@ void test_fp19(int a) { int main(int argc, char **argv) { uint64_t large; - large = 0x100000001; + large = get_number(); + + size_t somenumber = get_number(); test1(); test2(large); @@ -427,9 +436,9 @@ int main(int argc, char **argv) { test17(); test18(); test19(); - test20((int)argc); - test21((unsigned int)argc); - test22((int)argc); + test20((int)somenumber); + test21((unsigned int)somenumber); + test22((int)somenumber); test_fp1(large); test_fp2(); @@ -440,19 +449,19 @@ int main(int argc, char **argv) { test_fp7(); test_fp8(); test_fp9(); - test_fp10(argc); + test_fp10((int)somenumber); - test_fp11(argc, 22); - test_fp11(argc, (size_t)argc); + 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)argc, (unsigned short)argc); - test_fp17((int)argc); - test_fp18((unsigned int)argc); - test_fp19((int)argc); + test_fp16((unsigned short)somenumber, (unsigned short)somenumber); + test_fp17((int)somenumber); + test_fp18((unsigned int)somenumber); + test_fp19((int)somenumber); return 0; } From 18bceaadb084390e31f313eff4d061d91199bf4c Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Mon, 9 Jun 2025 13:55:23 +0200 Subject: [PATCH 14/15] use explicitly converted in bounds --- .../UnsafeImplicitConversions/UnsafeImplicitConversions.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql index 899cd75..a6b3515 100644 --- a/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql +++ b/cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql @@ -187,7 +187,7 @@ predicate safeLowerBound(Expr cast, IntegralType toType) { 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()) + delta >= + lowerBound(b.getInstruction().getUnconvertedResultExpression().getExplicitlyConverted()) + delta >= typeLowerBound(toType) ) } @@ -207,7 +207,7 @@ predicate safeUpperBound(Expr cast, IntegralType toType) { 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()) + delta <= + upperBound(b.getInstruction().getUnconvertedResultExpression().getExplicitlyConverted()) + delta <= typeUpperBound(toType) ) } From 79ad0ac80d0683c1a796c68168cc98a08e256369 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Mon, 9 Jun 2025 13:55:45 +0200 Subject: [PATCH 15/15] fix expected for taint tracking and add todo tests --- .../UnsafeImplicitConversions.expected | 273 ++++++++++++++++-- .../UnsafeImplicitConversions/test.cpp | 4 +- 2 files changed, 247 insertions(+), 30 deletions(-) diff --git a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected index adc8017..e5382b2 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/UnsafeImplicitConversions.expected @@ -1,28 +1,245 @@ -| test.cpp:73:17:73:21 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:74:20:74:24 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:75:23:75:27 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:76:28:76:32 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:81:36:81:40 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:89:17:89:21 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:95:17:95:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | -| test.cpp:101:17:101:21 | (unsigned int)... | Implicit cast from unsigned long to unsigned int (truncation) | -| test.cpp:107:17:107:21 | (int)... | Implicit cast from long to int (truncation) | -| test.cpp:115:17:115:17 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:122:17:122:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:122:28:122:28 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:128:17:128:21 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | -| test.cpp:134:17:134:21 | (int)... | Implicit cast from unsigned long to int (truncation) | -| test.cpp:141:17:141:18 | (unsigned short)... | Implicit cast from unsigned long to unsigned short (truncation) | -| test.cpp:147:17:147:21 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:152:17:152:26 | (int)... | Implicit cast from unsigned int to int (reinterpretation) | -| test.cpp:158:17:158:21 | (unsigned int)... | Implicit cast from long to unsigned int (truncation) | -| test.cpp:164:17:164:50 | (uint16_t)... | Implicit cast from int to unsigned short (truncation) | -| test.cpp:171:24:171:24 | (unsigned int)... | Implicit cast from short to unsigned int (widening) | -| test.cpp:177:18:177:20 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | -| test.cpp:183:9:183:9 | (unsigned long)... | Implicit cast from int to unsigned long (widening) | -| test.cpp:189:9:189:9 | (unsigned long long)... | Implicit cast from long long to unsigned long long (reinterpretation) | -| test.cpp:195:9:195:9 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | -| test.cpp:204:14:204:14 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | -| test.cpp:213:9:213:9 | (unsigned long long)... | Implicit cast from int to unsigned long long (widening) | -| test.cpp:316:12:316:47 | (unsigned int)... | Implicit cast from int to unsigned int (reinterpretation) | -| test.cpp:347:35:347:37 | (int)... | Implicit cast from unsigned short to int (promotion with bitwise complement) | +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 517e9f0..97e62ca 100644 --- a/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp +++ b/cpp/test/query-tests/security/UnsafeImplicitConversions/test.cpp @@ -119,14 +119,14 @@ void test7() { 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 = (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