Skip to content

buffer: avoid fast API calls when computing UTF-8 byte length - #67134

Closed
ronag wants to merge 1 commit into
nodejs:mainfrom
ronag:codex/buffer-bytelength-gc-20261029
Closed

ronag wants to merge 1 commit into
nodejs:mainfrom
ronag:codex/buffer-bytelength-gc-20261029

Conversation

@ronag

@ronag ronag commented Oct 30, 2026

Copy link
Copy Markdown
Member

Buffer.byteLength() can abort when flattening a string triggers a GC that
lazily deoptimizes its optimized caller. V8 does not support that
deoptimization after a value-returning fast API call, including when the
call takes its slow fallback.

Register byteLengthUtf8 as a regular callback and remove its fast callback.
The existing SIMD path for large two-byte strings remains. This removes
the fast API optimization for this binding and avoids shared result state
or startup changes.

Add a GC regression and coverage for flat, Latin-1, cons, sliced, two-byte,
lone-surrogate and external strings before and after optimization.

Validation on Linux x64, release build of main:

  • Base 502c117: the new GC regression aborts in 3/3 runs.
  • Candidate 7f67aa5: the same regression passes in 10/10 runs.
  • All 502 buffer, HTTP and string-decoder tests pass.
  • ESLint, cpplint, import, C++ formatting and whitespace checks pass.

Fixes: #66870

AI assistance: Codex helped prepare the change and regression tests.

String flattening can allocate and invalidate the optimized caller during
a byteLengthUtf8 call. V8 cannot lazily deoptimize a value-returning fast
API call, including its slow fallback.

Register byteLengthUtf8 as a regular callback so both flat and non-flat
strings can be measured without the unsupported deoptimization. Keep the
existing SIMD path for large two-byte strings.

Add a GC regression and coverage for optimized calls with different string
representations.

Fixes: nodejs#66870
Assisted-by: Codex
Signed-off-by: Robert Nagy <ronagy@icloud.com>
@ronag
ronag requested a balanced review from Copilot October 30, 2026 07:22
@nodejs-github-bot nodejs-github-bot added buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. labels Oct 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The regression depends on V8 optimization and GC-triggered lazy deoptimization, requiring human runtime verification of pre-fix failure, post-fix success, and stability across supported CI builds.

No open issues 🎉

What changed in this PR

This PR addresses the Buffer.byteLength() process abort reported in #66870 by replacing its UTF-8 fast API binding with a regular callback, allowing garbage-collection-triggered deoptimization.

Changes:

  • Removes the fast callback and its registration while retaining the existing SIMD path for large two-byte strings.
  • Adds a regression test for garbage collection during string flattening.
  • Adds string-representation coverage before and after optimization.
File Description
test/​parallel/​test-buffer-bytelength-strings.js Checks UTF-8 byte lengths across string representations before and after optimization.
test/​parallel/​test-buffer-bytelength-gc.js Exercises the GC-triggered deoptimization that caused the abort.
src/​node_buffer.cc Registers a regular UTF-8 byte-length callback and removes the fast API implementation.

Review effort: Balanced

@ronag
ronag marked this pull request as ready for review October 30, 2026 07:29
@ronag
ronag requested review from anonrig and mcollina October 30, 2026 07:29
@ronag

ronag commented Oct 30, 2026

Copy link
Copy Markdown
Member Author

Investigating how to possibly re-enable the optimization. But this is pretty bad and should be fixed ASAP.

@codecov

codecov Bot commented Oct 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.68%. Comparing base (502c117) to head (7f67aa5).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #67134      +/-   ##
==========================================
+ Coverage   90.66%   90.68%   +0.02%     
==========================================
  Files         797      797              
  Lines      278791   278743      -48     
  Branches    53620    53602      -18     
==========================================
+ Hits       252768   252791      +23     
+ Misses      16376    16306      -70     
+ Partials     9647     9646       -1     
Files with missing lines Coverage Δ
src/node_buffer.cc 74.08% <100.00%> (+2.41%) ⬆️

... and 29 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Abort "Lazy deopt after a fast API call with return value is unsupported" in Buffer.byteLength called from maybePrepareFinalChunk / res.end(string)

3 participants