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 . It will return -1 only when the end of the input stream has been reached. The similar (0x00
-0xff
). The Reader.read()
method reads a single character , and returns its value as an int
, in the range 0 -to 65,535 . It also returns -1 only when (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. Both methods are meant to be overridden by subclasses.These methods are often used to read a byte or character from a stream. Unfortunately many programmers prematurely convert the resulting int
back to a byte
or char
before checking whether they have reached the end of the stream (signaled by a return value of -1). Programs must check for the end of stream (e.g., -1) 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 provide 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 data.
Noncompliant Code Example (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 −1 in an attempt to detect the end of the stream.
Code Block | ||
---|---|---|
| ||
FileInputStream in; // initializeInitialize stream byte data; while ((data = (byte) in.read()) != -1) { // ... } |
When If the return value of read()
method is cast to the byte
value 0xFF
, it will be encounters a 0xFF
byte in the file, this value becomes indistinguishable from the -1 −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−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−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; // initializeInitialize 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 −1 in an attempt to detect the end of stream. This conversion leaves the value of c
data
as 0xffff
0xFFFF
(e.g., Character.MAX_VALUE
) instead of -1−1. Consequently, the test for the end of stream file never evaluates to true.
Code Block | ||
---|---|---|
| ||
FileReader in; // initializeInitialize stream char cdata; while ((cdata = (char) in.read()) != -1) { // ... } |
Compliant Solution (char
)
Use a variable of type int
to capture the return value of the character input method. When the value returned by read()
is not -1−1, it can be safely cast to type char
.
Code Block | ||
---|---|---|
| ||
FileReader in; // initializeInitialize stream int inbuff; char data; while ((inbuff = in.read()) != -1) { data = (char) inbuff; // ... } |
Risk Assessment
Historically, using a narrow type to capture the return value of a byte input function 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
FindBugs version 1.3.9 Some static analysis tools can detect violations of this rule with the INT: Bad comparison of nonnegative value with negative constant detector..
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
Distinguish between characters read from a file and EOF or WEOF |
FIO34-CPP. Use int to capture the return value of character IO functions |
Bibliography
FIO35-CPP. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char) |
Bibliography
[API 2014] | |
[JLS 2005] | |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="e65dc525-ce59-4115-98a4-73fcca716f13"><ac:plain-text-body><![CDATA[
[[API 2006
AA. Bibliography#API 06]]
Class InputStream
]]></ac:plain-text-body></ac:structured-macro>
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6cc02fa8-57a2-4604-8f28-6217500e0356"><ac:plain-text-body><![CDATA[
[[JLS 2005
AA. Bibliography#JLS 05]]
[§4.2
http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2] "Primitive Types and Values"
]]></ac:plain-text-body></ac:structured-macro>
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="a0452691-adc1-441b-9738-e63c63ed3873"><ac:plain-text-body><![CDATA[
[[Pugh 2008
] | "Waiting for the |
End" |
]]></ac:plain-text-body></ac:structured-macro>
...