Skip to content

kernel: Publish code_server mode before registering#11354

Open
bruckner wants to merge 1 commit into
erlang:maintfrom
bruckner:bruckner/kernel/code-server-mode-race/GH-11353
Open

kernel: Publish code_server mode before registering#11354
bruckner wants to merge 1 commit into
erlang:maintfrom
bruckner:bruckner/kernel/code-server-mode-race/GH-11353

Conversation

@bruckner

Copy link
Copy Markdown

Fixes #11353.

error_handler routes lazy loads through code:ensure_loaded/1 (which reads the mode from persistent_term via code_server:get_mode/0) as soon as whereis(code_server) succeeds — but code_server:init/3 registered the name first and published the term last, after the full code-path setup. A process hitting an undefined function inside that window crashes with badarg; when it's the logger kernel process handling an early-boot event, logger dies and boot halts with Kernel pid terminated (logger).

The issue has a deterministic, Elixir-free reproduction (bootstrap-only boot script + unreadable cwd, flipping crash/pass 5/5 with the cwd bit alone, confirmed on OTP 28 x86_64 and OTP 27 arm64). Boot scripts that only primLoad the bootstrap module set — the shape mix release generates, so every Elixir release's eval/migration path — are the exposed population.

This change publishes the term before registering, so a caller reaching ensure_loaded in the window blocks in the gen call until loop/1 serves it — the same semantics as a call arriving just after init completes.

Verified against the reproduction in #11353 by interposing the recompiled module into the failing boot: 5/5 boots pass with the fix where 5/5 crashed without it.

error_handler routes lazy loads through code:ensure_loaded/1 (which
reads the mode from persistent_term via code_server:get_mode/0) as
soon as whereis(code_server) succeeds. code_server:init/3 registered
the name first and published the term last, after the full code-path
setup, so a process hitting an undefined function inside that window
crashed with badarg. When that process is the logger kernel process
handling an early-boot event - reliably triggered by, for example, an
unreadable cwd making erl_prim_loader emit file_error reports for the
"." path entry on every lazy load - logger dies and the whole boot
halts with "Kernel pid terminated (logger)". Boot scripts that only
primLoad the bootstrap module set (as Elixir mix releases do) are the
exposed population; the stock start_clean loads all of kernel+stdlib
up front and never lazy-loads in the window.

Publish the term before registering. Callers reaching ensure_loaded
in the window then block in the gen call until loop/1 serves them,
the same semantics as calls arriving just after init completes.

Verified against the deterministic reproduction in erlangGH-11353: 5/5
boots pass with the fix where 5/5 crashed without it (OTP 28,
x86_64; also reproduced on OTP 27 arm64).
@CLAassistant

CLAassistant commented Jul 12, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions

Copy link
Copy Markdown
Contributor

CT Test Results

Tests are running... https://github.com/erlang/otp/actions/runs/29185366275

Results for commit abb3861

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

  • No CT logs found
  • No HTML docs found
  • No Windows Installer found

// Erlang/OTP Github Action Bot

@garazdawi

Copy link
Copy Markdown
Member

Does this commit in #11326 do anything for this?

@josevalim

Copy link
Copy Markdown
Contributor

@garazdawi that commit fixes the issue too, since it fails with badarg, but I believe this PR is still correct for the reasons outlined here: #11353 (comment)

@bruckner

Copy link
Copy Markdown
Author

It does — verified against the reproduction rather than just by
reading: I applied only the error_handler hunk from that commit
(the badarg/'DOWN' catch falling back to init:ensure_loaded/1)
on OTP 27 (Debian packages, arm64) and on the OTP 28 host where this
was found. Control crashes as reported; with the catch in place the
same boot passes 5/5. The init.erl half of your commit wasn't needed
for this case since init is still serving loads during boot — it
covers the shutdown side.

So #11326 fixes this symptom at the consumer, and this PR removes the
inconsistent registered-but-unpublished window at the source. They
seem complementary to me — a caller in the window blocks briefly on
the gen call instead of taking the init detour — but happy to defer on
whether you want one, the other, or both; feel free to fold the
reorder into #11326 and close this PR if that's cleaner.

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.

4 participants