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>
This commit is contained in:
Denys Vlasenko 2011-08-19 17:41:28 +02:00
parent 9aa97968ed
commit d9560c1080
12 changed files with 75 additions and 20 deletions

27
defs.h
View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}