diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2011-08-19 17:41:28 +0200 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2011-08-23 12:53:01 +0200 |
commit | d9560c108099394281012eb4bd7c46a46df6d31d (patch) | |
tree | da57c9bda67d8d48dcd6fcc71370c2f1ce19105c | |
parent | 9aa97968ed75103c1f8a18e079b73328710ebe1e (diff) | |
download | strace-d9560c108099394281012eb4bd7c46a46df6d31d.tar.gz strace-d9560c108099394281012eb4bd7c46a46df6d31d.tar.bz2 strace-d9560c108099394281012eb4bd7c46a46df6d31d.tar.xz |
Set saner MAX_ARGS (6 or 8) for X86_64 and I386
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>
-rw-r--r-- | defs.h | 27 | ||||
-rw-r--r-- | linux/arm/syscallent.h | 2 | ||||
-rw-r--r-- | linux/i386/syscallent.h | 2 | ||||
-rw-r--r-- | linux/m68k/syscallent.h | 2 | ||||
-rw-r--r-- | linux/mips/syscallent.h | 2 | ||||
-rw-r--r-- | linux/powerpc/syscallent.h | 4 | ||||
-rw-r--r-- | linux/s390/syscallent.h | 2 | ||||
-rw-r--r-- | linux/s390x/syscallent.h | 2 | ||||
-rw-r--r-- | linux/sh/syscallent.h | 2 | ||||
-rw-r--r-- | linux/sh64/syscallent.h | 2 | ||||
-rw-r--r-- | linux/sparc/syscallent.h | 2 | ||||
-rw-r--r-- | signal.c | 46 |
12 files changed, 75 insertions, 20 deletions
@@ -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. */ @@ -63,14 +63,35 @@ #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 diff --git a/linux/arm/syscallent.h b/linux/arm/syscallent.h index 9203b23..48cd3dd 100644 --- a/linux/arm/syscallent.h +++ b/linux/arm/syscallent.h @@ -435,7 +435,7 @@ #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 */ diff --git a/linux/i386/syscallent.h b/linux/i386/syscallent.h index 7779d38..1781223 100644 --- a/linux/i386/syscallent.h +++ b/linux/i386/syscallent.h @@ -434,7 +434,7 @@ #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 */ diff --git a/linux/m68k/syscallent.h b/linux/m68k/syscallent.h index c64575f..b5c35d1 100644 --- a/linux/m68k/syscallent.h +++ b/linux/m68k/syscallent.h @@ -432,7 +432,7 @@ #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 */ diff --git a/linux/mips/syscallent.h b/linux/mips/syscallent.h index 2306ab1..e9f953a 100644 --- a/linux/mips/syscallent.h +++ b/linux/mips/syscallent.h @@ -4002,7 +4002,7 @@ { 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 */ diff --git a/linux/powerpc/syscallent.h b/linux/powerpc/syscallent.h index e2b950e..6be7277 100644 --- a/linux/powerpc/syscallent.h +++ b/linux/powerpc/syscallent.h @@ -229,7 +229,7 @@ { 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 */ @@ -432,7 +432,7 @@ #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 */ diff --git a/linux/s390/syscallent.h b/linux/s390/syscallent.h index 38d0e03..9886d3b 100644 --- a/linux/s390/syscallent.h +++ b/linux/s390/syscallent.h @@ -432,7 +432,7 @@ #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 */ diff --git a/linux/s390x/syscallent.h b/linux/s390x/syscallent.h index 8b03db8..00439ca 100644 --- a/linux/s390x/syscallent.h +++ b/linux/s390x/syscallent.h @@ -431,7 +431,7 @@ #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 */ diff --git a/linux/sh/syscallent.h b/linux/sh/syscallent.h index d73e951..8dfdd16 100644 --- a/linux/sh/syscallent.h +++ b/linux/sh/syscallent.h @@ -436,7 +436,7 @@ #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 */ diff --git a/linux/sh64/syscallent.h b/linux/sh64/syscallent.h index 75617cd..ea01878 100644 --- a/linux/sh64/syscallent.h +++ b/linux/sh64/syscallent.h @@ -432,7 +432,7 @@ #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 */ diff --git a/linux/sparc/syscallent.h b/linux/sparc/syscallent.h index 62e0bcc..23269e0 100644 --- a/linux/sparc/syscallent.h +++ b/linux/sparc/syscallent.h @@ -354,7 +354,7 @@ #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 */ @@ -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; } |