Skip to content

Commit 4a4f4b0

Browse files
committed
Add support for java.util.concurrent.ThreadLocalRandom
1 parent e3cf5c2 commit 4a4f4b0

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

java/ql/src/semmle/code/java/security/Random.qll

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,39 @@ class StdlibRandomSource extends RandomDataSource {
8888
m.getDeclaringType() instanceof StdlibRandom
8989
}
9090

91+
// Note for the following bounds functions: `java.util.Random` only defines no-arg versions
92+
// of `nextInt` and `nextLong` plus `nextInt(int x)`, bounded to the range [0, x)
93+
// However `ThreadLocalRandom` provides one- and two-arg versions of `nextInt` and `nextLong`
94+
// which allow both lower and upper bounds for both types.
9195
override int getLowerBound() {
92-
// If this call is to `nextInt(int)`, the lower bound is zero.
93-
m.hasName("nextInt") and
96+
// If this call is to `nextInt(int)` or `nextLong(long), the lower bound is zero.
97+
m.hasName(["nextInt", "nextLong"]) and
9498
m.getNumberOfParameters() = 1 and
9599
result = 0
100+
or
101+
result = super.getLowerBound() // Include a lower bound provided via `getLowerBoundExpr`
96102
}
97103

98-
override Expr getUpperBoundExpr() {
99-
// If this call is to `nextInt(int)`, the upper bound is the first argument.
100-
m.hasName("nextInt") and
101-
m.getNumberOfParameters() = 1 and
104+
override Expr getLowerBoundExpr() {
105+
// If this call is to `nextInt(int, int)` or `nextLong(long, long)`, the lower bound is the first argument.
106+
m.hasName(["nextInt", "nextLong"]) and
107+
m.getNumberOfParameters() = 2 and
102108
result = this.getArgument(0)
103109
}
104110

111+
override Expr getUpperBoundExpr() {
112+
// If this call is to `nextInt(int)` or `nextLong(long)`, the upper bound is the first argument.
113+
// If it calls `nextInt(int, int)` or `nextLong(long, long)`, the upper bound is the first argument.
114+
m.hasName(["nextInt", "nextLong"]) and
115+
(
116+
m.getNumberOfParameters() = 1 and
117+
result = this.getArgument(0)
118+
or
119+
m.getNumberOfParameters() = 2 and
120+
result = this.getArgument(1)
121+
)
122+
}
123+
105124
override predicate resultMayBeBounded() {
106125
// `next` may be restricted by its `bits` argument,
107126
// `nextBoolean` can't possibly be usefully bounded,
@@ -110,7 +129,7 @@ class StdlibRandomSource extends RandomDataSource {
110129
m.hasName(["next", "nextBoolean", "nextDouble", "nextFloat", "nextGaussian"])
111130
or
112131
m.hasName(["nextInt", "nextLong"]) and
113-
m.getNumberOfParameters() = 1
132+
m.getNumberOfParameters() = [1, 2]
114133
}
115134

116135
override Expr getOutput() {
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
| Test.java:9:5:9:25 | abs(...) | Incorrect computation of abs of signed integral random value. |
2-
| Test.java:10:5:10:26 | abs(...) | Incorrect computation of abs of signed integral random value. |
3-
| Test.java:13:5:13:35 | abs(...) | Incorrect computation of abs of signed integral random value. |
4-
| Test.java:14:5:14:36 | abs(...) | Incorrect computation of abs of signed integral random value. |
1+
| Test.java:10:5:10:25 | abs(...) | Incorrect computation of abs of signed integral random value. |
2+
| Test.java:11:5:11:26 | abs(...) | Incorrect computation of abs of signed integral random value. |
3+
| Test.java:14:5:14:35 | abs(...) | Incorrect computation of abs of signed integral random value. |
4+
| Test.java:15:5:15:36 | abs(...) | Incorrect computation of abs of signed integral random value. |
5+
| Test.java:20:5:20:27 | abs(...) | Incorrect computation of abs of signed integral random value. |
6+
| Test.java:21:5:21:28 | abs(...) | Incorrect computation of abs of signed integral random value. |

java/ql/test/query-tests/BadAbsOfRandom/Test.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import java.util.Random;
2+
import java.util.concurrent.ThreadLocalRandom;
23
import org.apache.commons.lang3.RandomUtils;
34

45
public class Test {
@@ -15,6 +16,14 @@ public static void test() {
1516
Math.abs(RandomUtils.nextInt(1, 10)); // GOOD: random value already has a restricted range
1617
Math.abs(RandomUtils.nextLong(1, 10)); // GOOD: random value already has a restricted range
1718

19+
ThreadLocalRandom tlr = ThreadLocalRandom.current();
20+
Math.abs(tlr.nextInt());
21+
Math.abs(tlr.nextLong());
22+
Math.abs(tlr.nextInt(10)); // GOOD: random value already has a restricted range
23+
Math.abs(tlr.nextLong(10)); // GOOD: random value already has a restricted range
24+
Math.abs(tlr.nextInt(1, 10)); // GOOD: random value already has a restricted range
25+
Math.abs(tlr.nextLong(1, 10)); // GOOD: random value already has a restricted range
26+
1827
}
1928

2029
}

0 commit comments

Comments
 (0)