NationalSecurityAgency/ghidra · error · IOException
Unable to read {} bytes
Error message
Unable to read {} bytes What it means
GByteProvider.readBytes(long index, long length) seeks to index, reads into a freshly allocated buffer, and throws if the number of bytes actually read (randomAccessFile.read) does not equal the requested length. Java's RandomAccessFile.read is allowed to return fewer bytes than requested, and returns -1 at EOF, so a mismatch means the requested range extends beyond EOF or hit a short read.
Source
Thrown at GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GByteProvider.java:106
}
/**
* @see GByteProvider.app.util.bin.ByteProvider#readByte(long)
*/
public byte readByte(long index) throws IOException {
randomAccessFile.seek(index);
return randomAccessFile.readByte();
}
/**
* @see GByteProvider.app.util.bin.ByteProvider#readBytes(long, long)
*/
public byte [] readBytes(long index, long length) throws IOException {
randomAccessFile.seek(index);
byte [] b = new byte[(int)length];
int nRead = randomAccessFile.read(b);
if (nRead != length) {
throw new IOException("Unable to read "+length+" bytes");
}
return b;
}
/**
* @see GByteProvider.app.util.bin.ByteProvider#writeByte(long, byte)
*/
public void writeByte(long index, byte value) throws IOException {
randomAccessFile.seek(index);
randomAccessFile.write(value);
}
/**
* @see GByteProvider.app.util.bin.ByteProvider#writeBytes(long, byte[])
*/
public void writeBytes(long index, byte [] values) throws IOException {
randomAccessFile.seek(index);
randomAccessFile.write(values);View on GitHub (pinned to d5f144c24d)
Solutions
- Check index+length <= provider.length() before reading, and log the actual file length on failure.
- Verify the index/length were computed with correct endianness and sizing from the parsed structure.
- Confirm the backing file is complete (re-download / verify size and checksum).
- Avoid passing near-Long.MAX_VALUE lengths (also note the new byte[(int)length] cast can itself overflow for huge values).
Example fix
// before
byte[] b = provider.readBytes(index, length);
// after
if (index < 0 || length < 0 || index + length > provider.length()) {
throw new IOException("Refusing read [" + index + "+" + length + "] beyond EOF " + provider.length());
}
byte[] b = provider.readBytes(index, length); Defensive patterns
Strategy: validation
Validate before calling
long fileLen = provider.length();
if (index < 0 || length < 0 || length > fileLen - index) {
throw new IOException("Refusing read beyond EOF: index=" + index + " length=" + length + " fileLen=" + fileLen);
}
// also guard the int cast for the buffer allocation
if (length > Integer.MAX_VALUE) {
throw new IOException("Requested length " + length + " exceeds max array size");
}
byte[] b = provider.readBytes(index, length); Type guard
private static boolean rangeWithinFile(long index, long length, long fileLen) {
return index >= 0 && length >= 0 && length <= Integer.MAX_VALUE && index + length <= fileLen;
} Try / catch
try {
return provider.readBytes(index, length);
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Unable to read")) {
throw new IOException("Short read at index " + index + " for " + length + " bytes (file length " + provider.length() + ")", e);
}
throw e;
} Prevention
- Always check index + length <= provider.length() before reading.
- Verify parsed index/length fields use correct endianness and sizing.
- Confirm the backing file is complete (size/checksum) before parsing.
- Guard against length values that overflow the (int) array allocation.
When it happens
Trigger: Calling readBytes(index, length) where index+length exceeds the underlying file size, or where the file was truncated between sizing and reading. The cast to int for the buffer allocation also means very large length values (near Long.MAX_VALUE) will overflow the array size first, but the nRead != length check catches the EOF/short-read case specifically.
Common situations: A parsed length field from a corrupt/truncated structure pointing past EOF; a partially downloaded or truncated DMG; concurrent truncation of the backing file; an off-by-one or endianness error in computing index or length so the read runs off the end.
Related errors
- Bad nodeSize: {}
- Invalid number of elements specified: {}
- pos cannot be less than zero
- No system partitions found. Perhaps the decryption failed?
- GhidraRandomAccessFile is closed
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/8fc81d2d8607c6a9.
Report an issue: GitHub.