From 9866b9bca9829ef83948cba28e8cef8d8336fc95 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Fri, 1 Aug 2025 00:49:27 +0200 Subject: [PATCH] SIGSEGV: handle and pretty-print on amd64-linux --- src/stub/Makefile | 2 + src/stub/src/amd64-linux.elf-entry.S | 198 +++++++++++++++++++++++++++ src/stub/src/amd64-linux.elf-fold.S | 11 ++ 3 files changed, 211 insertions(+) diff --git a/src/stub/Makefile b/src/stub/Makefile index 9e1670a2..208a55ec 100644 --- a/src/stub/Makefile +++ b/src/stub/Makefile @@ -423,6 +423,8 @@ tc.amd64-linux.elf.gcc = amd64-linux-gcc-3.4.4 -fPIC -m64 -nostdinc -MMD -MT $@ tc.amd64-linux.elf.gcc += -fno-exceptions -fno-asynchronous-unwind-tables tc.amd64-linux.elf.gcc += -Wall -W -Wcast-align -Wcast-qual -Wstrict-prototypes -Wwrite-strings -Werror +#amd64-linux.elf-entry.h amd64-linux.elf-fold.h: tc.amd64-linux.elf.gcc += -DTEST_SIGSEGV + amd64-linux.elf-entry.h: $(srcdir)/src/$$T.S @echo; echo TARGET=$@ PATH=$(PATH); echo $(call tc,gcc) -c -x assembler-with-cpp $< -o tmp/$T.bin diff --git a/src/stub/src/amd64-linux.elf-entry.S b/src/stub/src/amd64-linux.elf-entry.S index b482e1f5..0f070301 100644 --- a/src/stub/src/amd64-linux.elf-entry.S +++ b/src/stub/src/amd64-linux.elf-entry.S @@ -95,6 +95,45 @@ _start: .globl _start push %rcx // argc push %rdx // param for atexit() +#if TEST_SIGSEGV +// install SIGSEGV handler for debugging +SIGSEGV= 11 +SA_SIGINFO= 4 // /usr/include/bits/sigaction.h +SA_RESTORER= 0x04000000 +__NR_rt_sigaction= 13 // /usr/include/asm/unistd_64.h + push %rdi // save reg + sub %edx,%edx // arg3= 0 ==> do not save old sigaction + push %rdx // .sa_mask: 64 bits (8 bytes) of flags for signals + lea __restore_rt(%rip),%rax; push %rax // .sa_restorer + push $SA_RESTORER | SA_SIGINFO // .sa_flags + lea sigsegv_sigaction(%rip),%rax; push %rax // .sa_sigaction + push %rsp; pop %rsi // arg2= &new struct sigaction + push $SIGSEGV; pop %rdi // arg1= signum + push $8; pop %r10 // sys4= sizeof(__sigset_t) ==> 64 bits + push $__NR_rt_sigaction; pop %rax; syscall + add $(3 + 1) * NBPW,%rsp // toss struct sigaction + pop %rdi // restore reg + +#if 0 //{ TEST ONLY + movl $0x18181818,%r8d + movl $0x19191919,%r9d + movl $0x1a1a1a1a,%r10d + movl $0x1b1b1b1b,%r11d + movl $0x1c1c1c1c,%r12d + movl $0x1d1d1d1d,%r13d + movl $0x1e1e1e1e,%r14d + movl $0x1f1f1f1f,%r15d + movl $0xaaaaaaaa,%eax + movl $0xbbbbbbbb,%ebx + movl $0xcccccccc,%ecx + movl $0xdddddddd,%edx + movl $0x55555555,%ebp + movl $0x66666666,%esi + movl $0x77777777,%edi + movl (%rdx),%edx // force SIGSEGV +#endif //} +#endif // TEST_SIGSEGV + #define old_sp %rbp F_FRAME= 7*NBPW F_ENTR= 6*NBPW; F_PMASK= F_ENTR @@ -295,6 +334,165 @@ eof_n2b: add $D_FOLD,%rax // beyond .data jmp *%rax // goto unfolded stub +#if TEST_SIGSEGV +__NR_rt_sigreturn= 15 + .balign 16 +__restore_rt: + endbr64 + mov $__NR_rt_sigreturn,%eax + syscall + + .balign 16 +sigsegv_sigaction: + endbr64 + push %rdx // save &ucontext_t + +// print /proc/self/maps of child (same as parent: the beauty of fork()) + mov $end_announce_sigaction - announce_sigaction,%edx // arg3 len + lea announce_sigaction(%rip),%rsi // arg2 buf + push $2; pop %rdi // arg1 fd_stderr + push $__NR_write; pop %eax; syscall // ignore error on write() + + xor %esi,%esi // arg2 O_RDONLY + lea proc_self_maps(%rip),%arg1 + push $__NR_open; pop %rax; syscall + mov %rax,%r12 // fd_maps +BUFLEN= 4096 + mov $BUFLEN,%ebx; sub %rbx,%rsp // allocate buffer +loop_maps: + mov %ebx,%edx // arg3 buflen + push %rsp; pop %rsi // arg2 buffer + mov %r12,%rdi // arg1 fd_maps + xor %eax,%eax; syscall // __NR_read + test %eax,%eax; jle done_maps // ignore error on read() + mov %eax,%edx // arg3 buflen + push %rsp; pop %rsi // arg2 buf + push $2; pop %rdi // arg1 fd_stderr + push $__NR_write; pop %rax; syscall // ignore error on write() + jmp loop_maps +done_maps: + mov %r12,%rdi //arg1 fd_maps + add %rbx,%rsp // discard buffer + push $__NR_close; pop %rax; syscall +// end printing of /proc/self/maps + + pop %rdx // restore &ucontext_t +__NR_fork= 57 + push $__NR_fork; pop %rax; call sys_check + test %eax,%eax; je child +parent: + jmp parent // spin; paused by gdb + +proc_self_cmdline: + .asciz "/proc/self/cmdline" +announce_sigaction: + .asciz "\n\nSIGSEGV address space:\n" +end_announce_sigaction: + +proc_self_maps: + .asciz "/proc/self/maps" +minus_q: + .asciz "-q" +path_gdb: + .asciz "/usr/bin/gdb" +commands_gdb: + .ascii "info inferiors\n" + + .ascii "print \"r8, r9\"\n" + .ascii "x/2xg $rdx + 5*8\n" + .ascii "print \"r10, r11\"\n" + .ascii "x/2xg\n" + .ascii "print \"r12, r13\"\n" + .ascii "x/2xg\n" + .ascii "print \"r14, r15\"\n" + .ascii "x/2xg\n" + + .ascii "print \"rdi, rsi\"\n" + .ascii "x/2xg\n" + .ascii "print \"rbp, rbx\"\n" + .ascii "x/2xg\n" + .ascii "print \"rdx, rax\"\n" + .ascii "x/2xg\n" + .ascii "print \"rcx, rsp\"\n" + .ascii "x/2xg\n" + .ascii "print \"rip, efl\"\n" + .ascii "x/2xg\n" + + .ascii "set $pc = *(long *)($rdx + 168)\n" + .ascii "print \"faulting instr\"\n" + .ascii "x/i $pc\n" + .ascii "print \"fault context\"\n" + .ascii "x/24i $pc - 0x20\n" + + .ascii "kill\n" + .ascii "quit 1" + .byte 0 +commands_gdb_end: + +child: +PATH_MAX= 4096 + sub $PATH_MAX,%rsp + lea proc_self_cmdline(%rip),%arg1 +O_RDONLY= 0 + push $O_RDONLY; pop %arg2 + push $__NR_open; pop %rax; call sys_check + push %rax; pop %arg1 // fd + push %rsp; pop %arg2 // buffer + push $PATH_MAX; pop %arg3 +__NR_read= 0 + push $__NR_read; pop %rax; call sys_check + push $__NR_close; pop %eax; call sys_check + +__NR_getppid= 110 + push $__NR_getppid; pop %eax; syscall + push %rax; pop %rsi + xor %eax,%eax; push %rax; push %rax // decimal(pid) fits in 16 bytes + push %rsp; pop %rdi; call unsimal +// argv + push %rsp; pop %rsi // fence post: &pid.unsimal + push $0 // argv[4] + push %rsi // arg3 pid + add $16,%rsi; push %rsi // arg2 exename + lea minus_q(%rip),%rax; push %rax // arg1 "-q" + add $(path_gdb - minus_q),%rax; push %rax // arg[0] "/usr/bin/gdb" + +#if 1 //{ pipe input to gdb + xor %edi,%edi; push $__NR_close; pop %rax; syscall + push %rax; push %rsp; pop %rdi // &fd_pipe[2]; 4 bytes each +__NR_pipe= 22 + push $__NR_pipe; pop %rax; call sys_check + pop %rdi; shr $32,%rdi // arg1 write side of pipe + + push $commands_gdb_end - commands_gdb; pop %arg3 + lea commands_gdb(%rip),%arg2 +__NR_write= 1 + push $__NR_write; pop %rax; call sys_check + push $__NR_close; pop %rax; syscall +#endif //} + + push $0; pop %arg3 // _environ BUG + push %rsp; pop %arg2 // argv + movq (%arg2),%arg1 // "/usr/bin/gdb" +__NR_execve= 59 + push $__NR_execve; pop %rax; call sys_check + hlt + +unsimal: // (dst, value) + push $10; pop %rcx // radix + mov %esi,%eax // value + call 0f + movb $0,(%rdi) // terminator + ret +0: + xor %edx,%edx; div %ecx; push %rdx // eax= quo(%edx:%eax / %ecx); edx= rem + // 'div' undefines all flags! + test %eax,%eax; je 1f; call 0b +1: + pop %rax; add $'0',%eax + stosb + ret +#endif // TEST_SIGSEGV + sys_check: push %rax // save __NR_ for debug syscall diff --git a/src/stub/src/amd64-linux.elf-fold.S b/src/stub/src/amd64-linux.elf-fold.S index ebab5fc7..f03728da 100644 --- a/src/stub/src/amd64-linux.elf-fold.S +++ b/src/stub/src/amd64-linux.elf-fold.S @@ -177,6 +177,17 @@ no_env_pse: mov %ebp,%ebx # fd movq %rax,3*NBPW(%rsp) # entry +#if TEST_SIGSEGV +// Uninstall SIGSEGV handler +SIGSEGV= 11 +__NR_rt_sigaction= 13 // /usr/include/asm/unistd_64.h + push $8; pop %sys4 // sys_arg4 minimal byte count + xor %edx,%edx // no old + xor %esi,%esi // no new + push $SIGSEGV; pop %rdi + push $__NR_rt_sigaction; pop %eax; syscall +#endif // TEST_SIGSEGV + sz_Ehdr= 8*NBPW e_type= 16 ET_EXEC= 2