Add support for Commons-Lang's RandomUtils

This is realised by somewhat generalising our interfaces for modelling RNGs. We also add tests for randomness-related queries that didn't have any, and addtest cases checking the Apache random-number generators are interchangeable with the stdlib ones.
This commit is contained in:
Chris Smowton
2021-03-05 11:40:11 +00:00
parent 8d292070a4
commit e3cf5c235e
30 changed files with 464 additions and 149 deletions

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Added models for the Apache Commons-Lang `RandomUtils` class. This may lead to extra results from queries that check for proper use of random-number generators or those which check the range of possible random values that could be returned, including `java/improper-validation-of-array-index-code-specified` and `java/uncontrolled-arithmetic`.

View File

@@ -11,15 +11,15 @@
*/
import java
import semmle.code.java.security.Random
from MethodAccess ma, Method abs, Method nextIntOrLong, MethodAccess nma
from MethodAccess ma, Method abs, Method nextIntOrLong, RandomDataSource nma
where
ma.getMethod() = abs and
abs.hasName("abs") and
abs.getDeclaringType().hasQualifiedName("java.lang", "Math") and
ma.getAnArgument() = nma and
nma.getMethod() = nextIntOrLong and
(nextIntOrLong.hasName("nextInt") or nextIntOrLong.hasName("nextLong")) and
nextIntOrLong.getDeclaringType().hasQualifiedName("java.util", "Random") and
nextIntOrLong.hasNoParameters()
nextIntOrLong.hasName(["nextInt", "nextLong"]) and
not nma.resultMayBeBounded()
select ma, "Incorrect computation of abs of signed integral random value."

View File

@@ -12,10 +12,11 @@
*/
import java
import semmle.code.java.security.Random
from MethodAccess ma, Method random
where
random.getDeclaringType().hasQualifiedName("java.util", "Random") and
random.getDeclaringType() instanceof RandomNumberGenerator and
ma.getMethod() = random and
ma.getQualifier() instanceof ClassInstanceExpr
select ma, "Random object created and used only once."

View File

@@ -1,6 +1,7 @@
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DefUse
import semmle.code.java.security.Random
private import BoundingChecks
/**
@@ -124,33 +125,16 @@ abstract class BoundedFlowSource extends DataFlow::Node {
}
/**
* Input that is constructed using a `Random` value.
* Input that is constructed using a random value.
*/
class RandomValueFlowSource extends BoundedFlowSource {
RandomValueFlowSource() {
exists(RefType random, MethodAccess nextAccess |
random.hasQualifiedName("java.util", "Random")
|
nextAccess.getCallee().getDeclaringType().getAnAncestor() = random and
nextAccess.getCallee().getName().matches("next%") and
nextAccess = this.asExpr()
)
}
RandomDataSource nextAccess;
override int lowerBound() {
// If this call is to `nextInt()`, the lower bound is zero.
this.asExpr().(MethodAccess).getCallee().hasName("nextInt") and
this.asExpr().(MethodAccess).getNumArgument() = 1 and
result = 0
}
RandomValueFlowSource() { this.asExpr() = nextAccess }
override int upperBound() {
// If this call specified an argument to `nextInt()`, and that argument is a compile time constant,
// it forms the upper bound.
this.asExpr().(MethodAccess).getCallee().hasName("nextInt") and
this.asExpr().(MethodAccess).getNumArgument() = 1 and
result = this.asExpr().(MethodAccess).getArgument(0).(CompileTimeConstantExpr).getIntValue()
}
override int lowerBound() { result = nextAccess.getLowerBound() }
override int upperBound() { result = nextAccess.getUpperBound() }
override string getDescription() { result = "Random value" }
}

View File

@@ -13,34 +13,14 @@
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.Random
import semmle.code.java.security.SecurityTests
import ArithmeticCommon
import DataFlow::PathGraph
class TaintSource extends DataFlow::ExprNode {
TaintSource() {
// Either this is an access to a random number generating method of the right kind, ...
exists(Method def |
def = this.getExpr().(MethodAccess).getMethod() and
(
// Some random-number methods are omitted:
// `nextDouble` and `nextFloat` are between 0 and 1,
// `nextGaussian` is extremely unlikely to hit max values.
def.getName() = "nextInt" or
def.getName() = "nextLong"
) and
def.getNumberOfParameters() = 0 and
def.getDeclaringType().hasQualifiedName("java.util", "Random")
)
or
// ... or this is the array parameter of `nextBytes`, which is filled with random bytes.
exists(MethodAccess m, Method def |
m.getAnArgument() = this.getExpr() and
m.getMethod() = def and
def.getName() = "nextBytes" and
def.getNumberOfParameters() = 1 and
def.getDeclaringType().hasQualifiedName("java.util", "Random")
)
exists(RandomDataSource m | not m.resultMayBeBounded() | m.getOutput() = this.getExpr())
}
}

View File

@@ -68,6 +68,7 @@ private import SSA
private import RangeUtils
private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon
private import semmle.code.java.controlflow.internal.GuardsLogic
private import semmle.code.java.security.Random
private import SignAnalysis
private import ModulusAnalysis
private import semmle.code.java.Reflection
@@ -486,14 +487,17 @@ private predicate boundFlowStep(Expr e2, Expr e1, int delta, boolean upper) {
or
e2.(AssignOrExpr).getSource() = e1 and positive(e2) and delta = 0 and upper = false
or
exists(MethodAccess ma, Method m |
e2 = ma and
ma.getMethod() = m and
m.hasName("nextInt") and
m.getDeclaringType().hasQualifiedName("java.util", "Random") and
e1 = ma.getAnArgument() and
delta = -1 and
upper = true
exists(RandomDataSource rds |
e2 = rds.getOutput() and
(
e1 = rds.getUpperBoundExpr() and
delta = -1 and
upper = true
or
e1 = rds.getLowerBoundExpr() and
delta = 0 and
upper = false
)
)
or
exists(MethodAccess ma, Method m |

View File

@@ -2,15 +2,164 @@ import java
import semmle.code.java.dataflow.DefUse
import semmle.code.java.dataflow.DataFlow
class SecureRandomNumberGenerator extends RefType {
/**
* A class with methods that generate random data.
*/
abstract class RandomNumberGenerator extends RefType { }
/**
* The `java.security.SecureRandom` class.
*/
class SecureRandomNumberGenerator extends RandomNumberGenerator {
SecureRandomNumberGenerator() { this.hasQualifiedName("java.security", "SecureRandom") }
}
class GetRandomData extends MethodAccess {
GetRandomData() {
this.getMethod().getName().matches("next%") and
this.getQualifier().getType() instanceof SecureRandomNumberGenerator
/**
* The `java.util.Random` class or any of its subtypes, including `java.security.SecureRandom`.
*/
class StdlibRandom extends RandomNumberGenerator {
StdlibRandom() { this.getAnAncestor().hasQualifiedName("java.util", "Random") }
}
/**
* The `org.apache.commons.lang3.RandomUtils` class.
*/
class ApacheRandomUtils extends RandomNumberGenerator {
ApacheRandomUtils() { this.hasQualifiedName("org.apache.commons.lang3", "RandomUtils") }
}
/**
* A method access that returns random data or writes random data to an argument.
*/
abstract class RandomDataSource extends MethodAccess {
RandomDataSource() {
exists(Method m | m = this.getMethod() |
m.getName().matches("next%") and
m.getDeclaringType() instanceof RandomNumberGenerator
)
}
/**
* Gets the integer lower bound, inclusive, of the values returned by this call,
* if applicable to this method's type and a constant bound is known.
*/
int getLowerBound() { result = this.getLowerBoundExpr().(CompileTimeConstantExpr).getIntValue() }
/**
* Gets the integer lower bound, inclusive, of the values returned by this call,
* if applicable to this method's type and a constant bound is known.
*/
Expr getLowerBoundExpr() { none() }
/**
* Gets the integer upper bound, exclusive, of the values returned by this call,
* if applicable to this method's type and a constant bound is known.
*/
int getUpperBound() { result = this.getUpperBoundExpr().(CompileTimeConstantExpr).getIntValue() }
/**
* Gets the integer upper bound, exclusive, of the values returned by this call,
* if applicable to this method's type and a constant bound is known.
*/
Expr getUpperBoundExpr() { none() }
/**
* Holds if this source of random data may return bounded values (e.g. integers between 1 and 10).
* If it does not hold, it may return any value in the range of its result type (e.g., any possible integer).
*/
predicate resultMayBeBounded() { none() }
/**
* Gets the result of this source of randomness: either the method access itself, or some argument
* in the case where it writes random data to that argument.
*/
abstract Expr getOutput();
}
/**
* A method access calling a method declared on `java.util.Random`
* that returns random data or writes random data to an argument.
*/
class StdlibRandomSource extends RandomDataSource {
Method m;
StdlibRandomSource() {
m = this.getMethod() and
m.getDeclaringType() instanceof StdlibRandom
}
override int getLowerBound() {
// If this call is to `nextInt(int)`, the lower bound is zero.
m.hasName("nextInt") and
m.getNumberOfParameters() = 1 and
result = 0
}
override Expr getUpperBoundExpr() {
// If this call is to `nextInt(int)`, the upper bound is the first argument.
m.hasName("nextInt") and
m.getNumberOfParameters() = 1 and
result = this.getArgument(0)
}
override predicate resultMayBeBounded() {
// `next` may be restricted by its `bits` argument,
// `nextBoolean` can't possibly be usefully bounded,
// `nextDouble` and `nextFloat` are between 0 and 1,
// `nextGaussian` is extremely unlikely to hit max values.
m.hasName(["next", "nextBoolean", "nextDouble", "nextFloat", "nextGaussian"])
or
m.hasName(["nextInt", "nextLong"]) and
m.getNumberOfParameters() = 1
}
override Expr getOutput() {
if m.hasName("getBytes") then result = this.getArgument(0) else result = this
}
}
/**
* A method access calling a method declared on `org.apache.commons.lang3.RandomUtils`
* that returns random data or writes random data to an argument.
*/
class ApacheCommonsRandomSource extends RandomDataSource {
Method m;
ApacheCommonsRandomSource() {
m = this.getMethod() and
m.getDeclaringType() instanceof ApacheRandomUtils
}
override Expr getLowerBoundExpr() {
// If this call is to `nextInt(int, int)` or `nextLong(long, long)`, the lower bound is the first argument.
m.hasName(["nextInt", "nextLong"]) and
m.getNumberOfParameters() = 2 and
result = this.getArgument(0)
}
override Expr getUpperBoundExpr() {
// If this call is to `nextInt(int, int)` or `nextLong(long, long)`, the upper bound is the second argument.
m.hasName(["nextInt", "nextLong"]) and
m.getNumberOfParameters() = 2 and
result = this.getArgument(1)
}
override predicate resultMayBeBounded() {
m.hasName(["nextDouble", "nextFloat"])
or
m.hasName(["nextInt", "nextLong"]) and
m.getNumberOfParameters() = 2
}
override Expr getOutput() { result = this }
}
/**
* A method access calling a method declared on `java.security.SecureRandom`
* that returns random data or writes random data to an argument.
*/
class GetRandomData extends StdlibRandomSource {
GetRandomData() { this.getQualifier().getType() instanceof SecureRandomNumberGenerator }
}
private predicate isSeeded(RValue use) {

View File

@@ -0,0 +1,4 @@
| Test.java:9:5:9:25 | abs(...) | Incorrect computation of abs of signed integral random value. |
| Test.java:10:5:10:26 | abs(...) | Incorrect computation of abs of signed integral random value. |
| Test.java:13:5:13:35 | abs(...) | Incorrect computation of abs of signed integral random value. |
| Test.java:14:5:14:36 | abs(...) | Incorrect computation of abs of signed integral random value. |

View File

@@ -0,0 +1 @@
Likely Bugs/Arithmetic/BadAbsOfRandom.ql

View File

@@ -0,0 +1,20 @@
import java.util.Random;
import org.apache.commons.lang3.RandomUtils;
public class Test {
public static void test() {
Random r = new Random();
Math.abs(r.nextInt());
Math.abs(r.nextLong());
Math.abs(r.nextInt(100)); // GOOD: random value already has a restricted range
Math.abs(RandomUtils.nextInt());
Math.abs(RandomUtils.nextLong());
Math.abs(RandomUtils.nextInt(1, 10)); // GOOD: random value already has a restricted range
Math.abs(RandomUtils.nextLong(1, 10)); // GOOD: random value already has a restricted range
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/apache-commons-lang3-3.7

View File

@@ -0,0 +1 @@
| Test.java:7:5:7:28 | nextInt(...) | Random object created and used only once. |

View File

@@ -0,0 +1 @@
Likely Bugs/Arithmetic/RandomUsedOnce.ql

View File

@@ -0,0 +1,11 @@
import java.util.Random;
public class Test {
public static void test() {
(new Random()).nextInt();
}
}

View File

@@ -1,3 +1,6 @@
import java.util.Random;
import org.apache.commons.lang3.RandomUtils;
public class A {
private static final int[] arr1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
private final int[] arr2;
@@ -194,4 +197,11 @@ public class A {
}
}
}
static int m16() {
return A.arr1[(new Random()).nextInt(arr1.length + 1)] + // BAD: random int may be out of range
A.arr1[(new Random()).nextInt(arr1.length)] + // GOOD: random int must be in range
A.arr1[RandomUtils.nextInt(0, arr1.length + 1)] + // BAD: random int may be out of range
A.arr1[RandomUtils.nextInt(0, arr1.length)]; // GOOD: random int must be in range
}
}

View File

@@ -1,14 +1,16 @@
| A.java:16:14:16:17 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:23:21:23:28 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:42:14:42:22 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:46:14:46:22 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:55:14:55:19 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:64:14:64:19 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:86:12:86:16 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:97:18:97:31 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 8. |
| A.java:110:14:110:21 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:111:14:111:21 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 1. |
| A.java:122:16:122:23 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 3. |
| A.java:134:16:134:23 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:182:9:182:13 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:192:9:192:13 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:19:14:19:17 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:26:21:26:28 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:45:14:45:22 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:49:14:49:22 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:58:14:58:19 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:67:14:67:19 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:89:12:89:16 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:100:18:100:31 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 8. |
| A.java:113:14:113:21 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:114:14:114:21 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 1. |
| A.java:125:16:125:23 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 3. |
| A.java:137:16:137:23 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:185:9:185:13 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:195:9:195:13 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:202:12:202:58 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |
| A.java:204:7:204:53 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. |

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/apache-commons-lang3-3.7

View File

@@ -1,7 +1,7 @@
edges
| Test.java:86:16:86:16 | 0 : Number | Test.java:88:27:88:30 | size |
| Test.java:105:16:105:16 | 0 : Number | Test.java:107:27:107:30 | size |
nodes
| Test.java:86:16:86:16 | 0 : Number | semmle.label | 0 : Number |
| Test.java:88:27:88:30 | size | semmle.label | size |
| Test.java:105:16:105:16 | 0 : Number | semmle.label | 0 : Number |
| Test.java:107:27:107:30 | size | semmle.label | size |
#select
| Test.java:91:30:91:30 | 0 | Test.java:86:16:86:16 | 0 : Number | Test.java:88:27:88:30 | size | The $@ is accessed here, but the array is initialized using $@ which may be zero. | Test.java:88:19:88:31 | new int[] | array | Test.java:86:16:86:16 | 0 | literal value 0 |
| Test.java:110:30:110:30 | 0 | Test.java:105:16:105:16 | 0 : Number | Test.java:107:27:107:30 | size | The $@ is accessed here, but the array is initialized using $@ which may be zero. | Test.java:107:19:107:31 | new int[] | array | Test.java:105:16:105:16 | 0 | literal value 0 |

View File

@@ -1,10 +1,10 @@
edges
| Test.java:57:27:57:60 | getProperty(...) : String | Test.java:61:31:61:34 | size |
| Test.java:57:27:57:60 | getProperty(...) : String | Test.java:67:34:67:37 | size |
| Test.java:76:27:76:60 | getProperty(...) : String | Test.java:80:31:80:34 | size |
| Test.java:76:27:76:60 | getProperty(...) : String | Test.java:86:34:86:37 | size |
nodes
| Test.java:57:27:57:60 | getProperty(...) : String | semmle.label | getProperty(...) : String |
| Test.java:61:31:61:34 | size | semmle.label | size |
| Test.java:67:34:67:37 | size | semmle.label | size |
| Test.java:76:27:76:60 | getProperty(...) : String | semmle.label | getProperty(...) : String |
| Test.java:80:31:80:34 | size | semmle.label | size |
| Test.java:86:34:86:37 | size | semmle.label | size |
#select
| Test.java:64:34:64:34 | 0 | Test.java:57:27:57:60 | getProperty(...) : String | Test.java:61:31:61:34 | size | The $@ is accessed here, but the array is initialized using $@ which may be zero. | Test.java:61:23:61:35 | new int[] | array | Test.java:57:27:57:60 | getProperty(...) | User-provided value |
| Test.java:70:37:70:37 | 0 | Test.java:57:27:57:60 | getProperty(...) : String | Test.java:67:34:67:37 | size | The $@ is accessed here, but the array is initialized using $@ which may be zero. | Test.java:67:26:67:38 | new int[] | array | Test.java:57:27:57:60 | getProperty(...) | User-provided value |
| Test.java:83:34:83:34 | 0 | Test.java:76:27:76:60 | getProperty(...) : String | Test.java:80:31:80:34 | size | The $@ is accessed here, but the array is initialized using $@ which may be zero. | Test.java:80:23:80:35 | new int[] | array | Test.java:76:27:76:60 | getProperty(...) | User-provided value |
| Test.java:89:37:89:37 | 0 | Test.java:76:27:76:60 | getProperty(...) : String | Test.java:86:34:86:37 | size | The $@ is accessed here, but the array is initialized using $@ which may be zero. | Test.java:86:26:86:38 | new int[] | array | Test.java:76:27:76:60 | getProperty(...) | User-provided value |

View File

@@ -1,19 +1,29 @@
edges
| Test.java:40:17:40:48 | nextInt(...) : Number | Test.java:43:30:43:34 | index |
| Test.java:40:17:40:48 | nextInt(...) : Number | Test.java:47:32:47:36 | index |
| Test.java:40:17:40:48 | nextInt(...) : Number | Test.java:51:39:51:43 | index |
| Test.java:93:17:93:17 | 0 : Number | Test.java:96:32:96:36 | index |
| ../../../../../stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/RandomUtils.java:34:14:34:14 | 0 : Number | Test.java:59:17:59:42 | nextInt(...) : Number |
| Test.java:41:17:41:48 | nextInt(...) : Number | Test.java:44:30:44:34 | index |
| Test.java:41:17:41:48 | nextInt(...) : Number | Test.java:48:32:48:36 | index |
| Test.java:41:17:41:48 | nextInt(...) : Number | Test.java:52:39:52:43 | index |
| Test.java:59:17:59:42 | nextInt(...) : Number | Test.java:62:30:62:34 | index |
| Test.java:59:17:59:42 | nextInt(...) : Number | Test.java:66:32:66:36 | index |
| Test.java:59:17:59:42 | nextInt(...) : Number | Test.java:70:39:70:43 | index |
| Test.java:112:17:112:17 | 0 : Number | Test.java:115:32:115:36 | index |
nodes
| Test.java:40:17:40:48 | nextInt(...) : Number | semmle.label | nextInt(...) : Number |
| Test.java:43:30:43:34 | index | semmle.label | index |
| Test.java:47:32:47:36 | index | semmle.label | index |
| Test.java:51:39:51:43 | index | semmle.label | index |
| Test.java:64:34:64:34 | 0 | semmle.label | 0 |
| Test.java:70:37:70:37 | 0 | semmle.label | 0 |
| Test.java:77:39:77:39 | 0 | semmle.label | 0 |
| Test.java:91:30:91:30 | 0 | semmle.label | 0 |
| Test.java:93:17:93:17 | 0 : Number | semmle.label | 0 : Number |
| Test.java:96:32:96:36 | index | semmle.label | index |
| Test.java:102:30:102:30 | 0 | semmle.label | 0 |
| ../../../../../stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/RandomUtils.java:34:14:34:14 | 0 : Number | semmle.label | 0 : Number |
| Test.java:41:17:41:48 | nextInt(...) : Number | semmle.label | nextInt(...) : Number |
| Test.java:44:30:44:34 | index | semmle.label | index |
| Test.java:48:32:48:36 | index | semmle.label | index |
| Test.java:52:39:52:43 | index | semmle.label | index |
| Test.java:59:17:59:42 | nextInt(...) : Number | semmle.label | nextInt(...) : Number |
| Test.java:62:30:62:34 | index | semmle.label | index |
| Test.java:66:32:66:36 | index | semmle.label | index |
| Test.java:70:39:70:43 | index | semmle.label | index |
| Test.java:83:34:83:34 | 0 | semmle.label | 0 |
| Test.java:89:37:89:37 | 0 | semmle.label | 0 |
| Test.java:96:39:96:39 | 0 | semmle.label | 0 |
| Test.java:110:30:110:30 | 0 | semmle.label | 0 |
| Test.java:112:17:112:17 | 0 : Number | semmle.label | 0 : Number |
| Test.java:115:32:115:36 | index | semmle.label | index |
| Test.java:121:30:121:30 | 0 | semmle.label | 0 |
#select
| Test.java:43:30:43:34 | index | Test.java:40:17:40:48 | nextInt(...) : Number | Test.java:43:30:43:34 | index | $@ flows to the index used in this array access, and may cause the operation to throw an ArrayIndexOutOfBoundsException. | Test.java:40:17:40:48 | nextInt(...) | Random value |
| Test.java:44:30:44:34 | index | Test.java:41:17:41:48 | nextInt(...) : Number | Test.java:44:30:44:34 | index | $@ flows to the index used in this array access, and may cause the operation to throw an ArrayIndexOutOfBoundsException. | Test.java:41:17:41:48 | nextInt(...) | Random value |
| Test.java:62:30:62:34 | index | Test.java:59:17:59:42 | nextInt(...) : Number | Test.java:62:30:62:34 | index | $@ flows to the index used in this array access, and may cause the operation to throw an ArrayIndexOutOfBoundsException. | Test.java:59:17:59:42 | nextInt(...) | Random value |

View File

@@ -1,7 +1,7 @@
edges
| Test.java:13:27:13:60 | getProperty(...) : String | Test.java:18:34:18:38 | index |
| Test.java:14:27:14:60 | getProperty(...) : String | Test.java:19:34:19:38 | index |
nodes
| Test.java:13:27:13:60 | getProperty(...) : String | semmle.label | getProperty(...) : String |
| Test.java:18:34:18:38 | index | semmle.label | index |
| Test.java:14:27:14:60 | getProperty(...) : String | semmle.label | getProperty(...) : String |
| Test.java:19:34:19:38 | index | semmle.label | index |
#select
| Test.java:18:34:18:38 | index | Test.java:13:27:13:60 | getProperty(...) : String | Test.java:18:34:18:38 | index | $@ flows to here and is used as an index causing an ArrayIndexOutOfBoundsException. | Test.java:13:27:13:60 | getProperty(...) | User-provided value |
| Test.java:19:34:19:38 | index | Test.java:14:27:14:60 | getProperty(...) : String | Test.java:19:34:19:38 | index | $@ flows to here and is used as an index causing an ArrayIndexOutOfBoundsException. | Test.java:14:27:14:60 | getProperty(...) | User-provided value |

View File

@@ -5,6 +5,7 @@
package test.cwe129.cwe.examples;
import java.security.SecureRandom;
import org.apache.commons.lang3.RandomUtils;
class Test {
public static void basic() {
@@ -52,6 +53,24 @@ class Test {
}
}
public static void apacheRandom() {
int array[] = { 0, 1, 2, 3, 4 };
int index = RandomUtils.nextInt(0, 10);
// BAD Accessing array without conditional check
System.out.println(array[index]);
if (index < array.length) {
// GOOD Accessing array under conditions
System.out.println(array[index]);
}
// GOOD, the array access is protected by short-circuiting
if (index < array.length && array[index] > 0) {
}
}
public static void construction() {
String userProperty = System.getProperty("userProperty");
@@ -101,4 +120,4 @@ class Test {
// GOOD array size is guaranteed to be larger than zero
System.out.println(array[0]);
}
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7

View File

@@ -1,10 +1,17 @@
edges
| Test.java:205:14:205:57 | nextInt(...) : Number | Test.java:209:17:209:20 | data |
| Test.java:205:14:205:57 | nextInt(...) : Number | Test.java:240:37:240:40 | data |
| Test.java:206:14:206:57 | nextInt(...) : Number | Test.java:210:17:210:20 | data |
| Test.java:206:14:206:57 | nextInt(...) : Number | Test.java:241:37:241:40 | data |
| Test.java:245:15:245:35 | nextInt(...) : Number | Test.java:249:17:249:21 | data2 |
| Test.java:245:15:245:35 | nextInt(...) : Number | Test.java:280:37:280:41 | data2 |
nodes
| Test.java:205:14:205:57 | nextInt(...) : Number | semmle.label | nextInt(...) : Number |
| Test.java:209:17:209:20 | data | semmle.label | data |
| Test.java:240:37:240:40 | data | semmle.label | data |
| Test.java:206:14:206:57 | nextInt(...) : Number | semmle.label | nextInt(...) : Number |
| Test.java:210:17:210:20 | data | semmle.label | data |
| Test.java:241:37:241:40 | data | semmle.label | data |
| Test.java:245:15:245:35 | nextInt(...) : Number | semmle.label | nextInt(...) : Number |
| Test.java:249:17:249:21 | data2 | semmle.label | data2 |
| Test.java:280:37:280:41 | data2 | semmle.label | data2 |
#select
| Test.java:209:17:209:24 | ... + ... | Test.java:205:14:205:57 | nextInt(...) : Number | Test.java:209:17:209:20 | data | $@ flows to here and is used in arithmetic, potentially causing an overflow. | Test.java:205:14:205:57 | nextInt(...) | Uncontrolled value |
| Test.java:240:37:240:46 | ... + ... | Test.java:205:14:205:57 | nextInt(...) : Number | Test.java:240:37:240:40 | data | $@ flows to here and is used in arithmetic, potentially causing an overflow. | Test.java:205:14:205:57 | nextInt(...) | Uncontrolled value |
| Test.java:210:17:210:24 | ... + ... | Test.java:206:14:206:57 | nextInt(...) : Number | Test.java:210:17:210:20 | data | $@ flows to here and is used in arithmetic, potentially causing an overflow. | Test.java:206:14:206:57 | nextInt(...) | Uncontrolled value |
| Test.java:241:37:241:46 | ... + ... | Test.java:206:14:206:57 | nextInt(...) : Number | Test.java:241:37:241:40 | data | $@ flows to here and is used in arithmetic, potentially causing an overflow. | Test.java:206:14:206:57 | nextInt(...) | Uncontrolled value |
| Test.java:249:17:249:25 | ... + ... | Test.java:245:15:245:35 | nextInt(...) : Number | Test.java:249:17:249:21 | data2 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | Test.java:245:15:245:35 | nextInt(...) | Uncontrolled value |
| Test.java:280:37:280:47 | ... + ... | Test.java:245:15:245:35 | nextInt(...) : Number | Test.java:280:37:280:41 | data2 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | Test.java:245:15:245:35 | nextInt(...) | Uncontrolled value |

View File

@@ -1,31 +1,31 @@
edges
| Test.java:92:8:92:24 | Integer.MAX_VALUE : Number | Test.java:95:8:95:8 | i |
| Test.java:108:13:108:26 | Long.MIN_VALUE : Number | Test.java:110:13:110:13 | i |
| Test.java:137:9:137:25 | Integer.MAX_VALUE : Number | Test.java:138:14:138:14 | i |
| Test.java:143:12:143:28 | Integer.MAX_VALUE : Number | Test.java:146:14:146:14 | i |
| Test.java:184:13:184:26 | Byte.MAX_VALUE : Number | Test.java:187:39:187:39 | b |
| Test.java:191:14:191:28 | Short.MAX_VALUE : Number | Test.java:194:41:194:41 | s |
| Test.java:198:12:198:28 | Integer.MAX_VALUE : Number | Test.java:201:37:201:37 | i |
| Test.java:93:8:93:24 | Integer.MAX_VALUE : Number | Test.java:96:8:96:8 | i |
| Test.java:109:13:109:26 | Long.MIN_VALUE : Number | Test.java:111:13:111:13 | i |
| Test.java:138:9:138:25 | Integer.MAX_VALUE : Number | Test.java:139:14:139:14 | i |
| Test.java:144:12:144:28 | Integer.MAX_VALUE : Number | Test.java:147:14:147:14 | i |
| Test.java:185:13:185:26 | Byte.MAX_VALUE : Number | Test.java:188:39:188:39 | b |
| Test.java:192:14:192:28 | Short.MAX_VALUE : Number | Test.java:195:41:195:41 | s |
| Test.java:199:12:199:28 | Integer.MAX_VALUE : Number | Test.java:202:37:202:37 | i |
nodes
| Test.java:92:8:92:24 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:95:8:95:8 | i | semmle.label | i |
| Test.java:108:13:108:26 | Long.MIN_VALUE : Number | semmle.label | Long.MIN_VALUE : Number |
| Test.java:110:13:110:13 | i | semmle.label | i |
| Test.java:137:9:137:25 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:138:14:138:14 | i | semmle.label | i |
| Test.java:143:12:143:28 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:146:14:146:14 | i | semmle.label | i |
| Test.java:184:13:184:26 | Byte.MAX_VALUE : Number | semmle.label | Byte.MAX_VALUE : Number |
| Test.java:187:39:187:39 | b | semmle.label | b |
| Test.java:191:14:191:28 | Short.MAX_VALUE : Number | semmle.label | Short.MAX_VALUE : Number |
| Test.java:194:41:194:41 | s | semmle.label | s |
| Test.java:198:12:198:28 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:201:37:201:37 | i | semmle.label | i |
| Test.java:93:8:93:24 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:96:8:96:8 | i | semmle.label | i |
| Test.java:109:13:109:26 | Long.MIN_VALUE : Number | semmle.label | Long.MIN_VALUE : Number |
| Test.java:111:13:111:13 | i | semmle.label | i |
| Test.java:138:9:138:25 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:139:14:139:14 | i | semmle.label | i |
| Test.java:144:12:144:28 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:147:14:147:14 | i | semmle.label | i |
| Test.java:185:13:185:26 | Byte.MAX_VALUE : Number | semmle.label | Byte.MAX_VALUE : Number |
| Test.java:188:39:188:39 | b | semmle.label | b |
| Test.java:192:14:192:28 | Short.MAX_VALUE : Number | semmle.label | Short.MAX_VALUE : Number |
| Test.java:195:41:195:41 | s | semmle.label | s |
| Test.java:199:12:199:28 | Integer.MAX_VALUE : Number | semmle.label | Integer.MAX_VALUE : Number |
| Test.java:202:37:202:37 | i | semmle.label | i |
#select
| Test.java:95:8:95:12 | ... + ... | Test.java:92:8:92:24 | Integer.MAX_VALUE : Number | Test.java:95:8:95:8 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:92:8:92:24 | Integer.MAX_VALUE | MAX_VALUE |
| Test.java:110:13:110:17 | ... - ... | Test.java:108:13:108:26 | Long.MIN_VALUE : Number | Test.java:110:13:110:13 | i | Variable i is assigned an extreme value $@, and may cause an underflow. | Test.java:108:13:108:26 | Long.MIN_VALUE | MIN_VALUE |
| Test.java:138:14:138:18 | ... + ... | Test.java:137:9:137:25 | Integer.MAX_VALUE : Number | Test.java:138:14:138:14 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:137:9:137:25 | Integer.MAX_VALUE | MAX_VALUE |
| Test.java:146:14:146:18 | ... + ... | Test.java:143:12:143:28 | Integer.MAX_VALUE : Number | Test.java:146:14:146:14 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:143:12:143:28 | Integer.MAX_VALUE | MAX_VALUE |
| Test.java:187:39:187:43 | ... + ... | Test.java:184:13:184:26 | Byte.MAX_VALUE : Number | Test.java:187:39:187:39 | b | Variable b is assigned an extreme value $@, and may cause an overflow. | Test.java:184:13:184:26 | Byte.MAX_VALUE | MAX_VALUE |
| Test.java:194:41:194:45 | ... + ... | Test.java:191:14:191:28 | Short.MAX_VALUE : Number | Test.java:194:41:194:41 | s | Variable s is assigned an extreme value $@, and may cause an overflow. | Test.java:191:14:191:28 | Short.MAX_VALUE | MAX_VALUE |
| Test.java:201:37:201:42 | ... + ... | Test.java:198:12:198:28 | Integer.MAX_VALUE : Number | Test.java:201:37:201:37 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:198:12:198:28 | Integer.MAX_VALUE | MAX_VALUE |
| Test.java:96:8:96:12 | ... + ... | Test.java:93:8:93:24 | Integer.MAX_VALUE : Number | Test.java:96:8:96:8 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:93:8:93:24 | Integer.MAX_VALUE | MAX_VALUE |
| Test.java:111:13:111:17 | ... - ... | Test.java:109:13:109:26 | Long.MIN_VALUE : Number | Test.java:111:13:111:13 | i | Variable i is assigned an extreme value $@, and may cause an underflow. | Test.java:109:13:109:26 | Long.MIN_VALUE | MIN_VALUE |
| Test.java:139:14:139:18 | ... + ... | Test.java:138:9:138:25 | Integer.MAX_VALUE : Number | Test.java:139:14:139:14 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:138:9:138:25 | Integer.MAX_VALUE | MAX_VALUE |
| Test.java:147:14:147:18 | ... + ... | Test.java:144:12:144:28 | Integer.MAX_VALUE : Number | Test.java:147:14:147:14 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:144:12:144:28 | Integer.MAX_VALUE | MAX_VALUE |
| Test.java:188:39:188:43 | ... + ... | Test.java:185:13:185:26 | Byte.MAX_VALUE : Number | Test.java:188:39:188:39 | b | Variable b is assigned an extreme value $@, and may cause an overflow. | Test.java:185:13:185:26 | Byte.MAX_VALUE | MAX_VALUE |
| Test.java:195:41:195:45 | ... + ... | Test.java:192:14:192:28 | Short.MAX_VALUE : Number | Test.java:195:41:195:41 | s | Variable s is assigned an extreme value $@, and may cause an overflow. | Test.java:192:14:192:28 | Short.MAX_VALUE | MAX_VALUE |
| Test.java:202:37:202:42 | ... + ... | Test.java:199:12:199:28 | Integer.MAX_VALUE : Number | Test.java:202:37:202:37 | i | Variable i is assigned an extreme value $@, and may cause an overflow. | Test.java:199:12:199:28 | Integer.MAX_VALUE | MAX_VALUE |

View File

@@ -1,2 +1,2 @@
| Test.java:67:5:67:25 | ...+=... | Implicit cast of source type long to narrower destination type int. |
| Test.java:86:4:86:9 | ...+=... | Implicit cast of source type long to narrower destination type int. |
| Test.java:68:5:68:25 | ...+=... | Implicit cast of source type long to narrower destination type int. |
| Test.java:87:4:87:9 | ...+=... | Implicit cast of source type long to narrower destination type int. |

View File

@@ -1,3 +1,3 @@
| Test.java:20:23:20:48 | ... * ... | Potential overflow in $@ before it is converted to long by use in an assignment context. | Test.java:20:23:20:48 | ... * ... | int multiplication |
| Test.java:27:23:27:52 | ... + ... | Potential overflow in $@ before it is converted to long by use in an assignment context. | Test.java:27:23:27:48 | ... * ... | int multiplication |
| Test.java:34:23:34:63 | ...?...:... | Potential overflow in $@ before it is converted to long by use in an assignment context. | Test.java:34:30:34:55 | ... * ... | int multiplication |
| Test.java:21:23:21:48 | ... * ... | Potential overflow in $@ before it is converted to long by use in an assignment context. | Test.java:21:23:21:48 | ... * ... | int multiplication |
| Test.java:28:23:28:52 | ... + ... | Potential overflow in $@ before it is converted to long by use in an assignment context. | Test.java:28:23:28:48 | ... * ... | int multiplication |
| Test.java:35:23:35:63 | ...?...:... | Potential overflow in $@ before it is converted to long by use in an assignment context. | Test.java:35:30:35:55 | ... * ... | int multiplication |

View File

@@ -7,6 +7,7 @@ import java.io.InputStreamReader;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.HashMap;
import org.apache.commons.lang3.RandomUtils;
class Test {
public static void main(String[] args) {
@@ -239,6 +240,45 @@ class Test {
// subsequently cast to narrower type int
int widenedThenNarrowed = (int) (data + 10L);
}
// ArithmeticUncontrolled using Apache RandomUtils
int data2 = RandomUtils.nextInt();
{
// BAD: may overflow if data is large
int output = data2 + 1;
}
{
// GOOD: guarded
if (data2 < Integer.MAX_VALUE) {
int output = data2 + 1;
}
}
{
// guard against underflow
if (data2 > Integer.MIN_VALUE) {
int stillLarge = data2 - 1;
// FALSE NEGATIVE: stillLarge could still be very large, even
// after
// it has had arithmetic done on it
int output = stillLarge + 100;
}
}
{
// GOOD: uncontrolled int value is widened to type long, thus
// avoiding overflow
// (see binary numeric promotions in JLS 5.6.2)
long widened = data2 + 10L;
}
{
// BAD: uncontrolled int value is widened to type long, but
// subsequently cast to narrower type int
int widenedThenNarrowed = (int) (data2 + 10L);
}
}
public static long getLargeNumber() {

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7

View File

@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3;
import java.util.Random;
public class RandomUtils {
public RandomUtils() {
}
public static boolean nextBoolean() {
return false;
}
public static byte[] nextBytes(final int count) {
return null;
}
public static int nextInt(final int startInclusive, final int endExclusive) {
return 0;
}
public static int nextInt() {
return 0;
}
public static long nextLong(final long startInclusive, final long endExclusive) {
return 0;
}
public static long nextLong() {
return 0;
}
public static double nextDouble(final double startInclusive, final double endExclusive) {
return 0;
}
public static double nextDouble() {
return 0;
}
public static float nextFloat(final float startInclusive, final float endExclusive) {
return 0;
}
public static float nextFloat() {
return 0;
}
}