• Tiezhu Yang's avatar
    objtool: Handle frame pointer related instructions · da5b2ad1
    Tiezhu Yang authored
    After commit a0f7085f ("LoongArch: Add RANDOMIZE_KSTACK_OFFSET
    support"), there are three new instructions "addi.d $fp, $sp, 32",
    "sub.d $sp, $sp, $t0" and "addi.d $sp, $fp, -32" for the secondary
    stack in do_syscall(), then there is a objtool warning "return with
    modified stack frame" and no handle_syscall() which is the previous
    frame of do_syscall() in the call trace when executing the command
    "echo l > /proc/sysrq-trigger".
    
    objdump shows something like this:
    
    0000000000000000 <do_syscall>:
       0:   02ff8063        addi.d          $sp, $sp, -32
       4:   29c04076        st.d            $fp, $sp, 16
       8:   29c02077        st.d            $s0, $sp, 8
       c:   29c06061        st.d            $ra, $sp, 24
      10:   02c08076        addi.d          $fp, $sp, 32
      ...
      74:   0011b063        sub.d           $sp, $sp, $t0
      ...
      a8:   4c000181        jirl            $ra, $t0, 0
      ...
      dc:   02ff82c3        addi.d          $sp, $fp, -32
      e0:   28c06061        ld.d            $ra, $sp, 24
      e4:   28c04076        ld.d            $fp, $sp, 16
      e8:   28c02077        ld.d            $s0, $sp, 8
      ec:   02c08063        addi.d          $sp, $sp, 32
      f0:   4c000020        jirl            $zero, $ra, 0
    
    The instruction "sub.d $sp, $sp, $t0" changes the stack bottom and the
    new stack size is a random value, in order to find the return address of
    do_syscall() which is stored in the original stack frame after executing
    "jirl $ra, $t0, 0", it should use fp which points to the original stack
    top.
    
    At the beginning, the thought is tended to decode the secondary stack
    instruction "sub.d $sp, $sp, $t0" and set it as a label, then check this
    label for the two frame pointer instructions to change the cfa base and
    cfa offset during the period of secondary stack in update_cfi_state().
    This is valid for GCC but invalid for Clang due to there are different
    secondary stack instructions for ClangBuiltLinux on LoongArch, something
    like this:
    
    0000000000000000 <do_syscall>:
      ...
      88:   00119064        sub.d           $a0, $sp, $a0
      8c:   00150083        or              $sp, $a0, $zero
      ...
    
    Actually, it equals to a single instruction "sub.d $sp, $sp, $a0", but
    there is no proper condition to check it as a label like GCC, and so the
    beginning thought is not a good way.
    
    Essentially, there are two special frame pointer instructions which are
    "addi.d $fp, $sp, imm" and "addi.d $sp, $fp, imm", the first one points
    fp to the original stack top and the second one restores the original
    stack bottom from fp.
    
    Based on the above analysis, in order to avoid adding an arch-specific
    update_cfi_state(), we just add a member "frame_pointer" in the "struct
    symbol" as a label to avoid affecting the current normal case, then set
    it as true only if there is "addi.d $sp, $fp, imm". The last is to check
    this label for the two frame pointer instructions to change the cfa base
    and cfa offset in update_cfi_state().
    
    Tested with the following two configs:
    (1) CONFIG_RANDOMIZE_KSTACK_OFFSET=y &&
        CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=n
    (2) CONFIG_RANDOMIZE_KSTACK_OFFSET=y &&
        CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
    
    By the way, there is no effect for x86 with this patch, tested on the
    x86 machine with Fedora 40 system.
    
    Cc: stable@vger.kernel.org # 6.9+
    Signed-off-by: default avatarTiezhu Yang <yangtiezhu@loongson.cn>
    Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
    da5b2ad1
decode.c 8.44 KB