The abstract 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 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 the end of the stream has been reached. 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 (indicated 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 InputStream
or Reader
subclass that provides an implementation of the read()
method. This rule is a specific instance of rule NUM12-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data.
Noncompliant Code Example (byte
)
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. This conversion leaves the value of c
as 0xFFFF
(e.g., Character.MAX_VALUE
) instead of -1. Consequently, the test for the end of stream never evaluates to true (because the char
type is unsigned and the value of c
is 0-extended to 0x0000FFFF
).
FileInputStream in; // initialize stream byte data; while ((data = (byte) in.read()) != -1) { // ... }
When the return value of read()
method is cast to the byte
value 0xFF
, the returned byte value is 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.
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
.
FileInputStream in; // initialize stream int inbuff; byte data; while ((inbuff = in.read()) != -1) { data = (byte) inbuff; // ... }
Noncompliant Code Example (char
)
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 c
as 0xFFFF
(e.g., Character.MAX_VALUE
) instead of -1. Consequently, the test for the end of stream never evaluates to true.
FileReader in; // initialize stream char c; while ((c = (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, it can be safely cast to type char
.
FileReader in; // initialize 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 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
Some static analysis tools can detect violations of this rule.
Related Guidelines
FIO34-C. Use {{int}} to capture the return value of character IO functions |
|
FIO34-CPP. Use {{int}} to capture the return value of character IO functions |
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="8e3524f2-38c9-4b9f-8870-9b27eec34e85"><ac:plain-text-body><![CDATA[ |
[[API 2006 |
AA. Bibliography#API 06]] |
Class |
]]></ac:plain-text-body></ac:structured-macro> |
|
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="70fa341d-a860-45a2-82f6-a8577bf2524e"><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="6c66f433-e299-459b-8955-bdd89c71577d"><ac:plain-text-body><![CDATA[ |
[[Pugh 2008 |
AA. Bibliography#Pugh 08]] |
Waiting for the End |
]]></ac:plain-text-body></ac:structured-macro> |