{"record":{"id":"112b7b0b8421bdaa","repo":"BabylonJS/Babylon.js","slug":"zlib-invalid-expected-length","errorCode":null,"errorMessage":"zlib: invalid expected length","messagePattern":"zlib: invalid expected length","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/loaders/src/FBX/parsers/zlibInflate.ts","lineNumber":19,"sourceCode":"/* eslint-disable @typescript-eslint/naming-convention, jsdoc/require-param, jsdoc/require-returns */\r\nconst ADLER_MOD = 65521;\r\nconst MAX_BITS = 15;\r\n\r\nconst LENGTH_BASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];\r\nconst LENGTH_EXTRA_BITS = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];\r\nconst DISTANCE_BASE = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];\r\nconst DISTANCE_EXTRA_BITS = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];\r\nconst CODE_LENGTH_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];\r\n\r\n/**\r\n * Inflate a zlib-wrapped deflate stream.\r\n *\r\n * This implementation is intentionally scoped to FBX binary array payloads: one-shot,\r\n * synchronous zlib streams with the exact uncompressed length known up front.\r\n */\r\nexport function inflateZlib(input: Uint8Array, expectedLength: number): Uint8Array {\r\n    if (!Number.isInteger(expectedLength) || expectedLength < 0) {\r\n        throw new Error(\"zlib: invalid expected length\");\r\n    }\r\n    if (input.byteLength < 6) {\r\n        throw new Error(\"zlib: unexpected end of input\");\r\n    }\r\n\r\n    const cmf = input[0];\r\n    const flg = input[1];\r\n    if ((cmf & 0x0f) !== 8 || cmf >> 4 > 7 || ((cmf << 8) + flg) % 31 !== 0) {\r\n        throw new Error(\"zlib: invalid header\");\r\n    }\r\n    if ((flg & 0x20) !== 0) {\r\n        throw new Error(\"zlib: preset dictionary not supported\");\r\n    }\r\n\r\n    const reader = new BitReader(input, 2, input.byteLength - 4);\r\n    const output = new OutputWriter(expectedLength);\r\n\r\n    let isFinalBlock = false;\r","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/loaders/src/FBX/parsers/zlibInflate.ts#L1-L37","documentation":"inflateZlib validates its expectedLength argument before inflating and throws 'zlib: invalid expected length' if it is not a non-negative integer. This guards the pre-allocated output buffer, which must have an exact known size for one-shot FBX array decompression.","triggerScenarios":"parseArrayProperty computing expectedByteLength as NaN/undefined (e.g., unknown element size multiplied by count), a negative product from a corrupt count field, or a non-integer from a fractional size calculation.","commonSituations":"A malformed FBX record whose element count is garbage, producing a negative or non-integral expected length; a code change introducing a bad element-size lookup; calling inflateZlib directly with an unvalidated length.","solutions":["Fix the element-count/element-size computation in parseArrayProperty so expectedByteLength is a non-negative integer","Sanity-check the FBX record's array count field — a huge or negative count means a corrupt file","If calling inflateZlib yourself, validate the length first (Number.isInteger(n) && n >= 0)","Re-export the source FBX file if the count field is corrupt"],"exampleFix":"// before\nreturn inflateZlib(compressed, count * size); // NaN if size unknown\n// after\nconst expected = count * size;\nif (!Number.isInteger(expected) || expected < 0) throw new Error(`Bad array length for ${type}`);\nreturn inflateZlib(compressed, expected);","handlingStrategy":"validation","validationCode":"const expected = count * ELEMENT_SIZES[type];\nif (!Number.isInteger(expected) || expected < 0) {\n  throw new Error(`Corrupt FBX array length for ${type}: ${expected}`);\n}\nreturn inflateZlib(compressed, expected);","typeGuard":"function isValidInflateLength(n: unknown): n is number {\n  return typeof n === 'number' && Number.isInteger(n) && n >= 0;\n}","tryCatchPattern":"try {\n  return inflateZlib(input, expectedLength);\n} catch (e) {\n  if ((e as Error).message === 'zlib: invalid expected length') {\n    throw new Error('FBX array record has corrupt element count', { cause: e });\n  }\n  throw e;\n}","preventionTips":["Validate the length argument before calling inflateZlib","Sanity-check FBX array count fields against a reasonable maximum","Add unit tests for degenerate counts (0, negative, huge)"],"tags":["zlib","compression","argument-validation","fbx"],"backgroundTag":"invalid-argument-value","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}