From: Dmitry V. Levin Date: Tue, 9 Aug 2016 00:07:53 +0000 (+0000) Subject: Fix decoding of invalid syscalls mapped to indirect subcalls X-Git-Tag: v4.14~215 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=60d7ec80d940815482873cfe3f280a4eb16e1864;p=strace Fix decoding of invalid syscalls mapped to indirect subcalls When the syscall number returned by arch_get_scno is a mapped indirect subcall (i.e. mapped subcall of socketcall or ipc syscall), do not mistakenly treat it as a valid indirect subcall. * defs.h (SCNO_IS_VALID): Treat scno with TRACE_INDIRECT_SUBCALL flag as invalid. * syscall.c (syscall_name): Do no shuffle scno. (trace_syscall_entering, trace_syscall_exiting): Use tcp->s_ent->sys_name instead of syscall_name. (get_scno): In case of invalid syscall, allocate a dynamic struct sysent containing an appropriate .sys_name. * tests/nsyscalls.c (main) [SYS_socket_subcall]: Check decoding of direct syscall number SYS_socket_subcall+1. (main) [SYS_ipc_subcall]: Check decoding of direct syscall number SYS_ipc_subcall+1. --- diff --git a/defs.h b/defs.h index 709d9a4a..1893ef50 100644 --- a/defs.h +++ b/defs.h @@ -790,10 +790,13 @@ extern unsigned num_quals; #endif /* !IN_MPERS_BOOTSTRAP */ /* - * If you need non-NULL sysent[scno].sys_func and sysent[scno].sys_name + * If you need non-NULL sysent[scno].sys_func, non-NULL sysent[scno].sys_name, + * and non-indirect sysent[scno].sys_flags. */ #define SCNO_IS_VALID(scno) \ - ((unsigned long)(scno) < nsyscalls && sysent[scno].sys_func) + ((unsigned long)(scno) < nsyscalls \ + && sysent[scno].sys_func \ + && !(sysent[scno].sys_flags & TRACE_INDIRECT_SUBCALL)) /* Only ensures that sysent[scno] isn't out of range */ #define SCNO_IN_RANGE(scno) \ diff --git a/syscall.c b/syscall.c index e5ac7db5..8c158ad9 100644 --- a/syscall.c +++ b/syscall.c @@ -753,7 +753,7 @@ syscall_name(long scno) if (SCNO_IS_VALID(scno)) return sysent[scno].sys_name; else { - sprintf(buf, "syscall_%lu", shuffle_scno(scno)); + sprintf(buf, "syscall_%lu", scno); return buf; } } @@ -787,10 +787,7 @@ trace_syscall_entering(struct tcb *tcp) if (res != 1) { printleader(tcp); - if (scno_good != 1) - tprints("????" /* anti-trigraph gap */ "("); - else - tprintf("%s(", syscall_name(tcp->scno)); + tprintf("%s(", scno_good == 1 ? tcp->s_ent->sys_name : "????"); /* * " " will be added later by the code which * detects ptrace errors. @@ -849,7 +846,7 @@ trace_syscall_entering(struct tcb *tcp) #endif printleader(tcp); - tprintf("%s(", syscall_name(tcp->scno)); + tprintf("%s(", tcp->s_ent->sys_name); if ((tcp->qual_flg & QUAL_RAW) && SEN_exit != tcp->s_ent->sen) res = printargs(tcp); else @@ -910,7 +907,7 @@ trace_syscall_exiting(struct tcb *tcp) if ((followfork < 2 && printing_tcp != tcp) || (tcp->flags & TCB_REPRINT)) { tcp->flags &= ~TCB_REPRINT; printleader(tcp); - tprintf("<... %s resumed> ", syscall_name(tcp->scno)); + tprintf("<... %s resumed> ", tcp->s_ent->sys_name); } printing_tcp = tcp; @@ -1299,13 +1296,19 @@ get_scno(struct tcb *tcp) tcp->s_ent = &sysent[tcp->scno]; tcp->qual_flg = qual_flags[tcp->scno]; } else { - static const struct_sysent unknown = { - .nargs = MAX_ARGS, - .sys_flags = 0, - .sys_func = printargs, - .sys_name = "system call", - }; - tcp->s_ent = &unknown; + struct { + struct_sysent ent; + char buf[sizeof("syscall_%lu") + sizeof(long) * 3]; + } *s = xcalloc(1, sizeof(*s)); + set_tcb_priv_data(tcp, s, free); + + sprintf(s->buf, "syscall_%lu", shuffle_scno(tcp->scno)); + s->ent.nargs = MAX_ARGS; + s->ent.sen = SEN_printargs; + s->ent.sys_func = printargs; + s->ent.sys_name = s->buf; + + tcp->s_ent = &s->ent; tcp->qual_flg = QUAL_RAW | DEFAULT_QUAL_FLAGS; if (debug_flag) error_msg("pid %d invalid syscall %ld", tcp->pid, tcp->scno); diff --git a/tests/nsyscalls.c b/tests/nsyscalls.c index c9c92c4a..3d5d9732 100644 --- a/tests/nsyscalls.c +++ b/tests/nsyscalls.c @@ -57,10 +57,8 @@ static const struct_sysent syscallent[] = { # define SYSCALL_BIT 0 #endif -static const unsigned long nr = ARRAY_SIZE(syscallent) | SYSCALL_BIT; - -int -main(void) +static void +test_syscall(const unsigned long nr) { static const kernel_ulong_t a[] = { (kernel_ulong_t) 0xface0fedbadc0ded, @@ -71,14 +69,15 @@ main(void) (kernel_ulong_t) 0xface5fedbadc5ded }; - long rc = syscall(nr, a[0], a[1], a[2], a[3], a[4], a[5]); + long rc = syscall(nr | SYSCALL_BIT, + a[0], a[1], a[2], a[3], a[4], a[5]); #ifdef LINUX_MIPSO32 printf("syscall(%#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx)" - " = %ld ENOSYS (%m)\n", nr, + " = %ld ENOSYS (%m)\n", nr | SYSCALL_BIT, a[0], a[1], a[2], a[3], a[4], a[5], rc); #else printf("syscall_%lu(%#llx, %#llx, %#llx, %#llx, %#llx, %#llx)" - " = %ld (errno %d)\n", nr & (~SYSCALL_BIT), + " = %ld (errno %d)\n", nr, (unsigned long long) a[0], (unsigned long long) a[1], (unsigned long long) a[2], @@ -87,6 +86,20 @@ main(void) (unsigned long long) a[5], rc, errno); #endif +} + +int +main(void) +{ + test_syscall(ARRAY_SIZE(syscallent)); + +#ifdef SYS_socket_subcall + test_syscall(SYS_socket_subcall + 1); +#endif + +#ifdef SYS_ipc_subcall + test_syscall(SYS_ipc_subcall + 1); +#endif puts("+++ exited with 0 +++"); return 0;