NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: port-alpha/60865 (NetBSD-current/alpha panics running getcwd test)



I could reproduce this problem more easily:

```
# ls /proc

[  12.6627837] CPU 0: fatal kernel trap:

[  12.6747358] CPU 0    trap entry = 0x2 (memory management fault)
[  12.6924180] CPU 0    a0         = 0x0
[  12.7033379] CPU 0    a1         = 0x1
[  12.7142582] CPU 0    a2         = 0xffffffffffffffff
[  12.7290796] CPU 0    pc         = 0x0
[  12.7400000] CPU 0    ra         = 0x0
[  12.7509206] CPU 0    pv         = 0xfffffc0000a67ea8
[  12.7657422] CPU 0    curlwp     = 0xfffffc007f636a40
[  12.7805637] CPU 0        pid = 126, comm = ls

[  12.7940843] panic: trap
[  12.8013654] cpu0: Begin traceback...
[  12.8120241] at 0xfffffc00007f79a0 pc 0xfffffc000119eb4c
[  12.8297761] vpanic() at netbsd:vpanic+0x14c
[  12.8444251] printf() at netbsd:printf_nostamp+0xf0
[  12.8594534] --- root of call graph ---
[  12.8706338] cpu0: End traceback...
Stopped in pid 126.126 (ls) at netbsd:cpu_Debugger+0x4: ret zero,(ra)
```

TL;DR This is because kernel stack is only aligned to 8-byte
boundaries. If it is properly aligned to 16-byte boundaries,
panic disappears. We fixed user-space stack alignment few years
ago, but we overlooked kernel stack at that time.

Carrying out bisectioning, I've found that this is due to
kern_proc.c::proclist_foreach_call().

(1) If this function is compiled with
__attribute__((optimize("trivial-auto-var-init=uninitialized"))),
panic does not occur.

(2) With
__attribute__((optimize("trivial-auto-var-init=zero"))),
panic occurs.

(3) With
__attribute__((optimize("trivial-auto-var-init=uninitialized"))),
but memset(&marker, 0, sizeof(marker)) in that function,
panic occurs.

So, I guess that marker does not fit within stack frame, and
initialization corrupts caller's stack frame for
-ftrivial-auto-var-init=pattern (default), (2), and (3),

Actually, objdump for (3) reads:

```
0000000000003c60 <proclist_foreach_call>:
{
    3c60:       00 00 bb 27     ldah    gp,0(t12)
    3c64:       00 00 bd 23     lda     gp,0(gp)
    3c68:       50 fc de 23     lda     sp,-944(sp)
    3c6c:       00 00 5e b7     stq     ra,0(sp)
    3c70:       08 00 3e b5     stq     s0,8(sp)
    3c74:       10 00 5e b5     stq     s1,16(sp)
    3c78:       18 00 7e b5     stq     s2,24(sp)
    3c7c:       20 00 9e b5     stq     s3,32(sp)
    3c80:       28 00 be b5     stq     s4,40(sp)
    3c84:       30 00 de b5     stq     s5,48(sp)
    3c88:       38 00 fe b5     stq     fp,56(sp)
    3c8c:       7f 00 5e 20     lda     t1,127(sp)
        memset(&marker, 0, sizeof(marker));
    3c90:       00 00 7d a7     ldq     t12,0(gp)
{
    3c94:       0d f1 47 44     andnot  t1,0x3f,s4
    3c98:       09 04 f0 47     mov     a0,s0
    3c9c:       0b 04 f1 47     mov     a1,s2
        memset(&marker, 0, sizeof(marker));
    3ca0:       10 04 ed 47     mov     s4,a0
    3ca4:       11 04 ff 47     clr     a1
{
    3ca8:       0c 04 f2 47     mov     a2,s3
        memset(&marker, 0, sizeof(marker));
    3cac:       40 03 5f 22     lda     a2,832
3cb0: 00 40 5b 6b jsr ra,(t12),3cb4 <proclist_foreach_call+0x54>
```

So, this code does:

sp <-- sp - 944
&marker <-- roundup(sp + 64, 64)
memset(&marker, 0, 832)

If sp is aligned to 16-byte boundaries, as ABI (there's no ELF
ABI for alpha, so ABI means GCC here) requires, marker always
fits within stack frame.

However, if sp is aligned only aligned to 8-byte boundaries,
marker generally does not fit, and memset() corrupts caller's
stack frame.

Actually, inserting this code into proclist_foreach_call()
causes KASSERT failure:

```
	uint64_t sp;

	__asm__ volatile ("mov $30, %0" : "=r" (sp) ::);
	KASSERTMSG((sp & 0xf) == 0, "sp 0x%016" PRIx64, sp);
```

With attached patch below, panic no longer occurs for me.
But, I'm not sure that this fix is enough for proper kernel
stack alignments.

Thanks,
rin

```
commit 23014dd67d0
Author: Rin Okuyama <rokuyama.rk%gmail.com@localhost>
Date:   Tue Oct 20 20:32:59 2026 +0900

    alpha: Align kernel stack pointer to 16-byte bourndaries

diff --git a/sys/arch/alpha/alpha/machdep.c b/sys/arch/alpha/alpha/machdep.c
index 8a4f98beb1d..79f2bd0c444 100644
--- a/sys/arch/alpha/alpha/machdep.c
+++ b/sys/arch/alpha/alpha/machdep.c
@@ -725,7 +725,8 @@ nobootinfo:
 	 * Set the kernel sp, reserving space for an (empty) trapframe,
 	 * and make lwp0's trapframe pointer point to it for sanity.
 	 */
-	pcb0->pcb_hw.apcb_ksp = v + USPACE - sizeof(struct trapframe);
+	pcb0->pcb_hw.apcb_ksp = (v + USPACE - sizeof(struct trapframe)) &
+	    ~STACK_ALIGNBYTES;
 	lwp0.l_md.md_tf = (struct trapframe *)pcb0->pcb_hw.apcb_ksp;

 	/* Indicate that lwp0 has a CPU. */
diff --git a/sys/arch/alpha/alpha/vm_machdep.c b/sys/arch/alpha/alpha/vm_machdep.c
index 3cbaaf6a380..b6dd44c04c3 100644
--- a/sys/arch/alpha/alpha/vm_machdep.c
+++ b/sys/arch/alpha/alpha/vm_machdep.c
@@ -152,8 +152,9 @@ cpu_lwp_fork(struct lwp *l1, struct lwp *l2, void *stack, size_t stacksize,
 		 * copy trapframe from parent so return to user mode
 		 * will be to right address, with correct registers.
 		 */
-		l2tf = l2->l_md.md_tf = (struct trapframe *)
-		    (uvm_lwp_getuarea(l2) + USPACE - sizeof(struct trapframe));
+		l2tf = l2->l_md.md_tf = (struct trapframe *)((uintptr_t)
+		    (uvm_lwp_getuarea(l2) + USPACE - sizeof(struct trapframe)) &
+			~STACK_ALIGNBYTES);
 		memcpy(l2->l_md.md_tf, l1->l_md.md_tf,
 		    sizeof(struct trapframe));

```



Home | Main Index | Thread Index | Old Index