The abstract InputStream.read()
and Reader.read()
methods are used to read a byte or character, respectively, from a stream. The InputStream.read()
method reads a single byte from an input source and returns its value as an int
in the range 0 to 255 (0x00
-0xff
). The Reader.read()
method reads a single character and returns its value as an int
in the range 0 to 65,535 (0x0000
-0xffff
). Both methods return the 32-bit value -1
(0xffffffff
) to indicate that the end of the stream has been reached and no data is available. The larger int
size is used by both methods to differentiate between the end-of-stream indicator and the maximum byte (0xff
) or character (0xffff
) value. The end-of-stream indicator is an example of an in-band error indicator. In-band error indicators are problematic to work with, and the creation of new in-band-error indicators is discouraged.
Prematurely converting the resulting int
to a byte
or char
before testing for the value −1 makes it impossible to distinguish between characters read and the end of stream indicator. Programs must check for the end of stream before narrowing the return value to a byte
or char
.
This rule applies to any method that returns the value −1 to indicate the end of a stream. It includes any InputStream
or Reader
subclass that provides an implementation of the read()
method. This rule is a specific instance of NUM12-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted char
type is the only unsigned primitive type in Java. It is easy to overlook this fact and assume that a signed value can be stored and retrieved successfully. Common effects of the defective code include memory leaks and misrepresented data.
Noncompliant Code Example
...
Wiki Markup |
---|
This noncompliant example is from the {{sun.net.httpserver.ChunkedInputStream}} class. The {{InputStream}} class's {{read()}} method returns a signed byte in the form of a signed integer. In this case, the end of stream is being checked by casting the return value to a {{char}}. This conversion would leave the value of {{c}} as {{0xffff}} ({{Character.MAX_VALUE}}, decimal 65535) instead of -1. The termination test is doomed to fail. \[[Pugh 08|AA. Java References#Pugh 08]\] |
(byte
)
FileInputStream
is a subclass of InputStream
. It will return −1 only when the end of the input stream has been reached. This noncompliant code example casts the value returned by the read()
method directly to a value of type byte
and then compares this value with −1 in an attempt to detect the end of the stream.
Code Block | ||
---|---|---|
| ||
FileInputStream in;
// Initialize stream
byte data;
while ((data = (byte) in.read()) != -1) {
// ...
}
|
If the read()
method encounters a 0xFF
byte in the file, this value becomes indistinguishable from the −1 value used to indicate the end of stream, because the byte value is promoted and sign-extended to an int
before being compared with −1. Consequently, the loop will halt prematurely if a 0xFF
byte is read.
Compliant Solution (byte
)
Use a variable of type int
to capture the return value of the byte
input method. When the value returned by read()
is not −1, it can be safely cast to type byte
. When read()
returns 0x000000FF
, the comparison will test against 0xFFFFFFFF
, which evaluates to false.
Code Block | ||
---|---|---|
| ||
FileInputStream in;
// Initialize stream
int inbuff;
byte data;
while ((inbuff = in.read()) != -1) {
data = (byte) inbuff;
// ...
}
|
Noncompliant Code Example (char
)
FileReader
is a subclass of InputStreamReader
, which is in turn a subclass of Reader
. It also returns −1 only when the end of the stream is reached. This noncompliant code example casts the value of type int
returned by the read()
method directly to a value of type char
, which is then compared with −1 in an attempt to detect the end of stream. This conversion leaves the value of data
as 0xFFFF
(e.g., Character.MAX_VALUE
) instead of −1. Consequently, the test for the end of file never evaluates to true.
Code Block | ||
---|---|---|
| ||
FileReader in;
// Initialize stream
char data;
while ((data = (char) in.read()) | ||
Code Block | ||
| ||
char c; while ((c=(char)in.read())!= -1) { // ... } |
Compliant Solution (char
)
Always use Use a signed type of sufficient size to store signed data. To be compliant, use an integer
type to check for EOF
while reading in datavariable of type int
to capture the return value of the character input method. When the value returned by read()
is not −1, it can be safely cast to type char
.
Code Block | ||
---|---|---|
| ||
FileReader in; // Initialize stream int inbuff; char cdata; while ((cinbuff = in.read()) != -1) { data = (char) inbuff; // ... } |
Risk Assessment
Trying to store signed data in an unsigned type can lead to misinterpretations about the actual value.Historically, using a narrow type to capture the return value of a byte input method has resulted in significant vulnerabilities, including command injection attacks; see CA-1996-22 advisory. Consequently, the severity of this error is high.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
FIO08-J |
High |
Probable |
Medium |
P12 |
L1 |
Automated Detection
...
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Some static analysis tools can detect violations of this rule.
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Parasoft Jtest |
| CERT.FIO08.CRRV | Check the return value of methods which read or skip input | ||||||
SpotBugs |
| EOS_BAD_END_OF_STREAM_CHECK | Implemented (since 4.4.0) |
Related Guidelines
Bibliography
...
\[[API 06|AA. Java References#API 06]\] Class {{InputStream}}
\[[JLS 05|AA. Java References#JLS 05]\] 4.2 Primitive Types and Values
\[[Pugh 08|AA. Java References#Pugh 08]\] "Waiting for the end" Wiki Markup