You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

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 (MSB) to the least significant. Prior to JDK 1.4, custom methods had to be defined to be compatible with little-endian systems that use the reverse byte order. Handling byte order related issues is critical when data is to be exchanged in a networked environment that consists of machines varying in endianness. Failure to handle such cases can cause misinterpretations and unexpected program behavior.

Noncompliant Code Example

The java.io.DataInputStream class defines read methods (readByte, readShort, readInt, readLong, readFloat and readDouble) and the corresponding write methods. All these methods work with big-endian data only. The use of these methods can be unsafe while interfacing with traditional languages such as C or C++, that do not provide any guarantees on endianness. This noncompliant example shows such a discrepancy.

DataInputStream dis = new DataInputStream(
  new FileInputStream("data.txt"));  // Little-endian data might be read as big-endian
int serialNumber = dis.readInt();

Compliant Solution (1)

This compliant solution wraps the byte array containing the integer bytes read-in into a ByteBuffer and sets the byte order to little-endian. The result is stored in the integer serialNumber.

	 
DataInputStream dis = new DataInputStream(
  new FileInputStream("data.txt"));

byte[] buffer= new byte[4];
int bytesRead = dis.read(buffer);  // Bytes are read into buffer
int serialNumber = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();

Compliant Solution (2)

Assuming that an integer value is to be read from the file, read and write methods can be defined for handling little-endian data. The readLittleEndianInteger method reads data into a byte buffer and then pieces together the integer in the right order. Long values can be handled by defining a byte buffer of size 8. The writeLittleEndianInteger method obtains bytes by repeatedly casting the integer so that the most significant byte is extracted on successive right shifts.

	 
// read method
public static int readLittleEndianInteger(InputStream ips) throws IOException {
  int result;
  byte[] buffer = new byte[4];
  int check = ips.read(buffer);
  if (check != 4) throw new IOException("Unexpected End of Stream");
  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 {
  int result;
  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 (3)

In JDK 1.5+, the Integer.reverseBytes() method can be used to reverse the order of the bytes constituting the integer. Note that there is no such method for float and double values.

	 
public static int reverse(int i){
  return Integer.reverseBytes(i);
}

Risk Assessment

Reading and writing data without considering endianness may lead to serious misinterpretations about magnitude and sign, alike.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SEC01-J

low

unlikely

low

P3

L3

Automated Detection

TODO

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

[[API 06]] Class ByteBuffer: Methods wrap and order. Class Integer: method reverseBytes
[[Harold 97]] Chapter 2: Primitive Data Types, Cross Platform issues


04. Integers (INT)      04. Integers (INT)      INT01-J. Provide mechanisms to handle unsigned data when required

  • No labels