diff --git a/java/ql/lib/change-notes/2026-03-28-tainted-arithmetic-bounds-check.md b/java/ql/lib/change-notes/2026-03-28-tainted-arithmetic-bounds-check.md new file mode 100644 index 00000000000..238b1e2978f --- /dev/null +++ b/java/ql/lib/change-notes/2026-03-28-tainted-arithmetic-bounds-check.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `java/tainted-arithmetic` query no longer flags arithmetic expressions that are used directly as an operand of a comparison in bounds-checking patterns. For example, `if (off + len > array.length)` is now recognized as a bounds check rather than a potentially vulnerable computation, reducing false positives. diff --git a/java/ql/lib/semmle/code/java/security/ArithmeticCommon.qll b/java/ql/lib/semmle/code/java/security/ArithmeticCommon.qll index 9282e766627..791c1ab7ba0 100644 --- a/java/ql/lib/semmle/code/java/security/ArithmeticCommon.qll +++ b/java/ql/lib/semmle/code/java/security/ArithmeticCommon.qll @@ -132,7 +132,21 @@ private predicate inBitwiseAnd(Expr exp) { /** Holds if overflow/underflow is irrelevant for this expression. */ predicate overflowIrrelevant(Expr exp) { inBitwiseAnd(exp) or - exp.getEnclosingCallable() instanceof HashCodeMethod + exp.getEnclosingCallable() instanceof HashCodeMethod or + arithmeticUsedInBoundsCheck(exp) +} + +/** + * Holds if `exp` is an arithmetic expression used directly as an operand of a + * comparison, indicating it is part of a bounds check rather than a vulnerable + * computation. For example, in `if (off + len > array.length)`, the addition + * is the bounds check itself. + */ +private predicate arithmeticUsedInBoundsCheck(ArithExpr exp) { + exists(ComparisonExpr comp | + comp.getAnOperand() = exp and + comp.getEnclosingStmt() instanceof IfStmt + ) } /** diff --git a/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java b/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java index 5fde69929b2..80a83392873 100644 --- a/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java +++ b/java/ql/test/query-tests/security/CWE-190/semmle/tests/ArithmeticTainted.java @@ -138,4 +138,18 @@ public class ArithmeticTainted { // BAD: may underflow if input data is very small --data; } + + public static void boundsCheckGood(byte[] bs, int off, int len) { + // GOOD: arithmetic used directly in a bounds check, not as a computation + if (off + len > bs.length) { + throw new IndexOutOfBoundsException(); + } + } + + public static void boundsCheckGood2(int[] arr, int offset, int count) { + // GOOD: subtraction used directly in a bounds check + if (offset - count < 0) { + throw new IndexOutOfBoundsException(); + } + } }