Skip to content

Commit 97069a7

Browse files
authored
Merge pull request #2683 from aschackmull/java/lshift32
Java: Add new query for large left shifts and bugfix ConstantExpAppearsNonConstant.
2 parents 95d138b + dc7e8ad commit 97069a7

File tree

12 files changed

+157
-55
lines changed

12 files changed

+157
-55
lines changed

change-notes/1.24/analysis-java.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ The following changes in version 1.24 affect Java analysis in all applications.
1212
|-----------------------------|-----------|--------------------------------------------------------------------|
1313
| Disabled Spring CSRF protection (`java/spring-disabled-csrf-protection`) | security, external/cwe/cwe-352 | Finds disabled Cross-Site Request Forgery (CSRF) protection in Spring. |
1414
| Failure to use HTTPS or SFTP URL in Maven artifact upload/download (`java/maven/non-https-url`) | security, external/cwe/cwe-300, external/cwe/cwe-319, external/cwe/cwe-494, external/cwe/cwe-829 | Finds use of insecure protocols during Maven dependency resolution. Results are shown on LGTM by default. |
15+
| Left shift by more than the type width (`java/lshift-larger-than-type-width`) | correctness | Finds left shifts of ints by 32 bits or more and left shifts of longs by 64 bits or more. Results are shown on LGTM by default. |
1516
| Suspicious date format (`java/suspicious-date-format`) | correctness | Finds date format patterns that use placeholders that are likely to be incorrect. |
1617

1718
## Changes to existing queries
1819

1920
| **Query** | **Expected impact** | **Change** |
2021
|------------------------------|------------------------|-----------------------------------|
2122
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Final fields with a non-null initializer are no longer reported. |
22-
| Expression always evaluates to the same value (`java/evaluation-to-constant`) | Fewer false positives | Expressions of the form `0 * x` are usually intended and no longer reported. |
23+
| Expression always evaluates to the same value (`java/evaluation-to-constant`) | Fewer false positives | Expressions of the form `0 * x` are usually intended and no longer reported. Also left shift of ints by 32 bits and longs by 64 bits are no longer reported as they are not constant, these results are instead reported by the new query `java/lshift-larger-than-type-width`. |
2324
| Useless null check (`java/useless-null-check`) | More true positives | Useless checks on final fields with a non-null initializer are now reported. |
2425

2526
## Changes to libraries

java/ql/src/Likely Bugs/Arithmetic/ConstantExpAppearsNonConstant.qhelp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ Some expressions always evaluate to the same result, no matter what their subexp
1414
</li>
1515
<li><code>x % 1</code> always evaluates to <code>0</code>.
1616
</li>
17-
<li><code>x &lt;&lt; 64</code> (when <code>x</code> is of type <code>long</code>) always evaluates to <code>0</code>.
18-
</li>
19-
<li><code>x &lt;&lt; 32</code> (when <code>x</code> is not of type <code>long</code>) always evaluates to <code>0</code>.
20-
</li>
2117
<li><code>x &amp; 0</code> always evaluates to <code>0</code>.
2218
</li>
2319
<li><code>x || true</code> always evaluates to <code>true</code>.
@@ -58,7 +54,7 @@ an integer. The correct check is <code>x % 2 == 0</code>.
5854

5955
<li>
6056
The Java Language Specification:
61-
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.1">Multiplication operator *</a>, <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.3">Remainder operator %</a>, <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.19">Left shift operator &lt;&lt;</a>, <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.22.1">Bitwise AND operator &amp;</a>, <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.23">Conditional-and operator &amp;&amp;</a> and <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.24">Conditional-or operator ||</a>.
57+
<a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.1">Multiplication operator *</a>, <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.3">Remainder operator %</a>, <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.22.1">Bitwise AND operator &amp;</a>, <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.23">Conditional-and operator &amp;&amp;</a> and <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.24">Conditional-or operator ||</a>.
6258
</li>
6359

6460

java/ql/src/Likely Bugs/Arithmetic/ConstantExpAppearsNonConstant.ql

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111

1212
import java
1313

14-
int integralTypeWidth(IntegralType t) {
15-
if t.hasName("long") or t.hasName("Long") then result = 64 else result = 32
16-
}
17-
1814
int eval(Expr e) { result = e.(CompileTimeConstantExpr).getIntValue() }
1915

2016
predicate isConstantExp(Expr e) {
@@ -48,13 +44,6 @@ predicate isConstantExp(Expr e) {
4844
eval(r.getRightOperand().getProperExpr()) = 1
4945
)
5046
or
51-
// Left shift by 64 (longs) or 32 (all other integer types) is constant.
52-
exists(LShiftExpr s | s = e |
53-
exists(IntegralType t | t = s.getLeftOperand().getType() |
54-
eval(s.getRightOperand().getProperExpr()) = integralTypeWidth(t)
55-
)
56-
)
57-
or
5847
exists(AndBitwiseExpr a | a = e | eval(a.getAnOperand().getProperExpr()) = 0)
5948
or
6049
exists(AndLogicalExpr a | a = e |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
long longVal = intVal << 32; // BAD
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
7+
<overview>
8+
<p>
9+
The maximum shift distance used for left-shift operations is determined by the promoted type of
10+
its left-hand side. When the promoted type is <code>int</code> only the lowest 5 bits of the
11+
right-hand side are used as the shift distance. When the promoted type is <code>long</code>
12+
the lowest 6 bits of the right-hand side are used.
13+
</p>
14+
</overview>
15+
<recommendation>
16+
17+
<p>
18+
Restrict the amount that you shift any <code>int</code> to the range 0-31, or cast it to <code>long</code> before applying the left shift.
19+
</p>
20+
21+
</recommendation>
22+
<example>
23+
<p>
24+
The following line tries to left-shift an <code>int</code> by 32 bits.
25+
</p>
26+
27+
<sample src="LShiftLargerThanTypeWidth.java" />
28+
29+
<p>
30+
However, left-shifting an <code>int</code> by 32 bits is equivalent to
31+
left-shifting it by 0 bits, that is, no shift is applied. Instead the value should
32+
be cast to <code>long</code> before the shift is applied. Then the left-shift of 32 bits will work.
33+
</p>
34+
35+
<sample src="LShiftLargerThanTypeWidthGood.java" />
36+
37+
</example>
38+
<references>
39+
40+
41+
<li>
42+
The Java Language Specification:
43+
<a href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.19">Shift Operators</a>.
44+
</li>
45+
46+
47+
</references>
48+
</qhelp>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @name Left shift by more than the type width
3+
* @description Left-shifting an integer by more than its type width indicates a mistake.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @precision very-high
7+
* @id java/lshift-larger-than-type-width
8+
* @tags correctness
9+
*/
10+
11+
import java
12+
13+
int integralTypeWidth(IntegralType t) {
14+
if t.hasName("long") or t.hasName("Long") then result = 64 else result = 32
15+
}
16+
17+
from LShiftExpr shift, IntegralType t, int v, string typname, int width
18+
where
19+
shift.getLeftOperand().getType() = t and
20+
shift.getRightOperand().(CompileTimeConstantExpr).getIntValue() = v and
21+
width = integralTypeWidth(t) and
22+
v >= width and
23+
typname = ("a " + t.toString()).regexpReplaceAll("a ([aeiouAEIOU])", "an $1")
24+
select shift,
25+
"Left-shifting " + typname + " by more than " + width + " truncates the shift amount from " + v +
26+
" to " + (v % width)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
long longVal = ((long)intVal) << 32; // GOOD

java/ql/test/query-tests/ConstantExpAppearsNonConstant/ConstantExpAppearsNonConstant.expected

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,7 @@
55
| Test.java:27:28:27:44 | ... % ... | Expression always evaluates to the same value. |
66
| Test.java:29:29:29:48 | ... % ... | Expression always evaluates to the same value. |
77
| Test.java:30:32:30:50 | ... % ... | Expression always evaluates to the same value. |
8-
| Test.java:38:32:38:59 | ... << ... | Expression always evaluates to the same value. |
9-
| Test.java:41:32:41:59 | ... << ... | Expression always evaluates to the same value. |
10-
| Test.java:44:33:44:61 | ... << ... | Expression always evaluates to the same value. |
11-
| Test.java:50:31:50:49 | ... << ... | Expression always evaluates to the same value. |
12-
| Test.java:51:33:51:52 | ... << ... | Expression always evaluates to the same value. |
13-
| Test.java:52:35:52:55 | ... << ... | Expression always evaluates to the same value. |
14-
| Test.java:53:38:53:57 | ... << ... | Expression always evaluates to the same value. |
15-
| Test.java:54:37:54:58 | ... << ... | Expression always evaluates to the same value. |
16-
| Test.java:55:40:55:61 | ... << ... | Expression always evaluates to the same value. |
17-
| Test.java:62:30:62:46 | ... & ... | Expression always evaluates to the same value. |
18-
| Test.java:63:31:63:47 | ... & ... | Expression always evaluates to the same value. |
19-
| Test.java:78:32:78:58 | ... \|\| ... | Expression always evaluates to the same value. |
20-
| Test.java:79:33:79:59 | ... \|\| ... | Expression always evaluates to the same value. |
8+
| Test.java:37:30:37:46 | ... & ... | Expression always evaluates to the same value. |
9+
| Test.java:38:31:38:47 | ... & ... | Expression always evaluates to the same value. |
10+
| Test.java:53:32:53:58 | ... \|\| ... | Expression always evaluates to the same value. |
11+
| Test.java:54:33:54:59 | ... \|\| ... | Expression always evaluates to the same value. |

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

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,6 @@ public void tester(){
2929
long rem_is_constant_hex = rnd.nextLong() % 0x1; //NOT OK
3030
long rem_is_constant_binary = rnd.nextLong() % 01; //NOT OK
3131

32-
//Left shift by 32 or 64
33-
int lshift_not_constant_int = 42 << 6; //OK
34-
long lshift_not_constant_long = 42L << 6; //OK
35-
36-
int lshift_constant_cast = ((char) 42) << 16; //OK
37-
int lshift_not_constant_byte = ((byte)rnd.nextInt(8)) << 8; //OK (not constant, because of conversion from byte to int)
38-
int lshift_is_constant_byte = ((byte)rnd.nextInt(8)) << 32; //NOT OK
39-
40-
int lshift_not_constant_char = ((char)rnd.nextInt(8)) << 8; //OK (not constant, because of conversion from byte to int)
41-
int lshift_is_constant_char = ((char)rnd.nextInt(8)) << 32; //NOT OK
42-
43-
int lshift_not_constant_short = ((byte)rnd.nextInt(42)) << 16; //OK (not constant, because of conversion from byte to int)
44-
int lshift_is_constant_short = ((byte)rnd.nextInt(42)) << 32; //NOT OK
45-
46-
int lshift_appears_constant_int = 60 << 32; //OK
47-
long lshift_appears_constant_long = 60L << 64; //OK
48-
49-
int lshift_is_not_constant = rnd.nextInt() << 2; //OK
50-
int lshift_is_constant_int = rnd.nextInt() << 32; //NOT OK
51-
long lshift_is_constant_long = rnd.nextLong() << 64; //NOT OK
52-
int lshift_is_constant_int_hex = rnd.nextInt() << 0x20; //NOT OK
53-
int lshift_is_constant_int_binary = rnd.nextInt() << 040; //NOT OK
54-
long lshift_is_constant_long_hex = rnd.nextLong() << 0x40; //NOT OK
55-
long lshift_is_constant_long_binary = rnd.nextLong() << 0100; //NOT OK
56-
5732
//Bitwise 'and' by 0
5833
int band_not_constant = 42 & 6; //OK
5934
int band_appears_constant_left = 0 & 60; //OK
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
public class A {
2+
void test1(byte b, char c, short s, int i, long l) {
3+
long b1 = b << 31; // OK
4+
long b2 = b << 32; // BAD
5+
long b3 = b << 33; // BAD
6+
long b4 = b << 64; // BAD
7+
8+
long c1 = c << 22; // OK
9+
long c2 = c << 42; // BAD
10+
11+
long s1 = s << 22; // OK
12+
long s2 = s << 42; // BAD
13+
14+
long i1 = i << 22; // OK
15+
long i2 = i << 32; // BAD
16+
long i3 = i << 42; // BAD
17+
long i4 = i << 64; // BAD
18+
long i5 = i << 65; // BAD
19+
20+
long l1 = l << 22; // OK
21+
long l2 = l << 32; // OK
22+
long l3 = l << 42; // OK
23+
long l4 = l << 64; // BAD
24+
long l5 = l << 65; // BAD
25+
}
26+
27+
void test2(Byte b, Character c, Short s, Integer i, Long l) {
28+
long b1 = b << 31; // OK
29+
long b2 = b << 32; // BAD
30+
long b3 = b << 33; // BAD
31+
long b4 = b << 64; // BAD
32+
33+
long c1 = c << 22; // OK
34+
long c2 = c << 42; // BAD
35+
36+
long s1 = s << 22; // OK
37+
long s2 = s << 42; // BAD
38+
39+
long i1 = i << 22; // OK
40+
long i2 = i << 32; // BAD
41+
long i3 = i << 42; // BAD
42+
long i4 = i << 64; // BAD
43+
long i5 = i << 65; // BAD
44+
45+
long l1 = l << 22; // OK
46+
long l2 = l << 32; // OK
47+
long l3 = l << 42; // OK
48+
long l4 = l << 64; // BAD
49+
long l5 = l << 65; // BAD
50+
}
51+
}

0 commit comments

Comments
 (0)