]> dgit.raspbian.org Git - nodejs.git/commitdiff
crypto: guard WebCrypto cipher output length
authorFilip Skokan <panva.ip@gmail.com>
Mon, 25 May 2026 09:09:31 +0000 (11:09 +0200)
committerBastien Roucariès <rouca@debian.org>
Sun, 16 Aug 2026 17:34:06 +0000 (19:34 +0200)
Reject WebCrypto cipher operations whose computed output length would
exceed INT_MAX before passing the length to OpenSSL.

This avoids signed overflow in the AES and ChaCha20-Poly1305 one-shot
cipher paths and turns oversized inputs into a clean operation failure.

Refs: https://hackerone.com/reports/3760016
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/879
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
PR-URL: https://github.com/nodejs-private/node-private/pull/878
CVE-ID: CVE-2026-48933
origin: backport, https://github.com/nodejs/node/commit/38b4c5ed51b2ec81c28fbd379fea72e22fa12a15
bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#nodejs-webcrypto-aes-integer-overflow-leads-to-remote-process-abort-dos-cve-2026-48933---high

Gbp-Pq: Topic sec
Gbp-Pq: Name CVE-2026-48933.patch

src/crypto/crypto_aes.cc
src/crypto/crypto_cipher.h
test/cctest/test_node_crypto.cc

index c1c5bf762a765fc2fba705c6a6a1e707d9078e89..e47ef3ef5e08cfc6092c5352dede11307b9fda8f 100644 (file)
@@ -110,7 +110,15 @@ WebCryptoCipherStatus AES_Cipher(
   }
 
   size_t total = 0;
-  int buf_len = in.size() + EVP_CIPHER_CTX_block_size(ctx.get()) + tag_len;
+  const int block_size = EVP_CIPHER_CTX_block_size(ctx.get());
+  if (block_size < 0) {
+    return WebCryptoCipherStatus::FAILED;
+  }
+  int buf_len;
+  if (!TryGetIntCipherOutputLength(
+          in.size(), static_cast<size_t>(block_size) + tag_len, &buf_len)) {
+    return WebCryptoCipherStatus::FAILED;
+  }
   int out_len;
 
   if (mode == EVP_CIPH_GCM_MODE &&
@@ -146,7 +154,7 @@ WebCryptoCipherStatus AES_Cipher(
 
   total += out_len;
   CHECK_LE(out_len, buf_len);
-  out_len = EVP_CIPHER_CTX_block_size(ctx.get());
+  out_len = block_size;
   if (!EVP_CipherFinal_ex(
           ctx.get(), buf.data<unsigned char>() + total, &out_len)) {
     return WebCryptoCipherStatus::FAILED;
index e725a2f30dc6b0ef2c63e96f48495ba901044a0b..98811090ed283c46ee547a2e95924db81f611245 100644 (file)
@@ -10,6 +10,7 @@
 #include "memory_tracker.h"
 #include "v8.h"
 
+#include <climits>
 #include <string>
 
 namespace node {
@@ -134,6 +135,18 @@ enum class WebCryptoCipherStatus {
   FAILED
 };
 
+inline bool TryGetIntCipherOutputLength(size_t input_len,
+                                        size_t output_overhead,
+                                        int* output_len) {
+  static constexpr size_t kMaxLength = INT_MAX;
+  if (output_overhead > kMaxLength ||
+      input_len > kMaxLength - output_overhead) {
+    return false;
+  }
+  *output_len = static_cast<int>(input_len + output_overhead);
+  return true;
+}
+
 // CipherJob is a base implementation class for implementations of
 // one-shot sync and async ciphers. It has been added primarily to
 // support the AES and RSA ciphers underlying the WebCrypt API.
index 9d6405a40d90c707d2da411ae09faf22851cb863..00dd6304b01c8be0584bf35aa76270c2ab6b2f69 100644 (file)
@@ -2,10 +2,13 @@
 // and setting it to a file that does not exist.
 #define NODE_OPENSSL_SYSTEM_CERT_PATH "/missing/ca.pem"
 
+#include "crypto/crypto_cipher.h"
 #include "crypto/crypto_context.h"
+#include "gtest/gtest.h"
 #include "node_options.h"
 #include "openssl/err.h"
-#include "gtest/gtest.h"
+
+#include <climits>
 
 /*
  * This test verifies that a call to NewRootCertDir with the build time
@@ -21,3 +24,17 @@ TEST(NodeCrypto, NewRootCertStore) {
                                       "any errors on the OpenSSL error stack\n";
   X509_STORE_free(store);
 }
+
+TEST(NodeCrypto, TryGetIntCipherOutputLength) {
+  int output_len = 0;
+
+  EXPECT_TRUE(
+      node::crypto::TryGetIntCipherOutputLength(INT_MAX - 16, 16, &output_len));
+  EXPECT_EQ(output_len, INT_MAX);
+
+  EXPECT_FALSE(
+      node::crypto::TryGetIntCipherOutputLength(INT_MAX - 15, 16, &output_len));
+
+  EXPECT_FALSE(node::crypto::TryGetIntCipherOutputLength(
+      0, static_cast<size_t>(INT_MAX) + 1, &output_len));
+}