{"record":{"id":"b755f14ee777b73b","repo":"PHPOffice/PhpSpreadsheet","slug":"parameter-pos-pos-is-invalid","errorCode":null,"errorMessage":"Parameter pos=$pos is invalid.","messagePattern":"Parameter pos=\\$pos is invalid\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Reader\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Shared/OLERead.php","lineNumber":312,"sourceCode":"            }\n\n            // Additional Document Summary information\n            if ($name == chr(5) . 'DocumentSummaryInformation') {\n                $this->documentSummaryInformation = count($this->props) - 1;\n            }\n\n            $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;\n        }\n    }\n\n    /**\n     * Read 4 bytes of data at specified position.\n     */\n    private static function getInt4d(string $data, int $pos): int\n    {\n        if ($pos < 0) {\n            // Invalid position\n            throw new ReaderException('Parameter pos=' . $pos . ' is invalid.');\n        }\n\n        $len = strlen($data);\n        if ($len < $pos + 4) {\n            $data .= str_repeat(\"\\0\", $pos + 4 - $len);\n        }\n\n        // FIX: represent numbers correctly on 64-bit system\n        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334\n        // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems\n        $_or_24 = ord($data[$pos + 3]);\n        if ($_or_24 >= 128) {\n            // negative number\n            $_ord_24 = -abs((256 - $_or_24) << 24);\n        } else {\n            $_ord_24 = ($_or_24 & 127) << 24;\n        }\n","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Shared/OLERead.php#L294-L330","documentation":"Thrown by OLERead::getInt4d() when asked to read a 4-byte integer at a negative offset into the OLE data buffer. Offsets in a valid container are non-negative; a negative one means header fields (block counts, start blocks) decoded to nonsense values earlier, which is the signature of a corrupted or hostile file.","triggerScenarios":"During Reader\\Xls loading: constants like NUM_BIG_BLOCK_DEPOT_BLOCKS_POS or ROOT_START_BLOCK_POS combine with sizes/pointers from a damaged header so that a computed position goes negative, and getInt4d's `if ($pos < 0)` guard fires. Only reachable with malformed input — well-formed .xls files never produce negative positions.","commonSituations":"Truncated or bit-flipped .xls uploads; files patched by broken conversion tools; fuzzing payloads aimed at the binary reader.","solutions":["Re-obtain the workbook from the source and retry; verify integrity (checksum vs origin) if available.","Screen uploads before parsing: non-empty, plausible size, and correct OLE magic bytes, so damaged files fail fast with your own error message.","Catch Reader\\Exception around Xls loads and map it to a user-facing 'corrupt file' response instead of a server error.","Update PhpSpreadsheet — bounds/robustness fixes for the legacy reader are released regularly."],"exampleFix":"// before\n$spreadsheet = (new \\PhpOffice\\PhpSpreadsheet\\Reader\\Xls())->load('input.xls');\n// Parameter pos=-4 is invalid.\n\n// after\ntry {\n    $spreadsheet = (new \\PhpOffice\\PhpSpreadsheet\\Reader\\Xls())->load('input.xls');\n} catch (\\PhpOffice\\PhpSpreadsheet\\Reader\\Exception $e) {\n    return ['error' => 'The .xls file is corrupt and cannot be read. Please re-export it.'];\n}","handlingStrategy":"try-catch","validationCode":"// cheap structural pre-check for legacy workbooks:\n$size = filesize($file);\n$head = (string) @file_get_contents($file, false, null, 0, 512);\nif ($size === 0 || !str_starts_with($head, \"\\xD0\\xCF\\x11\\xE0\")) {\n    throw new RuntimeException('Empty or non-OLE workbook');\n}","typeGuard":null,"tryCatchPattern":"try { $spreadsheet = $reader->load($file); }\ncatch (\\PhpOffice\\PhpSpreadsheet\\Reader\\Exception $e) {\n    if (str_contains($e->getMessage(), 'is invalid.') || str_contains($e->getMessage(), 'OLE')) {\n        return ['error' => 'Corrupt .xls file — please re-export it.'];\n    }\n    throw $e;\n}","preventionTips":["Validate file size and signature at upload; malformed containers should fail your own checks first.","Map all legacy-reader exceptions to a user-facing 'corrupt file' outcome instead of HTTP 500s.","Update PhpSpreadsheet regularly — the legacy Xls reader receives ongoing robustness fixes."],"tags":["ole","file-corruption","out-of-bounds","phpspreadsheet"],"backgroundTag":"corrupted-binary-structure","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}