Skip to content

Roll to V8 15.0.245.2; require &'static for CFunction overload storage - #1990

Merged
littledivy merged 5 commits into
mainfrom
orch/divybot-160
Jun 6, 2026
Merged

Roll to V8 15.0.245.2; require &'static for CFunction overload storage#1990
littledivy merged 5 commits into
mainfrom
orch/divybot-160

Conversation

@divybot

@divybot divybot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Rolls rusty_v8 to V8 15.0.245.2 and lands the type-level
&'static-lifetime fix for fast-API CFunction overload storage
(#1989). The two go together: the V8 15 ABI is what makes
the lifetime fix necessary, and this PR validates that fix against real V8 15.

V8 15 roll

  • v8 submodule → 944a94aa (15.0-lkgr-denoland, V8 15.0.245.2,
    tag 15.0.245.2-denoland-944a94aa1735e029805d)
  • V8_TRACKING_BRANCH in tools/auto_update_v8.ts15.0-lkgr-denoland
  • third_party submodules synced to the new v8/DEPS (buildtools, abseil-cpp,
    libc++/src, libunwind/src, llvm-libc/src, partition_alloc, rust, tools/clang,
    tools/win)
  • build submodule → 2aeedfc1 (Roll build patches onto upstream b787aa66 for V8 15 chromium_build#204): upstream
    chromium build @ b787aa66 (V8 15's DEPS pin) + the 6 denoland patches.
    Required because V8 15's buildtools dropped the
    //buildtools/third_party/libc++:headers target that the old build
    config injected into every GN target.
  • README V8 version → 15.0.245.2

fast_api: require &'static for CFunction overload storage

Resolves #1989 by enforcing — at the Rust type level — the
storage-lifetime invariant introduced by upstream V8 change
crrev.com/c/7828135
([fastapi] Store v8::CFunction pointer directly in FunctionTemplateInfo,
shipped in V8 15).

Before that V8 patch, FunctionTemplate::NewWithCFunctionOverloads copied
each v8::CFunction into a Managed<CFunctionWithSignature> heap object, so
the embedder's overload storage only needed to outlive the call. After it,
V8 retains the raw v8::CFunction* directly inside FunctionTemplateInfo
and reads it back on every fast-call dispatch, so the pointed-to storage
must outlive every FunctionTemplate that references it — which in
practice means 'static (templates can live until isolate disposal).

Previously FunctionBuilder::build_fast accepted any &[CFunction] and
forwarded as_ptr() to V8, so a deno_core-style call shape like
builder.build_fast(scope, &[fast_function]) would silently dangle under the
V8 15 ABI. This PR makes that misuse a compile error today instead of a
use-after-free tomorrow.

Changes

  • FunctionBuilder::build_fast now takes overloads: &'static [CFunction].
  • fast_api::CFunction::new now takes type_info: &'static CFunctionInfo,
    so the type_info_ pointer it caches cannot dangle either.
  • fast_api::CFunctionInfo::new now takes arg_info: &'static [CTypeInfo],
    same reason — the constructed CFunctionInfo caches arg_info.as_ptr().
  • Doc comments on all three explain the invariant and the canonical
    static-promoted const pattern.

Tests

The existing fast-call call sites in tests/test_api.rs and
benches/function.rs already use the canonical pattern —

const FAST_TEST: fast_api::CFunction = fast_api::CFunction::new(
    fast_fn as _,
    &fast_api::CFunctionInfo::new(/* ... */),
);
// ...
.build_fast(scope, &[FAST_TEST]);

— which Rust static-promotes to &'static [CFunction; 1], so no fast-call
test changes are required.

A new trybuild compile_fail test
tests/compile_fail/build_fast_local_overloads.rs
demonstrates that a runtime-built Vec<CFunction> is now rejected with
E0597 — argument requires that 'overloads' is borrowed for 'static.

One unrelated test needed a V8-15 adjustment: security_token. V8 15 ships
Atomics.pause, whose Genesis::InitializeGlobal_js_atomics_pause installer
runs GetProperty(global, "Atomics") while bootstrapping every context. That
test installed a masking named-property interceptor on the child context's
global delegating all lookups to the parent global, so the bootstrap lookup
returned the parent's Atomics (already carrying pause); V8 reinstalled
pause and aborted with a duplicate-descriptor CHECK. Marking the interceptor
NON_MASKING (so it only fires for properties absent on the global — just the
variable the test reads) avoids the collision and preserves the test's
behavior.

Context

Closes denoland/divybot#160

Upstream crrev.com/c/7828135 (slated for V8 15) changes
FunctionTemplateInfo to store the raw v8::CFunction pointers passed to
NewWithCFunctionOverloads directly, rather than copying them into a
Managed<CFunctionWithSignature> heap object. The pointed-to storage must
therefore outlive every FunctionTemplate that references it - in
practice this means it has to be 'static, since a FunctionTemplate can
live until isolate disposal.

Enforce the invariant at the Rust type level so embedders cannot pass
stack-local overload storage:

* FunctionBuilder::build_fast now requires &'static [CFunction].
* CFunction::new now requires &'static CFunctionInfo so the type_info
  pointer it caches cannot dangle.
* CFunctionInfo::new now requires &'static [CTypeInfo] for arg_info for
  the same reason - the constructed CFunctionInfo caches arg_info.as_ptr().

The existing call sites in tests/test_api.rs and benches/function.rs
already use the canonical pattern - const FAST: CFunction = ...; build_fast(
scope, &[FAST]) - which Rust static-promotes to &'static [CFunction; 1],
so no test changes are required. A new trybuild compile_fail test
(tests/compile_fail/build_fast_local_overloads.rs) demonstrates that a
runtime-built Vec<CFunction> is now rejected with E0597.

Refs #1989
Closes denoland/orchid#160

Co-Authored-By: Divy Srivastava <me@littledivy.com>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@divybot divybot changed the title rusty_v8: make fast API CFunction overload storage V8-15 safe fast_api: require &'static for CFunction overload storage May 20, 2026
@littledivy

Copy link
Copy Markdown
Member

@divybot Looks like lgkr was cut 2 days ago https://chromium.googlesource.com/v8/v8/+/refs/heads/15.0-lkgr Let's update bump v8 submodule (roll it in the repo) and use it here

@divybot

divybot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

@littledivy I dug into rolling the submodule. Upstream 15.0-lkgr is cut, but the roll tooling here (tools/auto_update_v8.tstools/update_deps.py) tracks the patched <ver>-lkgr-denoland branch in denoland/v8, and that fork currently tops out at 14.9-lkgr-denoland — there's no 15.0-lkgr-denoland yet. Pointing the submodule straight at raw upstream 15.0-lkgr would drop the denoland build/config patches and break the GN build, so the patched branch is required first.

I only have read access to denoland/v8, so I can't cut/push that branch myself. Once 15.0-lkgr-denoland exists in the fork, I'll:

  • point the v8 submodule at it,
  • run tools/update_deps.py to sync the third_party submodules,
  • bump the README V8 version (currently 14.9.207.2),

and push the roll onto this branch so the &'static [CFunction] change is validated against real V8 15. Happy to prep the patch rebase (denoland patches onto upstream 15.0-lkgr) and open a PR against denoland/v8 from a fork if you'd prefer I drive that part too — just let me know.

@littledivy

Copy link
Copy Markdown
Member

@divybot 15.0-lkgr-denoland now exists in denoland/v8 (upstream 15.0-lkgr = V8 15.0.245.2 + the build patches, tag 15.0.245.2-denoland-944a94aa1735e029805d). The autoroll now maintains it nightly alongside 14.9.

Go ahead with the roll on this branch:

  • point the v8 submodule at 15.0-lkgr-denoland,
  • bump V8_TRACKING_BRANCH in tools/auto_update_v8.ts from 14.9-lkgr-denoland15.0-lkgr-denoland,
  • run tools/update_deps.py to sync third_party submodules,
  • bump the README V8 version to 15.0.245.2,

then push so the &'static [CFunction] change gets validated against real V8 15. Thanks for digging into the branch situation.

@divybot

divybot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Done — rolled in commit 52dbe57:

  • v8 submodule → 944a94aa (15.0-lkgr-denoland, V8 15.0.245.2)
  • tools/auto_update_v8.ts: V8_TRACKING_BRANCH15.0-lkgr-denoland
  • third_party submodules synced to the new v8/DEPS (changed: buildtools, abseil-cpp, libc++/src, libunwind/src, llvm-libc/src, partition_alloc, rust, tools/clang, tools/win; build left on the denoland fork as before)
  • README V8 version → 15.0.245.2

CI is running now and will validate the &'static [CFunction] change against real V8 15. I'll watch it and address any failures.

@divybot

divybot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Rolled in 52dbe57, but the build-from-source CI jobs (ptrcomp / asan / Windows cross-compile) fail at gn gen with:

ERROR Unresolved dependencies.
  //:rusty_v8(...) needs //buildtools/third_party/libc++:headers(...)
  // ...and every v8 test target needs the same

Root cause — the build submodule (denoland/chromium_build fork) is behind V8 15's buildtools:

  • V8 15's DEPS pins buildtools17495e45, which removed the group("headers") from buildtools/third_party/libc++/BUILD.gn.
  • Upstream chromium build at V8 15's pinned commit b787aa66 matches that: its config/BUILDCONFIG.gn now uses //buildtools/third_party/libc++:all_modules + the bare //buildtools/third_party/libc++ target, with no :headers.
  • But our build gitlink is still fffa45f2 (the fork's newest branch 20260506), whose config/BUILDCONFIG.gn injects public_deps += [ "//buildtools/third_party/libc++:headers" ] into every target → unresolved against the new buildtools.

tools/update_deps.py skips build ("we have our own fork"), so the roll didn't advance it. The denoland/chromium_build fork doesn't have a V8-15-compatible commit yet — its branches top out at 20260506, and b787aa66 isn't in the fork's history (GitHub compare 404s). I only have read access there, so I can't cut it (same as the v8 fork branch situation).

What's needed: a denoland/chromium_build commit based on V8 15's upstream build b787aa66 (with the rusty_v8 patches) pushed to the fork. Once it exists, point me at it (or push it) and I'll bump the build gitlink and re-push so the build-from-source variants go green. Happy to prep the patch rebase via a fork PR if you'd like me to drive it.

The v8 + third_party + README + tracking-branch changes from 52dbe57 are otherwise correct; only the build submodule bump is missing.

@littledivy

Copy link
Copy Markdown
Member

Yep please do that. Get this CI green

@divybot

divybot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Drove the build-submodule rebase. Opened denoland/chromium_build#204:

  • Base: upstream chromium/src/build @ b787aa66 (V8 15.0.245.2's DEPS pin)
  • + 6 denoland patches re-applied from the 20260506 snapshot (tip 2aeedfc1aae7eca3852cfe921657bc62330baaf9)
  • Only one patch needed a manual merge — "Add line table options…" in config/compiler/BUILD.gn (upstream had independently added !use_dwarf5 and a compress_debug_sections block; merged both with the denoland line_tables_only logic). Other 5 applied clean.

This fixes the gn gen failure: b787aa66's BUILDCONFIG.gn uses //buildtools/third_party/libc++:all_modules instead of the removed :headers target (verified: no :headers refs remain in the rebased tree).

Once #204 lands in denoland/chromium_build (merge or just push the branch as the canonical V8-15 build branch), I'll bump the build gitlink here to that commit and the build-from-source CI should go green.

Advance the build submodule from fffa45f2 to 2aeedfc1: the denoland
chromium_build patches re-applied onto upstream build b787aa66 (the commit
V8 15.0.245.2 pins in DEPS). b787aa66's BUILDCONFIG.gn uses
//buildtools/third_party/libc++:all_modules instead of the removed
:headers target, fixing the 'gn gen' unresolved-dependency failure on the
build-from-source CI variants (ptrcomp / asan / Windows cross-compile).

See denoland/chromium_build#204.
@divybot

divybot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Bumped the build gitlink to 2aeedfc1 (chromium_build#204's tip) in 926d558. Confirmed git fetch https://github.com/denoland/chromium_build.git 2aeedfc1 resolves via the fork network, so CI's submodule update can fetch it even before #204 is formally landed. Watching the build-from-source variants now.

Note: if #204 gets squash/rebase-merged (new SHA) rather than pushed as a branch, 2aeedfc1 could eventually be GC'd from the network — pushing it as a branch (or a normal merge) keeps the gitlink valid long-term.

V8 15 ships Atomics.pause: Genesis::InitializeGlobal_js_atomics_pause now
does a GetProperty(global, "Atomics") while bootstrapping every context.
The security_token test installs a *masking* named-property interceptor on
the child context's global that delegates all lookups to the parent global,
so that bootstrap lookup returned the parent's Atomics (which already had
pause), V8 reinstalled pause, and aborted with a duplicate-descriptor CHECK.

Make the interceptor NON_MASKING so it only fires for properties absent on
the global (just `variable`, which is all the test accesses). This skips
already-present built-ins during bootstrap and avoids the collision while
preserving the test's behavior.
@divybot

divybot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Build-from-source CI is now healthy — gn gen resolves and V8 15.0.245.2 compiles and links across all variants (the build-submodule bump to 2aeedfc1 did the job). The run got all the way into the test suite: 295/296 passed, with one genuine V8-15 regression I've now fixed in b5fcb91.

Failure: security_token aborted during v8::Context::New with:

# Check failed: LinearSearch(*desc->GetKey(), descriptor_number) == InternalIndex::NotFound().
  ... DescriptorArray::Append
  ... Genesis::InitializeGlobal_js_atomics_pause
  ... Genesis::InitializeExperimentalGlobal

Root cause: Atomics.pause graduated to a shipping feature in V8 15 (JAVASCRIPT_SHIPPING_FEATURES, default on). It's still installed via the experimental hook InitializeGlobal_js_atomics_pause, which now runs for every context and does GetProperty(global, "Atomics") during bootstrap. security_token installs a masking named-property interceptor on the child context's global that delegates every lookup to the parent global — so that bootstrap "Atomics" lookup returned the parent's Atomics (which already had pause). V8 then reinstalled pause on it → duplicate descriptor → CHECK abort. On V8 14.9 the hook was a no-op (feature off), so the lookup never happened. The other five global_template tests pass because they use NON_MASKING interceptors (or none).

Fix: make the security_token interceptor NON_MASKING, so it only fires for properties absent on the global (just variable, which is all the test ever reads). Built-in lookups during bootstrap no longer hit the interceptor, and both assertions (token grants access / no-token yields "no access") are unchanged.

This is a test-only artifact — a real catch-all masking global interceptor that shadows built-ins is not something rusty_v8/Deno embedders do — so no library or V8-fork change is needed. CI is re-running now; I'll confirm green.

@divybot

divybot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

✅ CI is green — run 27046401847 passed all 28 build/test jobs (debug+release × linux/macOS/Windows × simdutf/ptrcomp/asan). The previously-crashing security_token now passes everywhere with the NON_MASKING fix in b5fcb91.

The full V8 15 roll is now validated end-to-end against the &'static [CFunction] change:

  • v8 submodule → 944a94aa (15.0-lkgr-denoland, V8 15.0.245.2), V8_TRACKING_BRANCH15.0-lkgr-denoland
  • build2aeedfc1 (chromium_build#204) + third_party submodules synced
  • README → 15.0.245.2
  • fast_api: CFunction/CFunctionInfo/build_fast now require &'static (+ compile_fail test)
  • security_token test made NON_MASKING for V8 15's Atomics.pause bootstrap

Only remaining check is license/cla (bot-pending) — flagging in case a recheck is needed. Ready for review.

@littledivy

Copy link
Copy Markdown
Member

Update title and desc

@divybot divybot changed the title fast_api: require &'static for CFunction overload storage Roll to V8 15.0.245.2; require &'static for CFunction overload storage Jun 6, 2026
@littledivy

Copy link
Copy Markdown
Member

Build dep is rolled up: chromium_build now has the dated snapshot branch 20260606 pointing at 2aeedfc1aae7eca3852cfe921657bc62330baaf9 — the exact SHA this PR already pins the build submodule to. denoland/chromium_build#204 was closed (rolled, not merged onto upstream). The submodule no longer depends on a fork branch, so this is unblocked.

@divybot

divybot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Confirmed — no change needed. The build submodule on this branch already pins 2aeedfc1aae7eca3852cfe921657bc62330baaf9, which is exactly the tip of the new 20260606 snapshot branch:

$ git ls-tree orch/divybot-160 build
160000 commit 2aeedfc1aae7eca3852cfe921657bc62330baaf9  build
$ git ls-remote https://github.com/denoland/chromium_build.git refs/heads/20260606
2aeedfc1aae7eca3852cfe921657bc62330baaf9  refs/heads/20260606

This also clears the GC-reachability concern I raised earlier — the SHA is now anchored to a permanent dated branch instead of a closed PR's ref, so the gitlink stays valid long-term. CI is green (run 27046401847); only license/cla (bot) remains. Ready for review.

@littledivy

Copy link
Copy Markdown
Member

Review: looks good ✅

Cross-checked against the prior major roll (#1940, 14.6→14.7) — conventions all match.

Roll structure (matches #1940)

  • tools/auto_update_v8.ts tracking branch 14.9-lkgr-denoland15.0-lkgr-denoland
  • README.md 14.9.207.215.0.245.2
  • build submodule → 2aeedfc1 (chromium_build snapshot branch 20260606) ✓
  • buildtools17495e45 — exactly what the chromium_build base b787aa66 expects ✓
  • v8 + all third_party/*, tools/* bumped consistently ✓

Code changes — correct and well-documented

  • Core soundness fix: &'static now required on CFunction::new / CFunctionInfo::new / build_fast overloads. Right call — since crrev.com/c/7828135 V8 stores the raw v8::CFunction pointers directly in FunctionTemplateInfo (no copy), so non-'static storage would dangle. Fixes fast_api: tighten FunctionTemplateBuilder::build_fast overload-list lifetime for V8 15.x #1989.
  • compile_fail/build_fast_local_overloads regression test locks the invariant in. 👍
  • security_token fix (NON_MASKING): correct diagnosis — V8 15's Atomics.pause genesis does GetProperty(global, "Atomics") during context bootstrap; a masking handler answered with the parent's Atomics → duplicate-descriptor CHECK abort.

CI: all 28 build/test jobs green (debug+release × linux/macOS/windows × simdutf/ptrcomp/asan). Only license/cla pending.

One non-blocking nit: the v8 submodule pin 944a94aa has diverged from the 15.0-lkgr-denoland tip ac1e2398 (ahead 4 / behind 4). The 4 commits on each side are the same denoland patches (identical messages/order) re-cherry-picked onto the same Version 15.0.245.2 base — functionally equivalent, and CI built green against the pin. Optionally repin to the branch tip for tidiness, but not required.

Nothing blocking — ship-ready once CLA clears.

Same content as the previous pin 944a94aa (identical tree
ff06e1a909291763a6f8526b44079551f037246a and byte-identical DEPS) — the
denoland patch series was re-cherry-picked onto the same V8 15.0.245.2
base. Repinning to the branch tip so the submodule matches
V8_TRACKING_BRANCH (15.0-lkgr-denoland) and tools/auto_update_v8.ts no
longer sees the pin as diverged. No third_party changes.
@divybot

divybot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed the nit — repinned the v8 submodule to the 15.0-lkgr-denoland tip ac1e2398 (was 944a94aa) in 354fa64.

Verified the repin is content-neutral before pushing:

  • Identical tree: both commits resolve to tree ff06e1a909291763a6f8526b44079551f037246a — byte-for-byte identical source (the denoland patch series re-cherry-picked onto the same Version 15.0.245.2 base, exactly as you noted).
  • Identical DEPS: DEPS is byte-identical between the two, so no third_party/* changes were needed.
  • The submodule now matches V8_TRACKING_BRANCH (15.0-lkgr-denoland), so tools/auto_update_v8.ts won't report the pin as diverged.

CI is re-running against the tidied pin; I'll confirm it stays green. Only license/cla remains outstanding.

@littledivy

Copy link
Copy Markdown
Member

@divybot Open follow up orchid issue for upgrading this in Deno

@littledivy
littledivy merged commit c44c838 into main Jun 6, 2026
29 of 30 checks passed
@littledivy
littledivy deleted the orch/divybot-160 branch June 6, 2026 14:27
@littledivy littledivy mentioned this pull request Jun 7, 2026
This was referenced Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fast_api: tighten FunctionTemplateBuilder::build_fast overload-list lifetime for V8 15.x

3 participants