Commit 346ac563dcd for nodejs

commit 346ac563dcd774a1fe3293e034605d9f3076708d
Author: Eliau Elkouby <eliau.elkouby@gmail.com>
Date:   Wed Sep 23 14:13:20 2026 +0300

    deps: V8: cherry-pick 786c1c2d88d4

    Original commit message:

        [stack-traces] Fix overflow in Error.stackTraceLimit trimming

        When stack traces are captured for uncaught exceptions (enabled via
        Isolate::SetCaptureStackTraceForUncaughtExceptions, e.g. by the
        inspector or by Node.js's --trace-uncaught), CaptureAndSetErrorStack
        reuses the simple stack trace and trims it to Error.stackTraceLimit.

        Error.stackTraceLimit counts frames, but the raw call site data stores
        CallSiteInfo::Fields::kCount slots per frame, so the trim multiplied the
        limit by kCount: once in the uint32_t comparison against the array
        length and once, as int, to compute the new length. GetStackTraceLimit
        clamps the limit to [0, INT_MAX], so for very large limits the uint32_t
        product can wrap to a value below the array length. The trim branch is
        then taken although the limit exceeds the number of captured frames,
        and the int multiplication of the new length overflows.

        On main (kCount == 5) the product first wraps at 858993460. That limit
        trimmed the raw data to 4 slots (no complete frame) and 858993461 to 9
        slots (one frame), so error.stack silently lost frames. Infinity, the
        value from the Node.js report, is clamped to INT_MAX; its wrapped
        product (2147483643) is not below the array length, so on main it does
        not take the trim branch and does not reach the signed overflow.

        Fix this by comparing the limit with the number of frames in the raw
        data (length / kCount), and only multiplying once the limit is known to
        be smaller than the frame count. The resulting length is then bounded
        by the existing array length and cannot overflow. Behavior for limits
        that did not overflow is unchanged, since the raw data length is always
        a multiple of kCount.

        This regressed with https://crrev.com/c/7673818 (ebd15783b7b,
        "[objects]: Defer CallSiteInfo creation"), which switched from one
        CallSiteInfo per frame to kCount raw slots per frame.

        This is the underlying cause of Node.js issue 66074. The symptom there
        differs from main: Node's V8 14.6 backport of that change has
        kCount == 6 and uses int for the comparison and for RightTrim, so the
        product overflows for limits above 357913941. For many of those,
        including Infinity (INT_MAX * 6 wraps to -6), the result is negative
        and fails "Check failed: new_capacity > 0." in RightTrim. Comparing in
        frames avoids the overflow in both cases.

        The new cctest CaptureStackTraceForUncaughtExceptionHugeStackTraceLimit
        enables capture for uncaught exceptions and checks that limits of
        858993460, 858993461 and Infinity yield the same error.stack as a limit
        of 10, and that a limit of 1 still trims to a single frame. 858993460
        and 858993461 are the first limits whose product with kCount wraps
        around uint32_t; both fail without this change. The new test and the
        existing stack trace tests also pass in a UBSan build, with no
        diagnostics.

        Bug: 565047704
        Refs: https://github.com/nodejs/node/issues/66074
        Change-Id: I3422ca1de6a7dd9448c7fd53fb9bc5e40e2a17c1
        Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8426465
        Reviewed-by: Patrick Thier <pthier@chromium.org>
        Reviewed-by: Leszek Swirski <leszeks@chromium.org>
        Auto-Submit: eliau elkouby (‫אליהו אלקובי‬‎) <eliau.elkouby@gmail.com>
        Commit-Queue: Patrick Thier <pthier@chromium.org>
        Cr-Commit-Position: refs/heads/main@{#110043}

    Refs: https://github.com/v8/v8/commit/786c1c2d88d445eecf54efe7ba9cef27a3eef3bb
    Signed-off-by: Joyee Cheung <joyeec9h3@gmail.com>
    PR-URL: https://github.com/nodejs/node/pull/65161
    Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
    Reviewed-By: Richard Lau <richard.lau@ibm.com>

diff --git a/common.gypi b/common.gypi
index 88e26799d8b..08fd76b8c2a 100644
--- a/common.gypi
+++ b/common.gypi
@@ -44,7 +44,7 @@

     # Reset this number to 0 on major V8 upgrades.
     # Increment by one for each non-official patch applied to deps/v8.
-    'v8_embedder_string': '-node.18',
+    'v8_embedder_string': '-node.19',

     ##### V8 defaults for Node.js #####

diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS
index 0b440374225..d5beee57e11 100644
--- a/deps/v8/AUTHORS
+++ b/deps/v8/AUTHORS
@@ -129,6 +129,7 @@ Douglas Crosher <dtc-v8@scieneer.com>
 Dusan Milosavljevic <dusan.m.milosavljevic@gmail.com>
 Eden Wang <nedenwang@tencent.com>
 Edoardo Marangoni <edoardo@wasmer.io>
+Eliau Elkouby <eliau.elkouby@gmail.com>
 Elisha Hollander <just4now666666@gmail.com>
 Eric Rannaud <eric.rannaud@gmail.com>
 Erich Ocean <erich.ocean@me.com>
diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc
index c78eb19216c..3dedd6a4de0 100644
--- a/deps/v8/src/execution/isolate.cc
+++ b/deps/v8/src/execution/isolate.cc
@@ -1789,12 +1789,15 @@ MaybeDirectHandle<JSObject> Isolate::CaptureAndSetErrorStack(
           static_cast<uint32_t>(
               stack_trace_for_uncaught_exceptions_frame_limit_));
       DCHECK_GE(stack_trace_limit, 0);
-      if (static_cast<uint32_t>(stack_trace_limit) *
-              CallSiteInfo::Fields::kCount <
-          raw_data_for_call_site_infos->ulength().value()) {
+      // Compare in frames rather than raw slots to avoid overflowing for
+      // large Error.stackTraceLimit values.
+      uint32_t frame_count = raw_data_for_call_site_infos->ulength().value() /
+                             CallSiteInfo::Fields::kCount;
+      if (static_cast<uint32_t>(stack_trace_limit) < frame_count) {
         call_site_infos_or_formatted_stack = FixedArray::RightTrimOrEmpty(
             this, raw_data_for_call_site_infos,
-            stack_trace_limit * CallSiteInfo::Fields::kCount);
+            static_cast<uint32_t>(stack_trace_limit) *
+                CallSiteInfo::Fields::kCount);
       }
       // Notify the debugger.
       OnStackTraceCaptured(stack_trace);
diff --git a/deps/v8/test/cctest/test-api-stack-traces.cc b/deps/v8/test/cctest/test-api-stack-traces.cc
index d81238b9c30..0bda6c06582 100644
--- a/deps/v8/test/cctest/test-api-stack-traces.cc
+++ b/deps/v8/test/cctest/test-api-stack-traces.cc
@@ -440,6 +440,34 @@ TEST(CaptureStackTraceForUncaughtException) {
   CHECK_EQ(1, report_count);
 }

+TEST(CaptureStackTraceForUncaughtExceptionHugeStackTraceLimit) {
+  LocalContext env;
+  v8::Isolate* isolate = env.isolate();
+  v8::HandleScope scope(isolate);
+  isolate->SetCaptureStackTraceForUncaughtExceptions(true);
+
+  CompileRun(
+      "function foo() { return new Error().stack; }\n"
+      "function bar() { return foo(); }\n"
+      "function stackWithLimit(limit) {\n"
+      "  Error.stackTraceLimit = limit;\n"
+      "  return bar();\n"
+      "}\n");
+  Local<Value> expected = CompileRun("stackWithLimit(10)");
+  CHECK(expected->IsString());
+
+  // For these limits, limit * CallSiteInfo::Fields::kCount overflows.
+  for (const char* limit : {"858993460", "858993461", "Infinity"}) {
+    std::string source = std::string("stackWithLimit(") + limit + ")";
+    CHECK(CompileRun(source.c_str())->StrictEquals(expected));
+  }
+
+  // Small limits must still trim the stack trace.
+  CHECK(CompileRun("stackWithLimit(1).split('\\n').length === 2")->IsTrue());
+
+  isolate->SetCaptureStackTraceForUncaughtExceptions(false);
+}
+
 // Test uncaught exception in a setter
 const char uncaught_setter_exception_source[] =
     "var setters = ['column', 'lineNumber', 'scriptName',\n"