From abb3861bd1f66a628c7332dc58c7139adb8e5e0a Mon Sep 17 00:00:00 2001 From: Daniel Bruckner Date: Sun, 12 Jul 2026 01:08:22 -0700 Subject: [PATCH] kernel: Publish code_server mode before registering 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 GH-11353: 5/5 boots pass with the fix where 5/5 crashed without it (OTP 28, x86_64; also reproduced on OTP 27 arm64). --- lib/kernel/src/code_server.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 5d45f6f8fb51..3011d1e05f57 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -89,6 +89,16 @@ get_mode() -> %% ----------------------------------------------------------- init(Ref, Parent, [Root,Mode]) -> + %% The mode must be readable as soon as the name resolves: + %% error_handler routes lazy loads through code:ensure_loaded/1 + %% (which reads this term via get_mode/0) once + %% whereis(code_server) succeeds, and processes already running + %% during kernel start -- notably the logger kernel process -- + %% can hit an undefined function while init/3 is still building + %% the code path. Publishing the term only after register/2 left + %% a window where such a load raised badarg, killing logger and + %% the whole boot (GH-11353). + persistent_term:put(?MODULE, Mode), register(?MODULE, self()), process_flag(trap_exit, true), @@ -120,7 +130,6 @@ init(Ref, Parent, [Root,Mode]) -> moddb = Db, namedb = create_namedb(Path, Root)}, - persistent_term:put(?MODULE, Mode), Parent ! {Ref,{ok,self()}}, loop(State).