Skip to content

Commit e977519

Browse files
authored
Add cast-then-eof-check.ql
1 parent 96305c6 commit e977519

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Finds code which calls a stream reading method, such as `InputStream#read()`,
3+
* casts the result to a smaller numeric type such as `byte` and afterwards
4+
* checks for end-of-file by comparing the value against `-1`.
5+
*
6+
* This can lead to an erroneously detected end-of-file because due to the cast
7+
* the max unsigned value of the target type, e.g. the byte value 255, becomes
8+
* -1 as well. Instead the code should first check for -1 and only afterwards
9+
* perform the cast.
10+
*
11+
* For example:
12+
* ```java
13+
* // BAD
14+
* byte b = (byte) in.read();
15+
* if (b == -1) ...
16+
*
17+
* // GOOD
18+
* int read = in.read();
19+
* if (read == -1) ...
20+
* byte b = (byte) read;
21+
* ```
22+
*
23+
* This query was inspired by <https://github.com/openjdk/jdk/pull/30357>.
24+
*
25+
* @kind problem
26+
* @id TODO
27+
*/
28+
29+
import java
30+
import semmle.code.java.dataflow.DataFlow
31+
32+
class CastTargetType extends Type {
33+
CastTargetType() {
34+
this.hasName(["byte", "char", "short"]) or
35+
this.(Class).hasQualifiedName("java.lang", ["Byte", "Character", "Short"])
36+
}
37+
}
38+
39+
// Checks for flow from a method returning an `int`, to conversion to `byte` and comparison with `-1`
40+
from MethodCall readCall, EqualityTest eofCheck, Expr castedExpr
41+
where
42+
readCall.getType().hasName("int") and
43+
DataFlow::localExprFlow(readCall, castedExpr) and
44+
// Assumes that dataflow implicitly continues through cast to `byte`
45+
castedExpr.getType() instanceof CastTargetType and
46+
eofCheck.getAnOperand() = castedExpr and
47+
// Don't check for CompileTimeConstantExpr because that would also include values such as `0xFF` or constants which do not represent EOF
48+
eofCheck.getAnOperand().(MinusExpr).getExpr().(IntegerLiteral).getIntValue() = 1
49+
select eofCheck, "Incorrect end-of-file check after cast"

0 commit comments

Comments
 (0)