denoland/deno · critical

Failed to allocate EVP_CIPHER_CTX

Error message

Failed to allocate EVP_CIPHER_CTX

What it means

When node:crypto creates a ChaCha20-Poly1305 cipher via createCipheriv, Deno allocates an OpenSSL EVP_CIPHER_CTX. If that allocation fails (malloc returns null), init reports ContextAllocation and Deno panics — effectively an out-of-memory abort, not a crypto-usage error. Bad key/IV/tag inputs return normal CipherErrors; only the context allocation failure panics.

Source

Thrown at ext/node_crypto/cipher.rs:978

        }
        ChaCha20(Box::new(ChaCha20Cipher::new(key, iv)))
      }
      "chacha20-poly1305" => {
        if key.len() != 32 {
          return Err(CipherError::InvalidKeyLength);
        }
        if iv.len() != 12 {
          return Err(CipherError::InvalidInitializationVector);
        }
        let tag_len = auth_tag_length.unwrap_or(16);
        if !is_valid_chacha20_poly1305_tag_length(tag_len) {
          return Err(CipherError::InvalidAuthTag(tag_len));
        }
        ChaCha20Poly1305(Box::new(
          ChaCha20Poly1305Cipher::new(key, iv, tag_len, true).map_err(|e| {
            match e {
              CipherInitError::ContextAllocation => {
                panic!("Failed to allocate EVP_CIPHER_CTX")
              }
              CipherInitError::InitFailed => CipherError::InvalidKeyLength,
            }
          })?,
        ))
      }
      _ => return Err(CipherError::UnknownCipher(algorithm_name.to_string())),
    })
  }

  fn set_aad(&mut self, aad: &[u8]) {
    use Cipher::*;
    match self {
      Aes128Gcm(cipher, _) => {
        cipher.set_aad(aad);
      }
      Aes256Gcm(cipher, _) => {
        cipher.set_aad(aad);

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Relieve memory pressure: raise container/cgroup limits or restart the leaking process
  2. Create one cipher per message (update then final), not per chunk, so contexts are released
  3. Check for leaked Cipher objects — a missing final() keeps contexts alive
  4. If memory looks healthy, update Deno — a runtime regression would affect everyone using the cipher

Example fix

// before — new cipher context per chunk
for (const chunk of chunks) {
  const c = crypto.createCipheriv("chacha20-poly1305", key, iv);
  out.push(c.update(chunk), c.final());
}

// after — one cipher per message; final() frees the context
const c = crypto.createCipheriv("chacha20-poly1305", key, iv);
for (const chunk of chunks) out.push(c.update(chunk));
out.push(c.final());
Defensive patterns

Strategy: fallback

Validate before calling

// pre-flight: refuse to create ciphers under memory pressure
const { rss } = Deno.memoryUsage();
const total = Deno.systemMemoryInfo()?.total ?? Infinity;
if (rss > 0.85 * total) {
  throw new Error("memory pressure: defer creating new cipher contexts");
}
const c = crypto.createCipheriv("chacha20-poly1305", key, iv);

Prevention

When it happens

Trigger: `crypto.createCipheriv("chacha20-poly1305" | "id-chacha20-poly1305-ietf", key, iv)` succeeding validation but failing at EVP_CIPHER_CTX_new because the process is out of memory.

Common situations: Containers with low memory limits doing bulk encryption; workloads creating a cipher per chunk in a hot loop without calling final(); leaks elsewhere in the process crowding out the OpenSSL allocator.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/0c13973db3229f19. Report an issue: GitHub.