{"record":{"id":"a021688bf0b4157e","repo":"denoland/deno","slug":"aad-length-exceeds-i32-max","errorCode":null,"errorMessage":"AAD length exceeds i32::MAX","messagePattern":"AAD length exceeds i32::MAX","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ext/node_crypto/cipher.rs","lineNumber":383,"sourceCode":"  fn set_aad(&mut self, aad: &[u8]) {\n    self.aad_buf.extend_from_slice(aad);\n  }\n\n  /// Flush buffered AAD to EVP context. Called lazily before the first\n  /// encrypt/decrypt so that multiple setAAD() calls are concatenated.\n  fn flush_aad(&mut self) {\n    if !self.aad_flushed {\n      self.aad_flushed = true;\n      if !self.aad_buf.is_empty() {\n        // SAFETY: ctx is valid, aad_buf is a valid slice. Passing NULL\n        // output tells EVP this is AAD, not plaintext/ciphertext.\n        // Length is validated to fit in i32 before casting.\n        unsafe {\n          let aad_len: i32 = self\n            .aad_buf\n            .len()\n            .try_into()\n            .expect(\"AAD length exceeds i32::MAX\");\n          let mut outl: i32 = 0;\n          let ret = aws_lc_sys::EVP_CipherUpdate(\n            self.ctx,\n            std::ptr::null_mut(),\n            &mut outl,\n            self.aad_buf.as_ptr(),\n            aad_len,\n          );\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","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node_crypto/cipher.rs#L365-L401","documentation":"node:crypto in Deno implements chacha20-poly1305 with an EVP-based cipher that buffers AAD from setAAD() calls and flushes it to aws-lc right before the first encrypt/decrypt. EVP_CipherUpdate takes a C int length, so the buffered byte count must fit i32; this expect() panics when the cumulative AAD exceeds 2147483647 bytes (2 GiB). The panic originates in native code and crashes the process.","triggerScenarios":"const c = crypto.createCipheriv(\"chacha20-poly1305\", key, iv); c.setAAD(aad); ... where the total bytes passed to setAAD (possibly across multiple calls, since they are concatenated until the first update()) exceed 2 GiB, then the first c.update() triggers flush_aad() and the panic.","commonSituations":"Swapping arguments and accidentally passing the whole multi-gigabyte file to setAAD() instead of update(); piping an unbounded stream through setAAD in a loop; test harnesses that feed generated data into AAD without bounds. Real AEAD AAD is normally a few dozen bytes, so hitting this almost always indicates a logic bug rather than legitimate data.","solutions":["Audit the call site: AAD is metadata (headers/associated data) and should be tiny; if it is gigabytes you almost certainly meant to pass the data to update() instead of setAAD().","Cap total AAD: reject or chunk-by-redesign before the sum of setAAD() calls reaches 2^31-1 bytes.","If genuinely huge associated data is required, hash it first and use the digest (fixed 32 bytes) as the AAD.","Add a unit test asserting AAD size stays under a sane bound (e.g. 64 KiB)."],"exampleFix":"// before\ncipher.setAAD(fileBytes);        // oops: whole 3 GiB file as AAD\ncipher.update(header);           // flush_aad() -> panic\n\n// after\ncipher.setAAD(aadHeader);        // small associated data\ncipher.update(fileBytes);        // payload goes through update()","handlingStrategy":"validation","validationCode":"const MAX_I32 = 2 ** 31 - 1;\nlet aadTotal = 0;\nfunction safeSetAAD(cipher, aad) {\n  if (!Buffer.isBuffer(aad)) throw new TypeError(\"AAD must be a Buffer\");\n  if (aadTotal + aad.length > MAX_I32) {\n    throw new Error(`total AAD ${aadTotal + aad.length} exceeds 2 GiB limit`);\n  }\n  cipher.setAAD(aad);\n  aadTotal += aad.length;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep AAD to headers/associated data (bytes to a few KB); treat anything larger as a bug.","Track cumulative setAAD() bytes yourself — remember multiple calls are concatenated until the first update().","If associated data is huge by design, hash it and use the fixed-size digest as AAD."],"tags":["node-crypto","cipher","chacha20-poly1305","aad","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-14T05:17:10.506Z"}