In Java, data is stored in big-endian format (also called network order). That is, all data is represented sequentially starting from the most significant bit to the least significant. JDK versions prior to JDK 1.4 required definition of custom methods that manage reversing byte order to maintain compatibility with little-endian systems. Correct handling of byte order related issues is critical when exchanging data in a networked environment that includes both big-endian and little-endian machines, or when working with other languages via JNI. Failure to handle byte ordering issues can cause unexpected program behavior.
Noncompliant Code Example
The read methods (readByte, readShort, readInt, readLong, readFloat and readDouble
) and the corresponding write methods defined by class java.io.DataInputStream
operate only on big-endian data. Use of these methods while interoperating with traditional languages, such as C or C++, is insecure because such languages lack any guarantees about endianness. This noncompliant code example shows such a discrepancy.
DataInputStream dis = new DataInputStream( new FileInputStream("data")); // Little-endian data might be read as big-endian int serialNumber = dis.readInt();
Compliant Solution (Use ByteBuffer
)
This compliant solution uses methods provided by class ByteBuffer
(see [[API 2006]] ByteBuffer) to correctly extract an int
from the original input value. It wraps the input byte array with a ByteBuffer
, sets the byte order to little-endian, and extracts the int
. The result is stored in the integer serialNumber
.
DataInputStream dis = new DataInputStream( new FileInputStream("data")); byte[] buffer = new byte[4]; int bytesRead = dis.read(buffer); // Bytes are read into buffer if (bytesRead != 4) { throw new IOException("Unexpected End of Stream"); } int serialNumber = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
Class ByteBuffer
provides analogous get and put methods for the other numeric types.
Compliant Solution (Define Special-Purpose Methods)
An alternative compliant solution is to define read and write methods that support the necessary byte-swapping while reading from or writing to the file. In this example, the readLittleEndianInteger()
method reads four bytes into a byte buffer and then pieces together the integer in the correct order. The writeLittleEndianInteger()
method obtains bytes by repeatedly casting the integer so that the least significant byte is extracted on successive right shifts. Long
values can be handled by defining a byte buffer of size eight.
// read method public static int readLittleEndianInteger(InputStream ips) throws IOException { byte[] buffer = new byte[4]; int check = ips.read(buffer); if (check != 4) { throw new IOException("Unexpected End of Stream"); } int result = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; return result; } // write method public static void writeLittleEndianInteger(int i, OutputStream ops) throws IOException { byte[] buffer = new byte[4]; buffer[0] = (byte) i; buffer[1] = (byte) (i >> 8); buffer[2] = (byte) (i >> 16); buffer[3] = (byte) (i >> 24); ops.write(buffer); }
Compliant Solution (Use reverseBytes()
)
When programming for JDK 1.5+, use the reverseBytes()
method defined in the classes Character
, Short
, Integer
, and Long
to reverse the order of the integral value's bytes. Note that classes Float
and Double
lack such a method.
public static int reverse(int i){ return Integer.reverseBytes(i); }
Risk Assessment
Reading and writing data without considering endianness can lead to serious misinterpretations of both the magnitude and sign of the data.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
FIO12-J |
low |
unlikely |
low |
P3 |
L3 |
Automated Detection
Automated detection is infeasible in the general case.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6c8d5711-6590-45b5-8fc6-9c45a8aa89c1"><ac:plain-text-body><![CDATA[ |
[[API 2006 |
AA. Bibliography#API 06]] |
Class [ByteBuffer |
http://download.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html]: Methods |
http://download.oracle.com/javase/6/docs/api/java/lang/Integer.html]: method |
]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="09b8cead-630e-4505-a45e-a672d9237edb"><ac:plain-text-body><![CDATA[ |
[[Cohen 1981 |
AA. Bibliography#Cohen 81]] |
"On Holy Wars and a Plea for Peace" |
]]></ac:plain-text-body></ac:structured-macro> |
||
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="363b698d-ff83-445d-aad5-905c538405e2"><ac:plain-text-body><![CDATA[ |
[[Harold 1997 |
AA. Bibliography#Harold 97]] |
Chapter 2: "Primitive Data Types, Cross Platform issues" |
]]></ac:plain-text-body></ac:structured-macro> |
FIO11-J. Do not attempt to read raw binary data as character data 12. Input Output (FIO)