Skip to content

process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate - #35882

Open
robobun wants to merge 16 commits into
mainfrom
claude/farm/729db18d/process-env-exotic-object
Open

process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate#35882
robobun wants to merge 16 commits into
mainfrom
claude/farm/729db18d/process-env-exotic-object

Conversation

@robobun

@robobun robobun commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

What

Replace the POSIX process.env plain object with an exotic JSProcessEnvMap backed by the env_loader map, matching Node's RealEnvStore contract. On Windows the existing Proxy gains the same validation.

Why

createEnvironmentVariablesMap builds a plain objectPrototype object; each startup key gets CustomGetterSetter::create(vm, jsGetterEnvironmentVariable, nullptr) under CustomValue, so the first write clobbers the accessor into a raw data property and jsSetterEnvironmentVariable has zero live references (dead since #20527). Brand-new keys are plain data properties because the container has no OverridesPut, so Node's contract was never applied to any key.

Node v26 contract (verified locally):

process.env.X = 3000            →  "3000"   (ToString; undefined → "undefined")
process.env.X = Symbol()        →  TypeError
process.env[Symbol()] = "v"     →  TypeError
process.env["A=B"] = "v"        →  silently ignored ('A=B' in process.env === false)
process.env[""] = "v"           →  silently ignored
process.env.X = "ab\0cd"        →  "ab"   (NUL-truncated; key same)
Object.freeze(process.env)      →  TypeError
Object.defineProperty(process.env, k, {get})     →  ERR_INVALID_OBJECT_DEFINE_PROPERTY
Object.defineProperty(process.env, k, {value, writable:true, enumerable:true, configurable:true})  →  coerces + sets
process.env.X = v               →  calls uv_os_setenv; delete calls uv_os_unsetenv

Duplicate KEY= entries in environ resolve to the first occurrence (matches libc getenv() and Node); an entry without = is dropped (Node: 'FOOBAR' in process.env === false).

How

JSEnvironmentVariableMap.cpp: new JSProcessEnvMap : JSNonFinalObject (POSIX only) with OverridesPut | OverridesGetOwnPropertySlot | OverridesGetOwnPropertyNames | ProhibitsPropertyCaching:

  • put: throw on Symbol key/value; NUL-truncate key and value; drop =/empty keys; applySharedEnvSideEffects (TZ/TLS/verbose/proxy); Bun__ProcessEnv__put.
  • deleteProperty: Bun__ProcessEnv__delete.
  • defineOwnProperty: only fully-permissive data descriptors (else ERR_INVALID_OBJECT_DEFINE_PROPERTY), routed through put.
  • preventExtensions: return false → freeze/seal/preventExtensions throw.
  • getOwnPropertySlot / getOwnPropertyNames: read the env_loader map.

The Windows #else keeps the existing backing object + windowsEnv Proxy; inner #if OS(WINDOWS) blocks inside that branch collapsed.

BunObject.rs: Bun__ProcessEnv__put / Bun__ProcessEnv__delete: update env_mut().map, and on the main thread also libc::setenv/unsetenv + env_var::invalidate_for_setenv(key).

env_var.rs: add reset() to each cache kind and expose it on the macro-generated module; invalidate_for_setenv(key) matches HOME/PATH/USER/TMPDIR/TEMP/TMP/SHELL/XDG_* and resets. Without this os.homedir() keeps returning the first-read value after process.env.HOME = ....

env_loader.rs load_process: first-wins for duplicate keys (while still letting pre-seeded entries like bun test's NODE_ENV be overwritten by environ); drop no-= entries.

web_worker.rs: set the worker transpiler's env.behavior = LoadAllWithoutInlining (as run/test/repl already do). With main-thread setenv sync a main-thread write now reaches the worker's environ, which the worker's transpiler was then inlining into source as a string literal.

ProcessObjectInternals.ts (Windows Proxy): set and defineProperty throw on Symbol, drop =/empty keys, defineProperty rejects accessor/partial descriptors and routes through the coercing setter; preventExtensions/isExtensible traps added.

Verification

test/js/node/process/process-env-exotic.test.ts (17 cells: coercion, Symbol key/value, =/empty/NUL, freeze/defineProperty refusal, delete, spawn-poison, FFI getenv sync for set/delete, os.homedir() cache invalidation, execve'd duplicate-key / no-= launchers). 0/17 pass on the released build; 17/17 pass with this change (10/10 on Windows with 7 POSIX-only cells skipped).

Existing process.test.js, worker_threads.test.ts, env.test.ts, garbage-env.test.ts, spawn-env.test.ts remain green; two test expectations updated where the previously-dead 'TZ' in process.env branch is now reachable and where accessor defineProperty is now rejected.

Related

Subsumes #34727, #34728, #35264, #35877, #35879 and the web_worker.rs inlining fix from #35270. Differs from #35270 in that reads go through the env_loader map rather than live getenv().


no test proof · iteration 2 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/node/process/process-env-exotic.test.ts test/js/node/process/process.test.js

Known exclusions

  • After founding a SHARE_ENV tree (new Worker(url, {env: SHARE_ENV})), main'''s process.env is swapped to JSSharedEnvMap, which does not yet apply the Symbol-throw / =-empty-key drop / accessor-defineProperty reject / preventExtensions contract (the OS side is safe: syncOSEnvBun__ProcessEnv__put filters and truncates). Node keeps main on RealEnvStore after founding a tree.
  • delete process.env.TZ (and NODE_TLS_REJECT_UNAUTHORIZED / BUN_CONFIG_VERBOSE_FETCH) does not yet clear the corresponding native side-effect cache; put applies it via applySharedEnvSideEffects but deleteProperty has no delete-side variant. Not a regression (pre-PR delete had no side effect either).
  • cron.rs:2239 reads c_environ() without ENVIRON_LOCK on the POSIX worker path; switching it to create_null_delimited_env_map() (matching the Windows arm and Bun.spawn) is the follow-up.
  • getenv_z now leaks a small LSAN-ignored Box per call; splitting into a private leaking variant for the env_var cache plus a public Option<Box<[u8]>> for the ~20 other callers would narrow the LSAN suppression scope.
  • A worker created with new Worker(url, {env: {...}}) still gets a plain constructEmptyObject as its process.env (set directly in ZigGlobalObject.cpp initializeWorker, bypassing createEnvironmentVariablesMap), so the exotic contract does not apply there. Not a regression; needs a separate in-memory backing store to match Node's MapKVStore.

Fixes #34210
Fixes #29244

robobun added 2 commits July 26, 2026 05:27
The existing createEnvironmentVariablesMap builds a plain JSObject and
installs a CustomGetterSetter per startup key with a null setter
(CustomValue semantics since #20527), so the first write clobbers the
accessor into a raw data property and jsSetterEnvironmentVariable is dead
code. Brand-new keys were always plain data properties (the container has
no OverridesPut), so Node's process.env contract (ToString coercion,
Symbol key/value TypeError, '='/empty-key drop, NUL truncation, accessor/
partial defineProperty rejection, setenv write-through) was never
enforced.

Replace the POSIX process.env with a JSNonFinalObject subclass
(JSProcessEnvMap) backed by the env_loader map:

  * put(): ToString-coerce, throw on Symbol key/value, silently drop
    '='/empty keys, NUL-truncate key and value, then update the
    env_loader map and (main thread only) setenv() + invalidate the
    typed env_var cache so os.homedir()/tmpdir() pick up the change.
  * deleteProperty(): remove from the map and (main thread only)
    unsetenv() + invalidate the typed cache.
  * defineOwnProperty(): accept only a fully-permissive data descriptor
    (Node's ERR_INVALID_OBJECT_DEFINE_PROPERTY otherwise) and route it
    through put().
  * preventExtensions(): return false so Object.freeze/seal/
    preventExtensions throw.
  * getOwnPropertySlot/getOwnPropertyNames: read the env_loader map.

On Windows the existing Proxy stays; its set/defineProperty traps now
apply the same '='/empty-key drop and defineProperty validation, and a
preventExtensions trap is added so freeze/seal throw there too.

load_process: duplicate environ keys now resolve to the FIRST occurrence
(matching libc getenv and Node) and entries without '=' are dropped
instead of fabricated as KEY="".

env_var: add a per-var reset() and invalidate_for_setenv(key) so a
process.env write to HOME/PATH/USER/TMPDIR/etc. unstales the typed
accessor before the next os.* read.

web_worker: set the worker transpiler's env behavior to
LoadAllWithoutInlining (as run/test/repl already do). With main-thread
setenv sync, a main-thread process.env write now reaches the worker's
environ and was being inlined into worker source as a string literal.

Tests: test/js/node/process/process-env-exotic.test.ts covers the 11-cell
contract (coercion, Symbol validation, '='/empty/NUL handling, freeze/
defineProperty refusal, setenv/unsetenv via FFI getenv, os.homedir cache,
first-wins dup, no-'=' drop). worker_threads/process tests updated for
the defineProperty-accessor rejection and the previously dead 'TZ in
process.env' branch.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 50151e9c-f508-4685-92a4-99f292bb9d4e

📥 Commits

Reviewing files that changed from the base of the PR and between 44f6469 and 6130d1f.

📒 Files selected for processing (13)
  • src/bun_core/env_var.rs
  • src/bun_core/lib.rs
  • src/bun_core/util.rs
  • src/collections/array_hash_map.rs
  • src/dotenv/env_loader.rs
  • src/js/builtins/ProcessObjectInternals.ts
  • src/jsc/bindings/JSEnvironmentVariableMap.cpp
  • src/jsc/web_worker.rs
  • src/runtime/api/BunObject.rs
  • test/js/node/process/process-env-exotic.test.ts
  • test/js/node/process/process.test.js
  • test/js/node/worker_threads/worker_threads.test.ts
  • test/preload.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 9:14 AM PT - Jul 26th, 2026

@robobun, your commit 6130d1f has 1 failures in Build #82395 (All Failures):

  • 📦 Binary size — 12 over 0.50 MB
  • targetthis build canary: main #79916
    sizeΔ
    bun-darwin-aarch6458.13 MB57.58 MB+565.0 KB
    bun-darwin-x6463.48 MB62.95 MB+544.6 KB
    bun-linux-aarch6470.98 MB70.42 MB+576.0 KB
    bun-linux-x6472.47 MB71.95 MB+528.0 KB
    bun-linux-aarch64-musl64.88 MB64.32 MB+576.0 KB
    bun-linux-x64-musl66.98 MB66.45 MB+544.0 KB
    bun-linux-aarch64-android78.47 MB77.97 MB+512.0 KB
    bun-linux-x64-android80.62 MB80.10 MB+529.2 KB
    bun-freebsd-x6483.07 MB82.56 MB+528.0 KB
    bun-freebsd-aarch6484.84 MB84.31 MB+544.0 KB
    bun-windows-x6480.26 MB79.70 MB+577.0 KB
    bun-windows-aarch6470.87 MB70.34 MB+542.0 KB

    Add [skip size check] to the commit message if this increase is intentional.


🧪   To try this PR locally:

bunx bun-pr 35882

That installs a local version of the PR into your bun-35882 executable, so you can run:

bun-35882 --bun

@github-actions

Copy link
Copy Markdown
Contributor

Found 2 issues this PR may fix:

  1. Runtime transpiler cache inlines process.env dot-reads during Worker-thread imports — later processes execute the first process's env values #34210 - PR changes Workers to use LoadAllWithoutInlining, stopping the transpiler from baking process.env dot-reads as string literals into the disk cache
  2. os.homedir() uses process-start HOME snapshot instead of current process.env.HOME #29244 - PR adds typed cache invalidation that invalidates the os.homedir() cache when process.env.HOME is mutated

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #34210
Fixes #29244

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. process: reject accessor/partial descriptors in Object.defineProperty(process.env, ...) #34727 - Rejects accessor/partial descriptors in Object.defineProperty(process.env, ...), which process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate #35882 subsumes in its exotic object defineOwnProperty implementation
  2. process.env: coerce assigned values to strings across all construction paths #34728 - Coerces assigned process.env values to strings across all construction paths, which process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate #35882 subsumes in its put implementation
  3. process.env: drop '='/empty keys, truncate NUL in key/value like Node #35264 - Drops =/empty keys and truncates NUL in key/value like Node, which process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate #35882 subsumes in its validation logic
  4. process: back main-thread process.env by live libc environ on POSIX #35270 - Backs main-thread process.env by live libc environ on POSIX, which is the core architectural change process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate #35882 builds upon
  5. dotenv: skip environ entries with no '=' instead of fabricating an empty value #35877 - Skips environ entries with no = instead of fabricating an empty value, which process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate #35882 subsumes in env_loader.rs
  6. env: resolve duplicate environ keys to the first occurrence (match libc/Node) #35879 - Resolves duplicate environ keys to first occurrence, which process.env: Node-semantics exotic object on POSIX; coerce/validate/setenv-sync, first-wins dup load, typed-cache invalidate #35882 subsumes in env_loader.rs

🤖 Generated with Claude Code

Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/dotenv/env_loader.rs
Comment thread src/dotenv/env_loader.rs
Comment thread src/js/builtins/ProcessObjectInternals.ts
Comment thread src/js/builtins/ProcessObjectInternals.ts
Comment thread src/js/builtins/ProcessObjectInternals.ts
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/web_worker.rs
Comment thread src/runtime/api/BunObject.rs Outdated
Comment thread src/runtime/api/BunObject.rs
Comment thread src/runtime/api/BunObject.rs Outdated
Comment thread test/js/node/process/process-env-exotic.test.ts
Comment thread src/js/builtins/ProcessObjectInternals.ts
Comment thread test/js/node/process/process-env-exotic.test.ts Outdated
Comment thread test/js/node/process/process-env-exotic.test.ts Outdated
Comment thread src/runtime/api/BunObject.rs Outdated
…eanup, Symbol value in Windows defineProperty trap
…alue)

musl setenv()/__putenv frees the previous setenv-allocated string via
__env_rm_add; glibc leaks it. A &'static [u8] cached from getenv_z after
a runtime setenv could dangle on musl after a second overwrite. Instead
of reset()+re-read-getenv, invalidate_for_setenv now takes the new value
and each var's set_owned() leaks a Box<[u8]> copy so the cache never
holds a post-startup environ pointer. SAFETY comments in get_cached and
getenv_z corrected.

[skip size check]
Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/bun_core/env_var.rs
@robobun

robobun commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator Author

Status at 5143c3e: 21/21 new test cells pass locally (3/21 on the released build, all of which exercise plain-object semantics that already worked; the file as a whole fails without the src/ changes). process.test.js, worker_threads.test.ts, env.test.ts, and the .only()-fixture regression tests are green locally and on Windows. rust:check-all passes on all 10 targets.

Review threads from the automated reviewers are all addressed (seqlock on the split-word env_var::string::Cache with CAS-exclusive writers; ENVIRON_LOCK RwLock serialising getenv_z against the setenv path; proxy_env_storage.lock() around env_loader map mutation; syncOSEnv keeping setenv sync after a main-rooted SHARE_ENV swap; $-prefix read leak; ordered_remove on delete; Bun__ProcessEnv__put self-truncating NUL + filtering so no caller can trip a CString panic; musl-safe owned-copy cache via set_owned so a post-setenv environ pointer is never cached).

CI builds 82232/82244 failed only on test/js/bun/s3/s3.test.ts (Cloudflare R2 ServiceUnavailable, unrelated to this diff; reported for main-break triage) and the stale binary-size baseline (#79916 is 12 commits behind base; commits carry [skip size check]). Waiting on CI for 5143c3e.

A second set_owned overwrites ptr_value, making the previous leaked Box
unreachable to LSAN (which then SIGABRTs the test process on the asan
lane). The leak is intentional: values are short paths and
process.env.HOME/TMPDIR/... writes are rare. Add __lsan_ignore_object to
bun_core::asan and call it on the leaked allocation.

[skip size check]
Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/bun_core/lib.rs
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
…yncOSEnv doc

Clearing the slot under the lock already held means a worker spawned
after delete process.env.HTTP_PROXY no longer re-inserts the stale value
via sync_into.

syncOSEnv doc updated: on POSIX a worker write to a main-rooted tree
only reaches the shared store (Bun__ProcessEnv__put still gates setenv
on is_main_thread()); Windows is unchanged.

[skip size check]
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/runtime/api/BunObject.rs
Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/js/builtins/ProcessObjectInternals.ts
Comment thread src/bun_core/env_var.rs Outdated
…et(); Windows deleteProperty Symbol handling

string::Cache::deser_and_invalidate now takes &[u8] and leaks a Box<[u8]>
copy before storing, so both the initial get_force_reload() path and the
process.env write path cache Bun-owned bytes. An off-thread first read
racing a main-thread setenv could previously cache the post-setenv
musl-allocated string, which a later setenv frees. set_owned() is now a
thin deser_and_invalidate wrapper. reset() (all four) removed as dead.
Stale seqlock-struct and SAFETY comments updated.

Windows Proxy deleteProperty: early-return true on a Symbol key instead
of String(sym).toUpperCase() -> spurious SetEnvironmentVariableW + strict-
mode TypeError; matches Node and the new POSIX deleteProperty.

[skip size check]
Comment thread src/bun_core/env_var.rs
Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/bun_core/env_var.rs Outdated
Comment thread src/dotenv/env_loader.rs
Comment thread src/bun_core/util.rs Outdated
Comment thread src/bun_core/env_var.rs Outdated
…e-scan; document previous-Box leak

getenv_z/getenv_z_any_case now leak a Box<[u8]> copy of the value while
holding the read guard, so the returned &'static [u8] is valid regardless
of a later setenv. Previously the guard dropped on return and the caller's
Box::from memcpy ran unlocked; on musl a second main-thread setenv could
free the source between the two. deser_and_invalidate reverts to storing
the passed-in &'static directly; set_owned leaks before calling it and
documents why the previous cached slice is never freed.

web_worker.rs: set did_load_process=true on the cloned loader. The cloned
map already has the parent's environ snapshot; re-walking __environ on the
worker OS thread (via bun_sys::environ, which does not take ENVIRON_LOCK)
raced a main-thread process.env write's setenv.

[skip size check]
Comment thread src/bun_core/env_var.rs
Comment thread src/bun_core/env_var.rs
Comment thread src/bun_core/util.rs
Comment thread src/bun_core/util.rs
Comment thread src/jsc/web_worker.rs
@robobun

robobun commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator Author

Status at b776d54: all automated-review memory-safety findings addressed. Summary of the hardening beyond the original design:

  • bun_core::ENVIRON_LOCK (RwLock) serialises Bun's own getenv_z/getenv_z_any_case against the process.env write path's libc::setenv/unsetenv; getenv_z now copies into a leaked Box under the read guard so the returned &'static [u8] is Bun-owned regardless of a later setenv() (musl frees the previous setenv-allocated string on overwrite).
  • env_var::string::Cache: (ptr, len) guarded by a seqlock with CAS-exclusive writers; only Bun-owned leaked slices are ever stored. Previous cached slices deliberately never freed (a concurrent get() may hold them past the seqlock); bounded to ~10 well-known keys × rare writes + one leak per typed-var first read.
  • Bun__ProcessEnv__put/delete: self-truncate NUL, filter empty/=-keys, hold vm.proxy_env_storage.lock() around the env_loader map mutation (serialises against a spawning worker's clone_with_allocator), clear the matching proxy-var slot on delete, hold environ_write_lock() around setenv/unsetenv + invalidate_for_setenv.
  • syncOSEnv: main-rooted SHARE_ENV store writes reach setenv on the POSIX main thread (worker writes land only in the shared store, documented).
  • web_worker.rs: worker's cloned loader marked did_load_process = true so it never re-walks __environ (which is unlocked and would race main's setenv); worker transpiler set to LoadAllWithoutInlining.
  • Bun__getEnvValueBunString: reads env_loader().map directly (Loader::get's $-strip leaked into process.env['$PATH']); ordered_remove on delete preserves key order.
  • Windows Proxy: set/defineProperty/deleteProperty Symbol handling, =/empty-key drop, accessor/partial-descriptor rejection, envMapList predicate match, preventExtensions trap.
  • test/preload.ts: skip CI when copying bunEnv into process.env (now reaches setenv, so it flipped is_ci() in .only() fixtures).

21/21 new test cells pass locally (3/21 on the released build; file as a whole fails without src/). process.test.js/worker_threads.test.ts/env.test.ts/os.test.js and the .only()-fixture regression tests green locally and on Windows; rust:check-all 10/10. The only red CI lane on recent builds has been test/js/bun/s3/s3.test.ts (Cloudflare R2 ServiceUnavailable, reported for main-break triage) and the stale binary-size baseline (#79916 is 12 commits behind base; commits carry [skip size check]).

comment-cop continues to flag every multi-line doc/SAFETY comment; those threads are resolved without change since the comments document lock/seqlock/ownership invariants.

Comment thread src/jsc/web_worker.rs
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/bun_core/env_var.rs
Comment thread src/bun_core/util.rs
…tform_get

Closes the lost-update window where an off-thread first read completes
getenv_z (with the pre-write value), main runs set_owned with the new
value, then the off-thread deser_and_invalidate overwrites it with the
stale copy. Correctness-only after b776d54 (both slices are Bun-owned).

[skip size check]
Comment thread src/bun_core/env_var.rs
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Comment thread src/jsc/bindings/JSEnvironmentVariableMap.cpp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants