{"record":{"id":"2881c6d45917b858","repo":"denoland/deno","slug":"input-length-exceeds-i32-max","errorCode":null,"errorMessage":"input length exceeds i32::MAX","messagePattern":"input length exceeds i32::MAX","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ext/node_crypto/cipher.rs","lineNumber":409,"sourceCode":"          );\n          assert_eq!(ret, 1, \"EVP_CipherUpdate for AAD failed\");\n        }\n      }\n    }\n  }\n\n  fn encrypt(&mut self, input: &[u8], output: &mut [u8]) {\n    assert!(output.len() >= input.len());\n    self.flush_aad();\n    // SAFETY: ctx is valid and initialized for encryption. output is\n    // caller-provided with at least input.len() bytes. EVP_CipherUpdate\n    // writes at most input.len() bytes for a stream cipher.\n    // Length is validated to fit in i32 before casting.\n    unsafe {\n      let input_len: i32 = input\n        .len()\n        .try_into()\n        .expect(\"input length exceeds i32::MAX\");\n      let mut outl: i32 = 0;\n      let ret = aws_lc_sys::EVP_CipherUpdate(\n        self.ctx,\n        output.as_mut_ptr(),\n        &mut outl,\n        input.as_ptr(),\n        input_len,\n      );\n      assert_eq!(ret, 1, \"EVP_CipherUpdate for encryption failed\");\n    }\n  }\n\n  fn decrypt(&mut self, input: &[u8], output: &mut [u8]) {\n    assert!(output.len() >= input.len());\n    self.flush_aad();\n    // SAFETY: ctx is valid and initialized for decryption. output is\n    // caller-provided with at least input.len() bytes.\n    // Length is validated to fit in i32 before casting.","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node_crypto/cipher.rs#L391-L427","documentation":"Deno's node:crypto chacha20-poly1305 cipher passes each update() input to EVP_CipherUpdate, whose length parameter is a C int. encrypt() converts input.len() to i32 and this expect() panics when a single plaintext chunk handed to cipher.update() is larger than 2147483647 bytes (2 GiB). The panic occurs in native code during the update call and aborts the process.","triggerScenarios":"crypto.createCipheriv(\"chacha20-poly1305\", key, iv) followed by cipher.update(buf) where buf is a single Buffer/TypedArray larger than 2 GiB — typically the result of reading an entire large file into memory (await Deno.readFile / fs.promises.readFile) and encrypting it in one call.","commonSituations":"Backup/sync scripts that read whole files >2 GiB into one Buffer; test code generating one giant buffer; porting Node scripts after a Deno upgrade where sizes previously worked via a different cipher path; concatenating stream chunks into one buffer before encrypting.","solutions":["Encrypt in chunks well under 2 GiB per update() call — e.g. 4–64 MiB slices: `for (let o = 0; o < data.length; o += CHUNK) cipher.update(data.subarray(o, Math.min(o + CHUNK, data.length)));`","Stream the file instead of buffering it: read with fs.createReadStream and pipe chunks through cipher.update().","Validate input size before calling update() and throw a clear JS error (you control the message) instead of letting the process abort.","For one-shot small payloads (the normal case) nothing changes; guard only the large-file path."],"exampleFix":"// before\nconst data = await Deno.readFile(\"big.bin\"); // 3 GiB\ncipher.update(data); // single >2GiB call -> panic\n\n// after\nconst CHUNK = 64 * 1024 * 1024;\nfor (let o = 0; o < data.length; o += CHUNK) {\n  cipher.update(data.subarray(o, Math.min(o + CHUNK, data.length)));\n}","handlingStrategy":"validation","validationCode":"const MAX_I32 = 2 ** 31 - 1;\nconst CHUNK = 64 * 1024 * 1024;\nfunction updateChunked(cipher, data) {\n  if (data.byteLength > MAX_I32) {\n    for (let o = 0; o < data.length; o += CHUNK) {\n      cipher.update(data.subarray(o, Math.min(o + CHUNK, data.length)));\n    }\n  } else {\n    cipher.update(data);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never pass a single buffer larger than 2 GiB to cipher.update(); slice into 4–64 MiB chunks.","Stream large files with fs.createReadStream instead of reading them whole.","Apply identical chunking on encrypt and decrypt sides to keep AAD flush and tag ordering consistent."],"tags":["node-crypto","cipher","chacha20-poly1305","encrypt","size-limit","panic","native"],"backgroundTag":"input-exceeds-size-limit","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}