{"record":{"id":"8fc81d2d8607c6a9","repo":"NationalSecurityAgency/ghidra","slug":"unable-to-read-bytes","errorCode":null,"errorMessage":"Unable to read {} bytes","messagePattern":"Unable to read (.+?) bytes","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GByteProvider.java","lineNumber":106,"sourceCode":"    }\n\n    /**\n     * @see GByteProvider.app.util.bin.ByteProvider#readByte(long)\n     */\n    public byte readByte(long index) throws IOException {\n        randomAccessFile.seek(index);\n        return randomAccessFile.readByte();\n    }\n\n    /**\n     * @see GByteProvider.app.util.bin.ByteProvider#readBytes(long, long)\n     */\n    public byte [] readBytes(long index, long length) throws IOException {\n        randomAccessFile.seek(index);\n        byte [] b = new byte[(int)length];\n        int nRead = randomAccessFile.read(b);\n        if (nRead != length) {\n            throw new IOException(\"Unable to read \"+length+\" bytes\");\n        }\n        return b;\n    }\n\n    /**\n     * @see GByteProvider.app.util.bin.ByteProvider#writeByte(long, byte)\n     */\n    public void writeByte(long index, byte value) throws IOException {\n        randomAccessFile.seek(index);\n        randomAccessFile.write(value);\n    }\n\n    /**\n     * @see GByteProvider.app.util.bin.ByteProvider#writeBytes(long, byte[])\n     */\n    public void writeBytes(long index, byte [] values) throws IOException {\n        randomAccessFile.seek(index);\n        randomAccessFile.write(values);","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GByteProvider.java#L88-L124","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nbyte[] b = provider.readBytes(index, length);\n\n// after\nif (index < 0 || length < 0 || index + length > provider.length()) {\n    throw new IOException(\"Refusing read [\" + index + \"+\" + length + \"] beyond EOF \" + provider.length());\n}\nbyte[] b = provider.readBytes(index, length);","handlingStrategy":"validation","validationCode":"long fileLen = provider.length();\nif (index < 0 || length < 0 || length > fileLen - index) {\n    throw new IOException(\"Refusing read beyond EOF: index=\" + index + \" length=\" + length + \" fileLen=\" + fileLen);\n}\n// also guard the int cast for the buffer allocation\nif (length > Integer.MAX_VALUE) {\n    throw new IOException(\"Requested length \" + length + \" exceeds max array size\");\n}\nbyte[] b = provider.readBytes(index, length);","typeGuard":"private static boolean rangeWithinFile(long index, long length, long fileLen) {\n    return index >= 0 && length >= 0 && length <= Integer.MAX_VALUE && index + length <= fileLen;\n}","tryCatchPattern":"try {\n    return provider.readBytes(index, length);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Unable to read\")) {\n        throw new IOException(\"Short read at index \" + index + \" for \" + length + \" bytes (file length \" + provider.length() + \")\", e);\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["dmg","byte-provider","eof","parsing","corrupt-file","io"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}