{"record":{"id":"edc538be28fa4548","repo":"NationalSecurityAgency/ghidra","slug":"pos-cannot-be-less-than-zero","errorCode":null,"errorMessage":"pos cannot be less than zero","messagePattern":"pos cannot be less than zero","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GRandomAccessFile.java","lineNumber":133,"sourceCode":"\t/**\n\t * Sets the file-pointer offset, measured from the beginning of this \n\t * file, at which the next read or write occurs.  The offset may be \n\t * set beyond the end of the file. Setting the offset beyond the end \n\t * of the file does not change the file length.  The file length will \n\t * change only by writing after the offset has been set beyond the end \n\t * of the file. \n\t * @param      pos   the offset position, measured in bytes from the \n\t *                   beginning of the file, at which to set the file \n\t *                   pointer.\n\t * @throws IOException \n\t * @exception  IOException  if <code>pos</code> is less than \n\t *                          <code>0</code> or if an I/O error occurs.\n\t */\n\tpublic void seek(long pos) throws IOException {\n\t\tcheckOpen();\n\n\t\tif (pos < 0) {\n\t\t\tthrow new IOException(\"pos cannot be less than zero\");\n\t\t}\n\n\t\tif (pos < bufferFileStartIndex || pos >= bufferFileStartIndex + BUFFER_SIZE) {\n\t\t\t// check if the last buffer contained it, and swap in if necessary\n\t\t\tswapInLast();\n\t\t\tif (pos < bufferFileStartIndex || pos >= bufferFileStartIndex + BUFFER_SIZE) {\n\t\t\t\t// not in either, gotta get a new one\n\t\t\t\tbuffer = EMPTY;\n\t\t\t\tbufferOffset = 0;\n\t\t\t\tbufferFileStartIndex = pos;\n\t\t\t}\n\t\t}\n\t\tbufferOffset = pos - bufferFileStartIndex;\n\t}\n\n\t/**\n\t * This method reads a byte from the file, starting from the current file pointer. \n\t * <p>","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GRandomAccessFile.java#L115-L151","documentation":"GRandomAccessFile.seek(long pos) calls checkOpen() then explicitly rejects pos < 0 with this IOException, rather than letting the underlying RandomAccessFile.seek throw. A negative position is nonsensical for a file offset, so it indicates a computed offset went wrong upstream (underflow, bad arithmetic, endianness).","triggerScenarios":"Calling seek(pos) where pos is negative. pos is usually computed from parsed offsets (base + relative, or a record's start); a corrupt/wrong relative offset or an arithmetic underflow can drive it below zero.","commonSituations":"A parsed offset field read with wrong endianness yielding a huge value that wraps negative when narrowed or combined; subtracting a base offset that exceeds the position; truncated input where an offset field is missing (0) combined with a negative adjustment; off-by-one in computing a record's start.","solutions":["Log pos and its components before seek to identify which offset went negative.","Verify the offset fields feeding pos are read with the correct width and endianness.","Bounds-check pos against [0, length()) before seeking.","Confirm the parsed structure providing the offset is intact (re-check the parent parse)."],"exampleFix":"// before\nraf.seek(base + relative);\n\n// after\nlong pos = base + relative;\nif (pos < 0 || pos > raf.length()) {\n    throw new IOException(\"Bad seek target \" + pos + \" (base=\" + base + \", rel=\" + relative + \")\");\n}\nraf.seek(pos);","handlingStrategy":"validation","validationCode":"if (pos < 0) {\n    throw new IOException(\"Refusing negative seek: \" + pos + \" (computed offset is corrupt)\");\n}\nif (pos > raf.length()) {\n    throw new IOException(\"Seek target \" + pos + \" past EOF \" + raf.length());\n}\nraf.seek(pos);","typeGuard":"private static boolean isSeekable(long pos, long fileLen) {\n    return pos >= 0 && pos <= fileLen;\n}","tryCatchPattern":"try {\n    raf.seek(pos);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"less than zero\")) {\n        throw new IOException(\"Negative seek computed (likely corrupt offset field)\", e);\n    }\n    throw e;\n}","preventionTips":["Bounds-check computed offsets against [0, length()] before seeking.","Verify offset fields use correct endianness and width when computing positions.","Log the components of a computed offset so underflow is diagnosable."],"tags":["dmg","random-access","validation","parsing","corrupt-file"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}