• Matthew Bystrin's avatar
    riscv: stacktrace: fixed walk_stackframe() · a2a4d4a6
    Matthew Bystrin authored
    If the load access fault occures in a leaf function (with
    CONFIG_FRAME_POINTER=y), when wrong stack trace will be displayed:
    
    [<ffffffff804853c2>] regmap_mmio_read32le+0xe/0x1c
    ---[ end trace 0000000000000000 ]---
    
    Registers dump:
        ra     0xffffffff80485758 <regmap_mmio_read+36>
        sp     0xffffffc80200b9a0
        fp     0xffffffc80200b9b0
        pc     0xffffffff804853ba <regmap_mmio_read32le+6>
    
    Stack dump:
        0xffffffc80200b9a0:  0xffffffc80200b9e0  0xffffffc80200b9e0
        0xffffffc80200b9b0:  0xffffffff8116d7e8  0x0000000000000100
        0xffffffc80200b9c0:  0xffffffd8055b9400  0xffffffd8055b9400
        0xffffffc80200b9d0:  0xffffffc80200b9f0  0xffffffff8047c526
        0xffffffc80200b9e0:  0xffffffc80200ba30  0xffffffff8047fe9a
    
    The assembler dump of the function preambula:
        add     sp,sp,-16
        sd      s0,8(sp)
        add     s0,sp,16
    
    In the fist stack frame, where ra is not stored on the stack we can
    observe:
    
            0(sp)                  8(sp)
            .---------------------------------------------.
        sp->|       frame->fp      | frame->ra (saved fp) |
            |---------------------------------------------|
        fp->|         ....         |         ....         |
            |---------------------------------------------|
            |                      |                      |
    
    and in the code check is performed:
    	if (regs && (regs->epc == pc) && (frame->fp & 0x7))
    
    I see no reason to check frame->fp value at all, because it is can be
    uninitialized value on the stack. A better way is to check frame->ra to
    be an address on the stack. After the stacktrace shows as expect:
    
    [<ffffffff804853c2>] regmap_mmio_read32le+0xe/0x1c
    [<ffffffff80485758>] regmap_mmio_read+0x24/0x52
    [<ffffffff8047c526>] _regmap_bus_reg_read+0x1a/0x22
    [<ffffffff8047fe9a>] _regmap_read+0x5c/0xea
    [<ffffffff80480376>] _regmap_update_bits+0x76/0xc0
    ...
    ---[ end trace 0000000000000000 ]---
    As pointed by Samuel Holland it is incorrect to remove check of the stackframe
    entirely.
    
    Changes since v2 [2]:
     - Add accidentally forgotten curly brace
    
    Changes since v1 [1]:
     - Instead of just dropping frame->fp check, replace it with validation of
       frame->ra, which should be a stack address.
     - Move frame pointer validation into the separate function.
    
    [1] https://lore.kernel.org/linux-riscv/20240426072701.6463-1-dev.mbstr@gmail.com/
    [2] https://lore.kernel.org/linux-riscv/20240521131314.48895-1-dev.mbstr@gmail.com/
    
    Fixes: f766f77a ("riscv/stacktrace: Fix stack output without ra on the stack top")
    Signed-off-by: default avatarMatthew Bystrin <dev.mbstr@gmail.com>
    Reviewed-by: default avatarSamuel Holland <samuel.holland@sifive.com>
    Link: https://lore.kernel.org/r/20240521191727.62012-1-dev.mbstr@gmail.comSigned-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
    a2a4d4a6
stacktrace.c 3.69 KB