diff --git a/config/identical-files.json b/config/identical-files.json index e0a4e8dabf4..fa9087dc9d8 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -62,6 +62,10 @@ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll", "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll" ], + "Bound Java/C#": [ + "java/ql/src/semmle/code/java/dataflow/Bound.qll", + "csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll" + ], "C++ SubBasicBlocks": [ "cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll" diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll new file mode 100644 index 00000000000..b129203db70 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll @@ -0,0 +1,78 @@ +/** + * Provides classes for representing abstract bounds for use in, for example, range analysis. + */ + +private import internal.rangeanalysis.BoundSpecific + +private newtype TBound = + TBoundZero() or + TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or + TBoundExpr(Expr e) { + interestingExprBound(e) and + not exists(SsaVariable v | e = v.getAUse()) + } + +/** + * A bound that may be inferred for an expression plus/minus an integer delta. + */ +abstract class Bound extends TBound { + /** Gets a textual representation of this bound. */ + abstract string toString(); + + /** Gets an expression that equals this bound plus `delta`. */ + abstract Expr getExpr(int delta); + + /** Gets an expression that equals this bound. */ + Expr getExpr() { result = getExpr(0) } + + /** + * Holds if this element is at the specified location. + * The location spans column `sc` of line `sl` to + * column `ec` of line `el` in file `path`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 + } +} + +/** + * The bound that corresponds to the integer 0. This is used to represent all + * integer bounds as bounds are always accompanied by an added integer delta. + */ +class ZeroBound extends Bound, TBoundZero { + override string toString() { result = "0" } + + override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta } +} + +/** + * A bound corresponding to the value of an SSA variable. + */ +class SsaBound extends Bound, TBoundSsa { + /** Gets the SSA variable that equals this bound. */ + SsaVariable getSsa() { this = TBoundSsa(result) } + + override string toString() { result = getSsa().toString() } + + override Expr getExpr(int delta) { result = getSsa().getAUse() and delta = 0 } + + override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + getSsa().getLocation().hasLocationInfo(path, sl, sc, el, ec) + } +} + +/** + * A bound that corresponds to the value of a specific expression that might be + * interesting, but isn't otherwise represented by the value of an SSA variable. + */ +class ExprBound extends Bound, TBoundExpr { + override string toString() { result = getExpr().toString() } + + override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 } + + override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + getExpr().getLocation().hasLocationInfo(path, sl, sc, el, ec) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll new file mode 100644 index 00000000000..edf437e919c --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll @@ -0,0 +1,21 @@ +/** + * Provides C#-specific definitions for bounds. + */ + +private import csharp as CS +private import semmle.code.csharp.dataflow.SSA::Ssa as Ssa +private import semmle.code.csharp.dataflow.internal.rangeanalysis.ConstantUtils as CU + +class SsaVariable extends Ssa::Definition { + /** Gets a read of the source variable underlying this SSA definition. */ + Expr getAUse() { result = getARead() } +} + +class Expr = CS::Expr; + +class IntegralType = CS::IntegralType; + +class ConstantIntegerExpr = CU::ConstantIntegerExpr; + +/** Holds if `e` is a bound expression and it is not an SSA variable read. */ +predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.(CS::PropertyRead)) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll index e283909155c..08b64321a38 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll @@ -17,6 +17,13 @@ predicate propertyOverrides(Property p, string baseClass, string property) { ) } +/** + * Holds if `pa` is an access to the `Length` property of an array. + */ +predicate systemArrayLengthAccess(PropertyAccess pa) { + propertyOverrides(pa.getTarget(), "System.Array", "Length") +} + /** * Holds if expression `e` is either * - a compile time constant with integer value `val`, or @@ -47,7 +54,7 @@ private int getArrayLengthRec(ArrayCreation arrCreation, int index) { } private predicate isArrayLengthAccess(PropertyAccess pa, int length) { - propertyOverrides(pa.getTarget(), "System.Array", "Length") and + systemArrayLengthAccess(pa) and exists(ExplicitDefinition arr, ArrayCreation arrCreation | getArrayLengthRec(arrCreation, arrCreation.getNumberOfLengthArguments() - 1) = length and arrCreation = arr.getADefinition().getSource() and diff --git a/java/ql/src/semmle/code/java/dataflow/Bound.qll b/java/ql/src/semmle/code/java/dataflow/Bound.qll index 4a89f8cfeba..b129203db70 100644 --- a/java/ql/src/semmle/code/java/dataflow/Bound.qll +++ b/java/ql/src/semmle/code/java/dataflow/Bound.qll @@ -2,15 +2,13 @@ * Provides classes for representing abstract bounds for use in, for example, range analysis. */ -import java -private import SSA -private import RangeUtils +private import internal.rangeanalysis.BoundSpecific private newtype TBound = TBoundZero() or TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or TBoundExpr(Expr e) { - e.(FieldRead).getField() instanceof ArrayLengthField and + interestingExprBound(e) and not exists(SsaVariable v | e = v.getAUse()) } @@ -75,6 +73,6 @@ class ExprBound extends Bound, TBoundExpr { override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 } override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - getExpr().hasLocationInfo(path, sl, sc, el, ec) + getExpr().getLocation().hasLocationInfo(path, sl, sc, el, ec) } } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/BoundSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/BoundSpecific.qll new file mode 100644 index 00000000000..f7fca3a184f --- /dev/null +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/BoundSpecific.qll @@ -0,0 +1,20 @@ +/** + * Provides Java-specific definitions for bounds. + */ + +private import java as J +private import semmle.code.java.dataflow.SSA as Ssa +private import semmle.code.java.dataflow.RangeUtils as RU + +class SsaVariable = Ssa::SsaVariable; + +class Expr = J::Expr; + +class IntegralType = J::IntegralType; + +class ConstantIntegerExpr = RU::ConstantIntegerExpr; + +/** Holds if `e` is a bound expression and it is not an SSA variable read. */ +predicate interestingExprBound(Expr e) { + e.(J::FieldRead).getField() instanceof J::ArrayLengthField +}