]> granicus.if.org Git - strace/commitdiff
Set saner MAX_ARGS (6 or 8) for X86_64 and I386
authorDenys Vlasenko <dvlasenk@redhat.com>
Fri, 19 Aug 2011 15:41:28 +0000 (17:41 +0200)
committerDenys Vlasenko <dvlasenk@redhat.com>
Tue, 23 Aug 2011 10:53:01 +0000 (12:53 +0200)
I noticed that tcp->u_args[MAX_ARGS] array is way larger than
I'd expect: for all arches except HPPA it has 32 (!) elements.

I looked at the code and so far I spotted only one abuser of
this fact: sys_sigreturn. On several arches, it saves sigset_t
into tcp->u_args[1...N] on entry and prints it on exit, a-la

memcpy(&tcp->u_arg[1], &sc.oldmask[0], sizeof(sigset_t))

The problem here is that in glibc sigset_t is insanely large:
128 bytes, and using sizeof(sigset_t) in memcpy will overrun
&tcp->u_args[1] even with MAX_ARGS == 32:
On 32 bits, sizeof(tcp->u_args) == 32*4 == 128 bytes!
We may already have a bug there!

This commit changes the code to save NSIG / 8 bytes only.
NSIG can't ever be > 256, and in practice is <= 129,
thus NSIG / 8 is <= 16 bytes == 4 32-bit words,
and even MAX_ARGS == 5 should be enough for saving signal masks.

* defs.h: Reduce MAX_ARGS for X86_64 and I386 from 32 to 8
for FreeBSD and to 6 for everyone else. Add comment about current
state of needed MAX_ARGS.
* signal.c: Add comment about size of sigset_t.
(sprintsigmask): Reduce static string buffer from 8k to 2k.
(sys_sigreturn): Fix sigset saving to save only NSIG / 8 bytes,
not sizeof(sigset_t) bytes.
* linux/mips/syscallent.h: Reduce nargs of printargs-type syscall to 7.
* linux/arm/syscallent.h: Reduce nargs of printargs-type syscall to 6.
* linux/i386/syscallent.h: Likewise.
* linux/m68k/syscallent.h: Likewise.
* linux/powerpc/syscallent.h: Likewise.
* linux/s390/syscallent.h: Likewise.
* linux/s390x/syscallent.h: Likewise.
* linux/sh/syscallent.h: Likewise.
* linux/sh64/syscallent.h: Likewise.
* linux/sparc/syscallent.h: Likewise.

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
12 files changed:
defs.h
linux/arm/syscallent.h
linux/i386/syscallent.h
linux/m68k/syscallent.h
linux/mips/syscallent.h
linux/powerpc/syscallent.h
linux/s390/syscallent.h
linux/s390x/syscallent.h
linux/sh/syscallent.h
linux/sh64/syscallent.h
linux/sparc/syscallent.h
signal.c

diff --git a/defs.h b/defs.h
index 02242afc7f39c2e1a024b1cd8ee395fda3fa4150..64e4da08715ab61183549541c40374b76ebc2d06 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -48,7 +48,7 @@
 # endif
 #endif
 
-/* configuration section */
+/* Configuration section */
 #ifndef MAX_QUALS
 #if defined(LINUX) && defined(MIPS)
 #define MAX_QUALS      7000    /* maximum number of syscalls, signals, etc. */
 #ifndef DEFAULT_ACOLUMN
 #define DEFAULT_ACOLUMN        40      /* default alignment column for results */
 #endif
+
+/* Maximum number of args to a syscall.
+ *
+ * Make sure that all entries in all syscallent.h files
+ * have nargs <= MAX_ARGS!
+ * linux/<ARCH>/syscallent.h: ia64 has many syscalls with
+ * nargs = 8, mips has two with nargs = 7 (both are printargs),
+ * all others are <= 6.
+ * freebsd/i386/syscallent.h: one syscall with nargs = 8
+ * (sys_sendfile, looks legitimate)
+ * and one with nargs = 7 (sys_mmap, maybe it should have 6?).
+ * sunos4/syscallent.h: all are <= 6.
+ * svr4/syscallent.h: all are -1.
+ */
 #ifndef MAX_ARGS
 # ifdef HPPA
-#  define MAX_ARGS     6       /* maximum number of args to a syscall */
+#  define MAX_ARGS     6
+# elif defined X86_64 || defined I386
+#  ifdef FREEBSD
+#   define MAX_ARGS    8
+#  else
+#   define MAX_ARGS    6
+#  endif
 # else
 /* Way too big. Switch your arch to saner size after you tested that it works */
-#  define MAX_ARGS     32      /* maximum number of args to a syscall */
+#  define MAX_ARGS     32
 # endif
 #endif
+
 #ifndef DEFAULT_SORTBY
 #define DEFAULT_SORTBY "time"  /* default sorting method for call profiling */
 #endif
index 9203b2366824ecf1f1d0c759f65a61d9f649f08f..48cd3dd4f55cb97d0f517a6a744bb572a9551858 100644 (file)
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index 7779d380acc31a018900278bc856bd7454549e08..1781223d29adca431c1e6747c13e99cf9770c827 100644 (file)
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index c64575fd0d5804ac94eb9dc417ea5b540933ae1a..b5c35d111b0d99d790918c40e11535900f4b6def 100644 (file)
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index 2306ab10d71ff46a922c937bdaada62a7fd5e370..e9f953a25b193208243e382d0248932aafc71e0f 100644 (file)
        { 0,    0,      printargs,              "SYS_3999"      }, /* 3999 */ /* end of POSIX */
 #if !defined (LINUX_MIPSN32) && !defined (LINUX_MIPSN64)
        /* For an O32 strace, decode the o32 syscalls.  */
-       { 8,    0,      printargs,              "syscall"       }, /* 4000 */ /* start of Linux o32 */
+       { 7,    0,      printargs,              "syscall"       }, /* 4000 */ /* start of Linux o32 */
        { 1,    TP,     sys_exit,               "exit"          }, /* 4001 */
        { 0,    TP,     sys_fork,               "fork"          }, /* 4002 */
        { 3,    TD,     sys_read,               "read"          }, /* 4003 */
index e2b950ebc9954eee092b4427e637ea7f75e8a535..6be7277ee2dbb0090b12b3c977d8c456bc769969 100644 (file)
        { 5,    0,      printargs,              "pciconfig_read"        }, /* 198 */
        { 5,    0,      printargs,              "pciconfig_write"       }, /* 199 */
        { 3,    0,      printargs,              "pciconfig_iobase"      }, /* 200 */
-       { 8,    0,      printargs,              "MOL"                   }, /* 201 */
+       { 6,    0,      printargs,              "MOL"                   }, /* 201 */
        { 3,    TD,     sys_getdents64,         "getdents64"            }, /* 202 */
        { 2,    TF,     sys_pivotroot,          "pivot_root"            }, /* 203 */
        { 3,    TD,     sys_fcntl,              "fcntl64"               }, /* 204 */
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index 38d0e03e5e10bd0c244efa3ca3bae80583f89ac5..9886d3b54470c6b825ccf21ca20b2f1e6c8675b9 100644 (file)
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index 8b03db8a1861668df52de26de91b776bf5de28da..00439caaa4ef1617d4160bce77625321b103877c 100644 (file)
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index d73e951de4fefef7ce1220d73e43cf4e89de416d..8dfdd16acc2fb5414984131daa68b3b72dce1cc7 100644 (file)
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index 75617cd2f2bdee0d615c5806be2bdd99fdac1967..ea01878ec2ac3ac2c207f39973c58dc1eba29c07 100644 (file)
 #if SYS_socket_subcall != 400
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 400 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 400 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 401 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 402 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 403 */
index 62e0bccbc924a7d141dec73c0e8edf34e2bb534d..23269e0a63a5170b972b5321ba5d4c20a7dd51d8 100644 (file)
 #if SYS_socket_subcall != 353
  #error fix me
 #endif
-       { 8,    0,      printargs,              "socket_subcall"}, /* 353 */
+       { 6,    0,      printargs,              "socket_subcall"}, /* 353 */
        { 3,    TN,     sys_socket,             "socket"        }, /* 354 */
        { 3,    TN,     sys_bind,               "bind"          }, /* 355 */
        { 3,    TN,     sys_connect,            "connect"       }, /* 356 */
index 889134ad57d1a48bb8ad8698bbe7d43a0ec107ea..8c9433d69e9969543cbbe4f9d23da58753a2d876 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -262,6 +262,28 @@ static const struct xlat sigprocmaskcmds[] = {
 #endif
 #endif
 
+/* Note on the size of sigset_t:
+ *
+ * In glibc, sigset_t is an array with space for 1024 bits (!),
+ * even though all arches supported by Linux have only 64 signals
+ * except MIPS, which has 128. IOW, it is 128 bytes long.
+ *
+ * In-kernel sigset_t is sized correctly (it is either 64 or 128 bit long).
+ * However, some old syscall return only 32 lower bits (one word).
+ * Example: sys_sigpending vs sys_rt_sigpending.
+ *
+ * Be aware of this fact when you try to
+ *     memcpy(&tcp->u_arg[1], &something, sizeof(sigset_t))
+ * - sizeof(sigset_t) is much bigger than you think,
+ * it may overflow tcp->u_arg[] array, and it may try to copy more data
+ * than is really available in <something>.
+ * Similarly,
+ *     umoven(tcp, addr, sizeof(sigset_t), &sigset)
+ * may be a bad idea: it'll try to read much more data than needed
+ * to fetch a sigset_t.
+ * Use (NSIG / 8) as a size instead.
+ */
+
 const char *
 signame(int sig)
 {
@@ -310,11 +332,21 @@ static const char *
 sprintsigmask(const char *str, sigset_t *mask, int rt)
 /* set might include realtime sigs */
 {
+       /* Was [8 * sizeof(sigset_t) * 8], but
+        * glibc sigset_t is huge (1024 bits = 128 *bytes*),
+        * and we were ending up with 8k (!) buffer here.
+        *
+        * No Unix system can have sig > 255
+        * (waitpid API won't be able to indicate death from one)
+        * and sig 0 doesn't exist either.
+        * Therefore max possible no of sigs is 255: 1..255
+        */
+       static char outstr[8 * 255];
+
        int i, nsigs;
        int maxsigs;
        const char *format;
        char *s;
-       static char outstr[8 * sizeof(sigset_t) * 8];
 
        strcpy(outstr, str);
        s = outstr + strlen(outstr);
@@ -1134,7 +1166,7 @@ sys_sigreturn(struct tcb *tcp)
                if (umove(tcp, usp+__SIGNAL_FRAMESIZE, &sc) < 0)
                        return 0;
                tcp->u_arg[0] = 1;
-               memcpy(&tcp->u_arg[1], &sc.oldmask[0], sizeof(sigset_t));
+               memcpy(&tcp->u_arg[1], &sc.oldmask[0], NSIG / 8);
        } else {
                tcp->u_rval = tcp->u_error = 0;
                if (tcp->u_arg[0] == 0)
@@ -1177,14 +1209,15 @@ sys_sigreturn(struct tcb *tcp)
                if (umove(tcp, sp + 16 + SIGFRAME_SC_OFFSET, &sc) < 0)
                        return 0;
                tcp->u_arg[0] = 1;
-               memcpy(tcp->u_arg + 1, &sc.sc_mask, sizeof(sc.sc_mask));
+               memcpy(tcp->u_arg + 1, &sc.sc_mask, NSIG / 8);
        }
        else {
                sigset_t sigm;
                tcp->u_rval = tcp->u_error = 0;
                if (tcp->u_arg[0] == 0)
                        return 0;
-               memcpy(&sigm, tcp->u_arg + 1, sizeof(sigm));
+               sigemptyset(&sigm);
+               memcpy(&sigm, tcp->u_arg + 1, NSIG / 8);
                tcp->auxstr = sprintsigmask("mask now ", &sigm, 0);
                return RVAL_NONE | RVAL_STR;
        }
@@ -1377,14 +1410,15 @@ sys_sigreturn(struct tcb *tcp)
                if (umove(tcp, sp + SIGFRAME_UC_OFFSET, &uc) < 0)
                        return 0;
                tcp->u_arg[0] = 1;
-               memcpy(tcp->u_arg + 1, &uc.uc_sigmask, sizeof(uc.uc_sigmask));
+               memcpy(tcp->u_arg + 1, &uc.uc_sigmask, NSIG / 8);
        }
        else {
                sigset_t sigm;
                tcp->u_rval = tcp->u_error = 0;
                if (tcp->u_arg[0] == 0)
                        return 0;
-               memcpy(&sigm, tcp->u_arg + 1, sizeof(sigm));
+               sigemptyset(&sigm);
+               memcpy(&sigm, tcp->u_arg + 1, NSIG / 8);
                tcp->auxstr = sprintsigmask("mask now ", &sigm, 0);
                return RVAL_NONE | RVAL_STR;
        }