{"record":{"id":"a08202c4ff9c9d90","repo":"NationalSecurityAgency/ghidra","slug":"ghidrarandomaccessfile-is-closed","errorCode":null,"errorMessage":"GhidraRandomAccessFile is closed","messagePattern":"GhidraRandomAccessFile is closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GRandomAccessFile.java","lineNumber":31,"sourceCode":" * This implementation relies on java.net.RandomAccessFile,\n * but adds buffering to limit the amount.\n */\npublic class GRandomAccessFile {\n\tprivate static final byte[] EMPTY = new byte[0];\n\tprivate static final int BUFFER_SIZE = 0x100000;\n\n\tprivate RandomAccessFile randomAccessFile;\n\tprivate byte[] buffer = EMPTY;\n\tprivate long bufferOffset = 0;\n\tprivate long bufferFileStartIndex = 0;\n\tprivate byte[] lastbuffer = EMPTY;\n\tprivate long lastbufferOffset = 0;\n\tprivate long lastbufferFileStartIndex = 0;\n\tprivate boolean open = false;\n\n\tprivate void checkOpen() throws IOException {\n\t\tif (!open) {\n\t\t\tthrow new IOException(\"GhidraRandomAccessFile is closed\");\n\t\t}\n\t}\n\n\t/**\n\t * Creates a random access file stream to read from, and optionally to\n\t * write to, the file specified by the {@link File} argument.  A new {@link\n\t * FileDescriptor} object is created to represent this file connection.\n\t *\n\t * <p>\n\t * This implementation relies on java.net.RandomAccessFile,\n\t * but adds buffering to limit the amount.\n\t * <p>\n\t * \n\t * <a name=\"mode\"><p> The <tt>mode</tt> argument specifies the access mode\n\t * in which the file is to be opened.  The permitted values and their\n\t * meanings are:\n\t *\n\t * <blockquote><table summary=\"Access mode permitted values and meanings\">","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GRandomAccessFile.java#L13-L49","documentation":"GRandomAccessFile maintains an 'open' flag set false by close(). Every mutating/reading operation routes through checkOpen(), which throws this IOException if the instance has already been closed. It is the standard use-after-close guard for a buffered RandomAccessFile wrapper.","triggerScenarios":"Calling any operation (read, seek, write, length, etc.) on a GRandomAccessFile after close() was invoked. Commonly happens when the owning reader/provider is closed but a reference to the underlying file is still held and used, or in error-handling paths that close early then continue.","commonSituations":"Closing a DmgFileReader/ByteProvider in a finally block but then retrying an operation; nested resource management where the outer close invalidates an inner handle still in use; a try-with-resources that scoped too widely; double-close across multiple owners of the same handle.","solutions":["Audit the lifecycle: ensure no reads/seeks happen after the close() that owns this GRandomAccessFile.","Move the close() to the true end of use, or stop retaining references past close.","Use try-with-resources scoped tightly around the read loop so close runs only after all access completes.","Null out references after close to surface accidental reuse as an NPE earlier in the chain."],"exampleFix":"// before\ngraf.close();\n// ... later, in error path:\ngraf.seek(pos);\n\n// after - ensure no access after close, scope the resource\ntry (GRandomAccessFile graf = new GRandomAccessFile(file, mode)) {\n    graf.seek(pos);\n    // ... all reads here\n}","handlingStrategy":"try-catch","validationCode":"// No pre-call validation possible; track ownership centrally.\n// Optional: wrap access in a guard that checks an application-level 'closed' flag\nif (graf == null) throw new IllegalStateException(\"GRandomAccessFile reference is null (already released)\");\ngraf.seek(pos);","typeGuard":"// Not a type guard per se, but a lifecycle wrapper:\nclass SafeRandomAccessFile {\n    private final GRandomAccessFile inner;\n    private volatile boolean closed = false;\n    synchronized void close() throws IOException { closed = true; inner.close(); }\n    void seek(long pos) throws IOException {\n        if (closed) throw new IOException(\"already closed\");\n        inner.seek(pos);\n    }\n}","tryCatchPattern":"try {\n    graf.seek(pos);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"is closed\")) {\n        // Use-after-close: do not retry; re-open or fail upward with context\n        throw new IOException(\"Attempted to use GRandomAccessFile after close\", e);\n    }\n    throw e;\n}","preventionTips":["Scope resource lifetime with try-with-resources around the entire access window.","Do not retain or share the underlying handle beyond its owner's lifetime.","Null out references after close to surface accidental reuse earlier.","Audit finally/error paths that close early then continue processing."],"tags":["dmg","random-access","resource-lifecycle","use-after-close","io"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}