Skip to content

arm: the kernel backtrace follows a zero stack word to NULL and faults - #643

Open
cyclistmass wants to merge 1 commit into
Clozure:masterfrom
cyclistmass:albt-zero-stack-word
Open

cyclistmass wants to merge 1 commit into
Clozure:masterfrom
cyclistmass:albt-zero-stack-word

Conversation

@cyclistmass

Copy link
Copy Markdown
Contributor

walk_stack_frames follows a non-frame stack word as a pointer in two arms:

} else if ((header & fixnummask) == 0) {
  next = (lisp_frame *)header;
} else if (header == stack_alloc_marker) {
  next = (lisp_frame *)(current[1]);

Zero satisfies (header & fixnummask) == 0, and a control stack holds many zero
words. next becomes NULL, start = next still passes while (start < end),
and lisp_frame_p(NULL) dereferences address 0.

So the printer faults while it prints the backtrace for another fault, and it
loses the report that would identify the original defect. Any small fixnum does
the same at a different address, and a candidate below start walks backwards.

The right test is not "does this word carry a fixnum tag" but "is this a
forward address inside the region under walk". advance_or_stop accepts a candidate
only when start < candidate <= end. Anything else ends the walk with a
diagnostic, because a truncated backtrace beats a SIGSEGV in the debugger.

A core dump shows it. start enters as 0xffff829fc1f0, and reaches the call
as 0x0:

#7  lisp_frame_p (spPtr=spPtr@entry=0x0) at ../arm64-exceptions.c
#8  walk_stack_frames (start=0x0, start@entry=0xffff829fc1f0, end=0xffff82a00000)

The "Bad frame!" line also printed a pointer with %x, which truncates to 32
bits on a 64-bit target. It now uses %p.

albt.c builds for androidarm, darwinarm, darwinarm64, linuxarm and linuxarm64,
and links into the shipped kernel, so the defect is live for ARM32 as well.

I verified this on linuxarm64, using the command make -n albt.o prints rather
than one I guessed. Both files compile clean, albt.o grows from 25896 to 29176
bytes, and the patch adds no warning. I watched two positive controls fail.

I have not linked and booted a kernel with this change, and I did not reproduce
the fault on demand.

walk_stack_frames follows a non-frame stack word as a pointer in two of its
arms.  Zero satisfies `(header & fixnummask) == 0', and a control stack is
full of zero words, so next became NULL and lisp_frame_p(NULL) dereferenced
address 0.  The backtrace printer died inside the crash it was reporting.

advance_or_stop() accepts a candidate only when start < candidate <= end, and
otherwise ends the walk with a diagnostic naming the value it rejected.  Also
here: the "Bad frame!" line printed a pointer with %x, which truncates to 32
bits on a 64-bit target; it is now %p.
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.

1 participant