logo

mstrace

Small Linux strace(1) implementationgit clone https://anongit.hacktivis.me/git/mstrace.git/
commit: a9bc1a93890ac28224fd423a2f0cc580108c7ddd
parent d707cec7daf1f8ce513cd751e9ea7936bd6f9347
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Tue,  8 Apr 2025 00:44:42 +0200

split between syscall entry and exit

This way in the case of syscalls like nanosleep(2) where values
get modified, we cleanly have both before and after.

Diffstat:

Mmstrace.c29++++++-----------------------
Mprint_syscall.c69++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msyscalls.sh6+-----
Msyscalls.txt2--
4 files changed, 73 insertions(+), 33 deletions(-)

diff --git a/mstrace.c b/mstrace.c @@ -14,7 +14,8 @@ #include <sys/wait.h> #include <unistd.h> // getpid, fork, execvp -extern void print_syscall(__u64 nr, __u64 args[6]); +extern void print_syscall_entry(__u64 nr, __u64 args[6]); +extern void print_syscall_exit(__u64 nr, __u64 args[6]); pid_t child = -1; @@ -71,7 +72,6 @@ main(int argc, char *argv[]) return 1; } - bool neednl = false; struct { __u64 nr; @@ -106,17 +106,10 @@ main(int argc, char *argv[]) for(int i = 0; i < 6; i++) entry.args[i] = syscall_info.entry.args[i]; - // print execve(2) at entry time because parameters passed gets cleaned up - // meanwhile getcwd(2) pass a buffer and so needs to be printed at return time - if(entry.nr == SYS_execve) - { - print_syscall(entry.nr, entry.args); - neednl = true; - } - + print_syscall_entry(entry.nr, entry.args); break; case PTRACE_SYSCALL_INFO_EXIT: - if(entry.nr != SYS_execve) print_syscall(entry.nr, entry.args); + if(entry.nr != SYS_execve) print_syscall_exit(entry.nr, entry.args); if(syscall_info.exit.is_error) { @@ -129,32 +122,22 @@ main(int argc, char *argv[]) { fprintf(stderr, " = %" PRIi64 "\n", (int64_t)syscall_info.exit.rval); } - neednl = false; entry.nr = 0; break; case PTRACE_SYSCALL_INFO_SECCOMP: - print_syscall(syscall_info.seccomp.nr, syscall_info.seccomp.args); - neednl = true; + print_syscall_exit(syscall_info.seccomp.nr, syscall_info.seccomp.args); // TODO: Check how strace manages this fprintf(stderr, " [seccomp_ret = %" PRIu32 "]", syscall_info.seccomp.ret_data); break; case PTRACE_SYSCALL_INFO_NONE: - if(neednl) fprintf(stderr, "\n"); - fprintf(stderr, "[syscall-info] none\n"); - neednl = false; break; } } if(entry.nr != 0) - { - print_syscall(entry.nr, entry.args); - neednl = true; - } - - if(neednl) fprintf(stderr, "\n"); + print_syscall_exit(entry.nr, entry.args); if(WIFEXITED(status)) { diff --git a/print_syscall.c b/print_syscall.c @@ -18,6 +18,12 @@ extern pid_t child; +union u8_timespec +{ + struct timespec ts; + uint8_t buf[sizeof(struct timespec)]; +}; + static char * read_str(intptr_t addr, size_t len) { @@ -44,6 +50,22 @@ read_str(intptr_t addr, size_t len) return str_buf; } +static struct timespec * +read_timespec(intptr_t addr) +{ + static union u8_timespec local; + + for(size_t i = 0; i < sizeof(struct timespec);) + { + unsigned long tmp = ptrace(PTRACE_PEEKDATA, child, addr + i); + + memcpy(local.buf + i, &tmp, sizeof(tmp)); + i += sizeof(tmp); + } + + return &(local.ts); +} + #define Pri_str "\"%s\"" #define Pri_i "%" PRIi64 #define Pri_u "%" PRIu64 @@ -51,21 +73,62 @@ read_str(intptr_t addr, size_t len) #define Pri_x "0x%" PRIx64 void -print_syscall(__u64 nr, __u64 args[6]) +print_syscall_entry(__u64 nr, __u64 args[6]) +{ + switch(nr) + { + case SYS_execve: + fprintf(stderr, "<%s("Pri_str ", " Pri_x ", " Pri_x ")\n", str_syscalls[nr], read_str(args[0], PATH_MAX), (int64_t)args[1], (int64_t)args[2]); + return; + case SYS_nanosleep: + { + struct timespec *rqtp = read_timespec(args[0]); + struct timespec *rmtp = read_timespec(args[1]); + + fprintf(stderr, + "<%s({tv_sec = " Pri_i ", tv_nsec = " Pri_i "}, {tv_sec = " Pri_i ", tv_nsec = " Pri_i + "})\n", + str_syscalls[nr], + rqtp->tv_sec, + rqtp->tv_nsec, + rmtp->tv_sec, + rmtp->tv_nsec); + return; + } + } +} + +void +print_syscall_exit(__u64 nr, __u64 args[6]) { switch(nr) { #include "syscalls_cases.h" + case SYS_nanosleep: + { + struct timespec *rqtp = read_timespec(args[0]); + struct timespec *rmtp = read_timespec(args[1]); + + fprintf(stderr, + ">%s({tv_sec = " Pri_i ", tv_nsec = " Pri_i "}, {tv_sec = " Pri_i ", tv_nsec = " Pri_i + "})", + str_syscalls[nr], + rqtp->tv_sec, + rqtp->tv_nsec, + rmtp->tv_sec, + rmtp->tv_nsec); + break; + } // fallback default: assert(nr < SYSCALLS_MAX); if(str_syscalls[nr] != NULL) { - fprintf(stderr, "%s(", str_syscalls[nr]); + fprintf(stderr, ">%s(", str_syscalls[nr]); } else { - fprintf(stderr, "syscall(%" PRIu64 ", ", (uint64_t)nr); + fprintf(stderr, ">syscall(%" PRIu64 ", ", (uint64_t)nr); } fprintf(stderr, diff --git a/syscalls.sh b/syscalls.sh @@ -2,7 +2,7 @@ while read syscall args do - fmt='"%s("' + fmt='">%s("' fmt_args='str_syscalls[nr]' fmt_sep='' @@ -40,10 +40,6 @@ do fmt="${fmt}${fmt_sep}Pri_str" fmt_args="${fmt_args}, read_str(args[$n], PATH_MAX)" ;; - timespec) - fmt="${fmt}${fmt_sep}\"{tv_sec=\"Pri_i\", tv_nsec=\"Pri_i\"}\"" - fmt_args="${fmt_args}, (int64_t)(((const struct timespec *)args[$n])->tv_sec), (int64_t)(((const struct timespec *)args[$n])->tv_nsec)" - ;; *) echo "syscalls.sh: error: Unhandled type ${i}" >&2 exit 1 diff --git a/syscalls.txt b/syscalls.txt @@ -29,7 +29,6 @@ close int creat path oct dup int dup2 int int -execve path hex hex exit_group int faccessat int path oct oct fchmod int oct @@ -65,4 +64,3 @@ unlink path unlinkat int path vfork write int buf size -nanosleep timespec timespec