diff --git a/.bazelrc b/.bazelrc index a5fea40281..911e0802b5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -183,5 +183,15 @@ common:ci-v8 --build_metadata=TAG_os=linux common:ci-v8 --config=remote common:ci-v8 --strategy=remote +# Source-built Bazel V8 artifacts use the in-process sandbox by default. This +# does not affect Cargo's default prebuilt rusty_v8 path. +common --@v8//:v8_enable_pointer_compression=True +common --@v8//:v8_enable_sandbox=True + +# Keep currently published rusty_v8 release artifacts non-sandboxed until the +# artifact migration ships matching Rust feature selection for Cargo consumers. +common:v8-release-compat --@v8//:v8_enable_pointer_compression=False +common:v8-release-compat --@v8//:v8_enable_sandbox=False + # Optional per-user local overrides. try-import %workspace%/user.bazelrc diff --git a/.github/scripts/rusty_v8_bazel.py b/.github/scripts/rusty_v8_bazel.py index ec73e0e5a7..5ad6d3c408 100644 --- a/.github/scripts/rusty_v8_bazel.py +++ b/.github/scripts/rusty_v8_bazel.py @@ -63,8 +63,10 @@ def bazel_output_files( platform: str, labels: list[str], compilation_mode: str = "fastbuild", + bazel_configs: list[str] | None = None, ) -> list[Path]: expression = "set(" + " ".join(labels) + ")" + bazel_configs = bazel_configs or [] result = subprocess.run( [ "bazel", @@ -72,6 +74,7 @@ def bazel_output_files( "-c", compilation_mode, f"--platforms=@llvm//platforms:{platform}", + *[f"--config={config}" for config in bazel_configs], "--output=files", expression, ], @@ -87,7 +90,9 @@ def bazel_build( platform: str, labels: list[str], compilation_mode: str = "fastbuild", + bazel_configs: list[str] | None = None, ) -> None: + bazel_configs = bazel_configs or [] subprocess.run( [ "bazel", @@ -95,6 +100,7 @@ def bazel_build( "-c", compilation_mode, f"--platforms=@llvm//platforms:{platform}", + *[f"--config={config}" for config in bazel_configs], *labels, ], cwd=ROOT, @@ -106,13 +112,14 @@ def ensure_bazel_output_files( platform: str, labels: list[str], compilation_mode: str = "fastbuild", + bazel_configs: list[str] | None = None, ) -> list[Path]: - outputs = bazel_output_files(platform, labels, compilation_mode) + outputs = bazel_output_files(platform, labels, compilation_mode, bazel_configs) if all(path.exists() for path in outputs): return outputs - bazel_build(platform, labels, compilation_mode) - outputs = bazel_output_files(platform, labels, compilation_mode) + bazel_build(platform, labels, compilation_mode, bazel_configs) + outputs = bazel_output_files(platform, labels, compilation_mode, bazel_configs) missing = [str(path) for path in outputs if not path.exists()] if missing: raise SystemExit(f"missing built outputs for {labels}: {missing}") @@ -187,8 +194,9 @@ def single_bazel_output_file( platform: str, label: str, compilation_mode: str = "fastbuild", + bazel_configs: list[str] | None = None, ) -> Path: - outputs = ensure_bazel_output_files(platform, [label], compilation_mode) + outputs = ensure_bazel_output_files(platform, [label], compilation_mode, bazel_configs) if len(outputs) != 1: raise SystemExit(f"expected exactly one output for {label}, found {outputs}") return outputs[0] @@ -198,11 +206,17 @@ def merged_musl_archive( platform: str, lib_path: Path, compilation_mode: str = "fastbuild", + bazel_configs: list[str] | None = None, ) -> Path: - llvm_ar = single_bazel_output_file(platform, LLVM_AR_LABEL, compilation_mode) - llvm_ranlib = single_bazel_output_file(platform, LLVM_RANLIB_LABEL, compilation_mode) + llvm_ar = single_bazel_output_file(platform, LLVM_AR_LABEL, compilation_mode, bazel_configs) + llvm_ranlib = single_bazel_output_file( + platform, + LLVM_RANLIB_LABEL, + compilation_mode, + bazel_configs, + ) runtime_archives = [ - single_bazel_output_file(platform, label, compilation_mode) + single_bazel_output_file(platform, label, compilation_mode, bazel_configs) for label in MUSL_RUNTIME_ARCHIVE_LABELS ] @@ -233,11 +247,13 @@ def stage_release_pair( target: str, output_dir: Path, compilation_mode: str = "fastbuild", + bazel_configs: list[str] | None = None, ) -> None: outputs = ensure_bazel_output_files( platform, [release_pair_label(target)], compilation_mode, + bazel_configs, ) try: @@ -254,7 +270,7 @@ def stage_release_pair( staged_library = output_dir / staged_archive_name(target, lib_path) staged_binding = output_dir / f"src_binding_release_{target}.rs" source_archive = ( - merged_musl_archive(platform, lib_path, compilation_mode) + merged_musl_archive(platform, lib_path, compilation_mode, bazel_configs) if is_musl_archive_target(target, lib_path) else lib_path ) @@ -293,6 +309,12 @@ def parse_args() -> argparse.Namespace: stage_release_pair_parser.add_argument("--platform", required=True) stage_release_pair_parser.add_argument("--target", required=True) stage_release_pair_parser.add_argument("--output-dir", required=True) + stage_release_pair_parser.add_argument( + "--bazel-config", + action="append", + default=[], + dest="bazel_configs", + ) stage_release_pair_parser.add_argument( "--compilation-mode", default="fastbuild", @@ -330,6 +352,7 @@ def main() -> int: target=args.target, output_dir=Path(args.output_dir), compilation_mode=args.compilation_mode, + bazel_configs=args.bazel_configs, ) return 0 if args.command == "resolved-v8-crate-version": diff --git a/.github/scripts/verify_cargo_workspace_manifests.py b/.github/scripts/verify_cargo_workspace_manifests.py index 4812e2428d..93b41ea59f 100644 --- a/.github/scripts/verify_cargo_workspace_manifests.py +++ b/.github/scripts/verify_cargo_workspace_manifests.py @@ -25,7 +25,10 @@ TOP_LEVEL_NAME_EXCEPTIONS = { UTILITY_NAME_EXCEPTIONS = { "path-utils": "codex-utils-path", } -MANIFEST_FEATURE_EXCEPTIONS = {} +MANIFEST_FEATURE_EXCEPTIONS = { + "codex-rs/code-mode/Cargo.toml": {"sandbox": ("v8/v8_enable_sandbox",)}, + "codex-rs/v8-poc/Cargo.toml": {"sandbox": ("v8/v8_enable_sandbox",)}, +} OPTIONAL_DEPENDENCY_EXCEPTIONS = set() INTERNAL_DEPENDENCY_FEATURE_EXCEPTIONS = {} diff --git a/.github/workflows/rusty-v8-release.yml b/.github/workflows/rusty-v8-release.yml index ee92eff4fa..9731948f60 100644 --- a/.github/workflows/rusty-v8-release.yml +++ b/.github/workflows/rusty-v8-release.yml @@ -111,6 +111,7 @@ jobs: -c opt "--platforms=@llvm//platforms:${PLATFORM}" + --config=v8-release-compat "${pair_target}" "${extra_targets[@]}" --build_metadata=COMMIT_SHA=$(git rev-parse HEAD) @@ -134,6 +135,7 @@ jobs: --platform "${PLATFORM}" \ --target "${TARGET}" \ --compilation-mode opt \ + --bazel-config v8-release-compat \ --output-dir "dist/${TARGET}" - name: Upload staged musl artifacts diff --git a/.github/workflows/v8-canary.yml b/.github/workflows/v8-canary.yml index 119d042275..3b59348775 100644 --- a/.github/workflows/v8-canary.yml +++ b/.github/workflows/v8-canary.yml @@ -105,6 +105,7 @@ jobs: bazel_args=( build "--platforms=@llvm//platforms:${PLATFORM}" + --config=v8-release-compat "${pair_target}" "${extra_targets[@]}" --build_metadata=COMMIT_SHA=$(git rev-parse HEAD) @@ -127,6 +128,7 @@ jobs: python3 .github/scripts/rusty_v8_bazel.py stage-release-pair \ --platform "${PLATFORM}" \ --target "${TARGET}" \ + --bazel-config v8-release-compat \ --output-dir "dist/${TARGET}" - name: Upload staged musl artifacts diff --git a/MODULE.bazel b/MODULE.bazel index be00937946..f675038791 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -327,6 +327,18 @@ crate.annotation( "RUSTY_V8_SRC_BINDING_PATH": "$(execpath @v8_targets//:rusty_v8_binding_for_target)", }, crate = "v8", + # Keep the Rust feature aligned with the source-built Bazel artifacts. + # Windows MSVC still consumes upstream non-sandboxed prebuilts. + crate_features_select = { + "aarch64-apple-darwin": ["v8_enable_sandbox"], + "aarch64-pc-windows-gnullvm": ["v8_enable_sandbox"], + "aarch64-unknown-linux-gnu": ["v8_enable_sandbox"], + "aarch64-unknown-linux-musl": ["v8_enable_sandbox"], + "x86_64-apple-darwin": ["v8_enable_sandbox"], + "x86_64-pc-windows-gnullvm": ["v8_enable_sandbox"], + "x86_64-unknown-linux-gnu": ["v8_enable_sandbox"], + "x86_64-unknown-linux-musl": ["v8_enable_sandbox"], + }, gen_build_script = "on", patch_args = ["-p1"], patches = [ diff --git a/codex-rs/code-mode/Cargo.toml b/codex-rs/code-mode/Cargo.toml index 23b2ce2306..19d6c3ab45 100644 --- a/codex-rs/code-mode/Cargo.toml +++ b/codex-rs/code-mode/Cargo.toml @@ -9,6 +9,9 @@ doctest = false name = "codex_code_mode" path = "src/lib.rs" +[features] +sandbox = ["v8/v8_enable_sandbox"] + [lints] workspace = true diff --git a/codex-rs/v8-poc/BUILD.bazel b/codex-rs/v8-poc/BUILD.bazel index 0cadbd518d..05a46451a9 100644 --- a/codex-rs/v8-poc/BUILD.bazel +++ b/codex-rs/v8-poc/BUILD.bazel @@ -3,6 +3,10 @@ load("//:defs.bzl", "codex_rust_crate") codex_rust_crate( name = "v8-poc", crate_name = "codex_v8_poc", + crate_features = select({ + "@rules_rs//rs/experimental/platforms/constraints:windows_msvc": [], + "//conditions:default": ["sandbox"], + }), deps_extra = ["@crates//:v8"], ) diff --git a/codex-rs/v8-poc/Cargo.toml b/codex-rs/v8-poc/Cargo.toml index 4bf008c095..cbcb7ca404 100644 --- a/codex-rs/v8-poc/Cargo.toml +++ b/codex-rs/v8-poc/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true name = "codex_v8_poc" path = "src/lib.rs" +[features] +sandbox = ["v8/v8_enable_sandbox"] + [lints] workspace = true diff --git a/codex-rs/v8-poc/src/lib.rs b/codex-rs/v8-poc/src/lib.rs index 0e9faaab10..f43a9a1253 100644 --- a/codex-rs/v8-poc/src/lib.rs +++ b/codex-rs/v8-poc/src/lib.rs @@ -12,6 +12,17 @@ pub fn embedded_v8_version() -> &'static str { v8::V8::get_version() } +/// Returns whether the linked V8 library was built with the in-process sandbox. +#[must_use] +pub fn linked_v8_has_sandbox() -> bool { + unsafe extern "C" { + fn v8__V8__IsSandboxEnabled() -> bool; + } + + // `rusty_v8` exposes this symbol for its own sandbox verification tests. + unsafe { v8__V8__IsSandboxEnabled() } +} + #[cfg(test)] mod tests { use pretty_assertions::assert_eq; @@ -53,6 +64,11 @@ mod tests { assert!(!super::embedded_v8_version().is_empty()); } + #[test] + fn sandbox_feature_matches_linked_v8() { + assert_eq!(super::linked_v8_has_sandbox(), cfg!(feature = "sandbox")); + } + #[test] fn evaluates_integer_addition() { assert_eq!(evaluate_expression("1 + 2"), "3"); diff --git a/defs.bzl b/defs.bzl index fc8b0e0da6..4af81ecef1 100644 --- a/defs.bzl +++ b/defs.bzl @@ -331,6 +331,7 @@ def codex_rust_crate( rust_test( name = unit_test_binary, crate = name, + crate_features = crate_features, deps = all_crate_deps(normal = True, normal_dev = True) + maybe_deps + deps_extra, # Unit tests also compile to standalone Windows executables, so # keep their stack reserve aligned with binaries and integration diff --git a/patches/v8_bazel_rules.patch b/patches/v8_bazel_rules.patch index df845939d0..70ab440d85 100644 --- a/patches/v8_bazel_rules.patch +++ b/patches/v8_bazel_rules.patch @@ -65,10 +65,19 @@ index 9648e4a..88efd41 100644 ":should_add_rdynamic": ["-rdynamic"], "//conditions:default": [], diff --git a/orig/v8-14.6.202.11/BUILD.bazel b/mod/v8-14.6.202.11/BUILD.bazel -index 85f31b7..7314584 100644 +index 85f31b7..bbc351b 100644 --- a/orig/v8-14.6.202.11/BUILD.bazel +++ b/mod/v8-14.6.202.11/BUILD.bazel -@@ -303,7 +303,7 @@ v8_int( +@@ -148,6 +148,8 @@ v8_flag(name = "v8_enable_trace_maps") + + v8_flag(name = "v8_enable_v8_checks") + ++v8_flag(name = "v8_enable_sandbox") ++ + v8_flag(name = "v8_enable_verify_csa") + + v8_flag(name = "v8_enable_verify_heap") +@@ -303,7 +305,7 @@ v8_int( # If no explicit value for v8_enable_pointer_compression, we set it to 'none'. v8_string( name = "v8_enable_pointer_compression", @@ -77,7 +86,15 @@ index 85f31b7..7314584 100644 ) # Default setting for v8_enable_pointer_compression. -@@ -4077,28 +4077,14 @@ filegroup( +@@ -503,6 +505,7 @@ v8_config( + "v8_enable_slow_dchecks": "ENABLE_SLOW_DCHECKS", + "v8_enable_runtime_call_stats": "V8_RUNTIME_CALL_STATS", + "v8_enable_snapshot_native_code_counters": "V8_SNAPSHOT_NATIVE_CODE_COUNTERS", ++ "v8_enable_sandbox": "V8_ENABLE_SANDBOX", + "v8_enable_trace_maps": "V8_TRACE_MAPS", + "v8_enable_turbofan": "V8_ENABLE_TURBOFAN", + "v8_enable_v8_checks": "V8_ENABLE_CHECKS", +@@ -4077,28 +4080,14 @@ filegroup( }), ) @@ -112,7 +129,7 @@ index 85f31b7..7314584 100644 ) filegroup( -@@ -4405,6 +4391,20 @@ genrule( +@@ -4405,6 +4394,20 @@ genrule( srcs = [ "include/js_protocol.pdl", "src/inspector/inspector_protocol_config.json", @@ -133,7 +150,7 @@ index 85f31b7..7314584 100644 ], outs = [ "include/inspector/Debugger.h", -@@ -4426,15 +4426,19 @@ genrule( +@@ -4426,15 +4429,19 @@ genrule( "src/inspector/protocol/Schema.cpp", "src/inspector/protocol/Schema.h", ], @@ -157,7 +174,7 @@ index 85f31b7..7314584 100644 ], ) -@@ -4448,6 +4451,15 @@ filegroup( +@@ -4448,6 +4455,15 @@ filegroup( ], ) @@ -173,7 +190,7 @@ index 85f31b7..7314584 100644 filegroup( name = "d8_files", srcs = [ -@@ -4567,16 +4579,9 @@ cc_library( +@@ -4567,16 +4583,9 @@ cc_library( ], ) @@ -192,7 +209,8 @@ index 85f31b7..7314584 100644 + actual = "@simdutf//:simdutf", ) -@@ -4593,7 +4598,7 @@ v8_library( + v8_library( +@@ -4593,7 +4602,7 @@ v8_library( copts = ["-Wno-implicit-fallthrough"], icu_deps = [ ":icu/generated_torque_definitions_headers", @@ -201,7 +219,7 @@ index 85f31b7..7314584 100644 ], icu_srcs = [ ":generated_regexp_special_case", -@@ -4608,7 +4613,7 @@ v8_library( +@@ -4608,7 +4617,7 @@ v8_library( ], deps = [ ":lib_dragonbox", @@ -210,7 +228,7 @@ index 85f31b7..7314584 100644 ":lib_fp16", ":simdutf", ":v8_libbase", -@@ -4664,6 +4669,7 @@ alias( +@@ -4664,6 +4673,7 @@ alias( alias( name = "core_lib_icu", actual = "icu/v8", @@ -218,7 +236,7 @@ index 85f31b7..7314584 100644 ) v8_library( -@@ -4715,7 +4721,7 @@ v8_binary( +@@ -4715,7 +4725,7 @@ v8_binary( ], deps = [ ":v8_libbase", diff --git a/third_party/v8/README.md b/third_party/v8/README.md index 6b85ba66c2..f0961df1d0 100644 --- a/third_party/v8/README.md +++ b/third_party/v8/README.md @@ -3,14 +3,21 @@ This directory wires the `v8` crate to exact-version Bazel inputs. Bazel consumer builds use: -- upstream `denoland/rusty_v8` release archives on Windows -- source-built V8 archives on Darwin, GNU Linux, and musl Linux +- upstream `denoland/rusty_v8` release archives on Windows MSVC +- source-built V8 archives on Darwin, GNU Linux, musl Linux, and Windows GNU - `openai/codex` release assets for published musl release pairs Cargo builds still use prebuilt `rusty_v8` archives by default. Only Bazel overrides `RUSTY_V8_ARCHIVE`/`RUSTY_V8_SRC_BINDING_PATH` in `MODULE.bazel` to select source-built local archives for its consumer builds. +Source-built Bazel V8 artifacts enable V8's in-process sandbox by default, and +the Bazel `v8` crate feature selection tracks those targets. A full consumer +rollout still needs matching sandbox-enabled archives for every non-source-built +target. Until that artifact migration lands, the rusty_v8 publishing workflows +use `--config=v8-release-compat` to preserve the current non-sandboxed release +artifact contract. + Current pinned versions: - Rust crate: `v8 = =146.4.0`