为什么我在调用sys_pause系统调用时收到SIGSEGV?

时间:2017-09-28 22:53:27

标签: linux assembly gdb nasm x86-64

我正在尝试创建一个x86_64汇编程序,显示" SIGTERM收到"每当发送SIGTERM信号时。我的应用程序直接使用Linux系统调用:

%define sys_write        0x01
%define sys_rt_sigaction 0x0d
%define sys_pause        0x22
%define sys_exit         0x3c

%define SIGTERM 0x0f

%define STDOUT 0x01


; Definition of sigaction struct for sys_rt_sigaction
struc sigaction
    .sa_handler  resq 1
    .sa_flags    resq 1
    .sa_restorer resq 1
    .sa_mask     resq 1
endstruc


section .data

    ; Message shown when a syscall fails
    error_msg     db  'syscall error', 0x0a
    error_msg_len equ $ - error_msg

    ; Message shown when SIGTERM is received
    sigterm_msg     db  'SIGTERM received', 0x0a
    sigterm_msg_len equ $ - sigterm_msg


section .bss

    act resb sigaction_size
    val resd 1


section .text
global _start

_start:

    ; Initialize act
    lea rax, [handler]
    mov [act + sigaction.sa_handler], rax

    ; Set the handler
    mov rax, sys_rt_sigaction
    mov rdi, SIGTERM
    lea rsi, [act]
    mov rdx, 0x00
    mov r10, 0x08
    syscall

    ; Ensure the syscall succeeded
    cmp rax, 0
    jne error

    ; Pause until a signal is received
    mov rax, sys_pause
    syscall

    ; Upon success, jump to exit
    jmp exit

error:

    ; Display an error message
    mov rax, sys_write
    mov rdi, STDOUT
    mov rsi, error_msg
    mov rdx, error_msg_len
    syscall

    ; Set the return value to one
    mov dword [val], 0x01

exit:

    ; Terminate the application gracefully
    mov rax, sys_exit
    mov rdi, [val]
    syscall

handler:

    ; Display a message
    mov rax, sys_write
    mov rdi, STDOUT
    mov rsi, sigterm_msg
    mov rdx, sigterm_msg_len
    syscall

    ret

当我运行应用程序时,它会在sys_pause系统调用时挂起(如预期的那样),但是当我发送SIGTERM信号时,它会因分段错误而崩溃。

所以我将应用程序加载到GDB中以弄清楚发生了什么:

(gdb) break _start
Breakpoint 1 at 0x4000b0
(gdb) run
Starting program: [...] 

Breakpoint 1, 0x00000000004000b0 in _start ()
(gdb) info proc
process 9639
(gdb) continue
Continuing.

GDB会话挂起,然后我打开另一个终端并运行kill SIGTERM 9639。这导致以下输出:

Program received signal SIGTERM, Terminated.
0x00000000004000ec in _start ()
然后我跑了:

(gdb) disas _start
Dump of assembler code for function _start:
   0x00000000004000b0 <+0>:     lea    0x400123,%rax
   0x00000000004000b8 <+8>:     mov    %rax,0x600160
   0x00000000004000c0 <+16>:    mov    $0xd,%eax
   0x00000000004000c5 <+21>:    mov    $0xf,%edi
   0x00000000004000ca <+26>:    lea    0x600160,%rsi
   0x00000000004000d2 <+34>:    mov    $0x0,%edx
   0x00000000004000d7 <+39>:    mov    $0x8,%r10d
   0x00000000004000dd <+45>:    syscall 
   0x00000000004000df <+47>:    cmp    $0x0,%rax
   0x00000000004000e3 <+51>:    jne    0x4000ee <error>
   0x00000000004000e5 <+53>:    mov    $0x22,%eax
   0x00000000004000ea <+58>:    syscall 
=> 0x00000000004000ec <+60>:    jmp    0x400114 <exit>
End of assembler dump.

然后我继续申请:

(gdb) continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00000000004000ec in _start ()

永远不会调用信号处理程序,并且应用程序已崩溃。

我做错了什么?

2 个答案:

答案 0 :(得分:3)

在应用程序正常运行之前,需要进行两次更正。

sa_restorer

Jester向我指出this answer提到内核需要填写sa_restorer sigaction成员。

修复此问题需要定义SA_RESTORER

%define SA_RESTORER 0x04000000

...并初始化sa_restorersa_flags成员:

mov [act + sigaction.sa_flags], dword SA_RESTORER
lea rax, [restorer]
mov [act + sigaction.sa_restorer], rax

然后我为restorer函数添加了一个空存根:

restorer:

    ret

此时,调用处理程序时没有错误,但应用程序仍在崩溃......

sys_rt_sigreturn

显然,sa_restorer函数需要调用sys_rt_sigreturn系统调用。这需要定义sys_rt_sigreturn

%define sys_rt_sigreturn 0x0f

然后修改了restorer函数:

restorer:

    ; return from the signal handler
    mov rax, sys_rt_sigreturn
    syscall

此时,应用程序运行时没有崩溃。

答案 1 :(得分:3)

以下是Nathan Osman的整个工作和更正程序。在这里回答是因为这样的例子似乎不存在于互联网上的任何其他地方。

%define sys_write        0x01
%define sys_rt_sigaction 0x0d
%define sys_pause        0x22
%define sys_exit         0x3c
%define sys_rt_sigreturn 0x0f
%define SIGTERM 0x0f
%define SIGINT 0x02
%define STDOUT 0x01
%define SA_RESTORER 0x04000000

; Definition of sigaction struct for sys_rt_sigaction
struc sigaction
    .sa_handler  resq 1
    .sa_flags    resq 1
    .sa_restorer resq 1
    .sa_mask     resq 1
endstruc

section .data
    ; Message shown when a syscall fails
    error_msg     db  'syscall error', 0x0a
    error_msg_len equ $ - error_msg
    ; Message shown when SIGTERM is received
    sigterm_msg     db  'SIGTERM received', 0x0a
    sigterm_msg_len equ $ - sigterm_msg

section .bss
    act resb sigaction_size
    val resd 1

section .text
global _start
_start:
    ; Initialize act
    mov qword [act + sigaction.sa_handler], handler
    mov [act + sigaction.sa_flags], dword SA_RESTORER
    mov qword [act + sigaction.sa_restorer], restorer

    ; Set the handler
    mov rax, sys_rt_sigaction
    ;mov rdi, SIGINT
    mov rdi, SIGTERM
    lea rsi, [act]
    mov rdx, 0x00
    mov r10, 0x08
    syscall

    ; Ensure the syscall succeeded
    cmp rax, 0
    jne error

    ; Pause until a signal is received
    mov rax, sys_pause
    syscall

    ; Upon success, jump to exit
    jmp exit

error:
    ; Display an error message
    mov rax, sys_write
    mov rdi, STDOUT
    mov rsi, error_msg
    mov rdx, error_msg_len
    syscall

    ; Set the return value to one
    mov dword [val], 0x01

exit:
    ; Terminate the application gracefully
    mov rax, sys_exit
    mov rdi, [val]
    syscall

handler:
    ; Display a message
    mov rax, sys_write
    mov rdi, STDOUT
    mov rsi, sigterm_msg
    mov rdx, sigterm_msg_len
    syscall
    ret

restorer:
    ; return from the signal handler
    mov rax, sys_rt_sigreturn
    syscall

为了便于测试,您可以通过取消注释此处的第一个列表并对第二个列表进行注释来将SIGTERM替换为SIGINT。

    ;mov rdi, SIGINT
    mov rdi, SIGTERM

将其保存到signal.asm并使用

进行编译和运行
nasm -f elf64 signal.asm -o signal.o && ld signal.o && ./a.out

在SIGINT版本中,按Control-C中断程序应打印消息'SIGTERM received'(也可以更改它以使其更准确)。在SIGTERM版本中,而是在gdb

中运行它
$ gdb ./a.out
[GNU gdb...]
(gdb) r
Starting program: /path/a.out 

现在按Control-C

^C
Program received signal SIGINT, Interrupt.
0x0000000000400107 in _start ()
(gdb) signal SIGTERM
Continuing with signal SIGTERM.
SIGTERM received
[Inferior 1 (process 22742) exited normally]
(gdb)

按预期打印“收到的SIGTERM”消息。