idt: report the faulting task, ESP and caller trail on a page fault - #128
Merged
Conversation
An EIP=0 kernel page fault was undiagnosable from the log. The dump printed the faulting address, error code, CR3 and the page walk -- enough to say that address 0 was entered, but not which task was running nor how control got there. Issue #126 is exactly this: four nightly-CI panics, all reporting Faulting address: 0x00000000 Error code: 0x00000011 (present | instruction fetch) EIP: 0x00000000 CS: 0x0010 with nothing to tell them apart. The fault point differed between runs -- three faulted just after the EDR daemon's config line, one after a completed scan -- so the log could not distinguish a bad indirect call from a context switch into a corrupted saved EIP. Those are very different bugs and the output supported neither. Adds three things to the kernel-fault path: Task: PID and name of current_task (or an explicit <none>, which is itself informative: it means pre-scheduler or no current task). ESP(kernel): the kernel stack pointer at fault time. Stack near: eight words from there, to be matched against nm. useresp is deliberately NOT used for the kernel case. The CPU pushes it only on a privilege change, so on a CPL=0 fault that slot holds whatever happened to follow the frame. The kernel ESP is the address of the hardware-pushed portion of the frame instead. The user case keeps useresp/ss, where they are valid. The stack read is bounded to kernel-mapped addresses and breaks out otherwise: this runs inside the fault handler, which must not itself fault -- the same constraint that already forced pae_dump_tables() over recursive mapping. Verified against a deliberately injected null call (reverted, not committed), which reproduces the CI signature exactly: Task: PID=26865 'edr_daemon' (kernel) ESP(kernel): 0x004c8f00 Stack near ESP: 00000000 00000010 00010246 001304ae 001586ec ... and 001304ae resolves via i686-elf-nm to edr_daemon_main+0x9e -- the injected call site. So the trail recovers the caller that the old dump lost. Clean boot with the change in place, 0 panics. Warning-clean under -Werror. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CEkhAhgTxbE5TgifyYf8v4
This was referenced Aug 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
An
EIP=0kernel page fault currently cannot be diagnosed from the log. The dump gives the faulting address, error code, CR3 and the page walk — enough to say address 0 was entered, but not which task was running, and not how control got there.#126 is exactly this. Four nightly-CI panics, all identical:
The fault point differed between runs — three faulted just after the EDR daemon's config line, one after a completed scan cycle — so the log cannot distinguish a bad indirect call from a context switch into a task with a corrupted saved EIP. Those are very different bugs, and the current output supports neither diagnosis. I could not reproduce the panic locally either (toolchain, QEMU version and GRUB target are all ruled out in #126), which leaves the next nightly run as the main source of evidence — so that run should produce something usable.
What
On the kernel path, three additions:
Task:current_task, or an explicit<none>— itself informative, meaning pre-scheduler or no current taskESP(kernel):Stack near ESP:nmuserespis deliberately not used for the kernel case: the CPU pushes it only on a privilege change, so on a CPL=0 fault that slot holds whatever followed the frame. The kernel ESP is derived from the hardware-pushed portion of the frame instead. The user path keepsuseresp/ss, where they are valid.The stack read is bounded to kernel-mapped addresses and breaks out otherwise — this runs inside the fault handler, which must not itself fault. That is the same constraint that already forced
pae_dump_tables()over recursive mapping.Verification
Not just "it compiles" — I injected a deliberate null call (reverted, not part of this PR) to fire the path. It reproduces the CI signature exactly, and the new lines do the job:
Resolving that trail:
So the caller the old dump lost is recoverable.
make -j8 kernel.elfwarning-clean under-Werror.Diagnostic only — no behaviour change on any non-faulting path. This does not fix #126; it makes the next occurrence interpretable.