]> granicus.if.org Git - strace/commitdiff
Fix decoding of invalid syscalls mapped to indirect subcalls
authorDmitry V. Levin <ldv@altlinux.org>
Tue, 9 Aug 2016 00:07:53 +0000 (00:07 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Tue, 9 Aug 2016 01:13:24 +0000 (01:13 +0000)
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.

defs.h
syscall.c
tests/nsyscalls.c

diff --git a/defs.h b/defs.h
index 709d9a4a5dc471f77718b2211c44c6c86e17621e..1893ef50f6d40f7a2a82474cbd21a06548c5079b 100644 (file)
--- 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) \
index e5ac7db55e5f36b7634a0c66a25abd9a4abf7da9..8c158ad94566b0b26d52086b6922a653a54f61d0 100644 (file)
--- 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 : "????");
                /*
                 * " <unavailable>" 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);
index c9c92c4a702c99df3d3ab529fad4c0e07ba7addd..3d5d9732df42859a39f0b5cef5549c7af91387f7 100644 (file)
@@ -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;