[PATCH] set saner MAX_ARGS
Denys Vlasenko
dvlasenk at redhat.com
Thu Aug 18 10:47:38 UTC 2011
On Thu, 2011-08-18 at 12:23 +0200, Denys Vlasenko wrote:
> 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!
>
> I propose to change 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 MAX_ARGS == 5 should be enough for saving signal masks.
>
> The proposed patch is below.
>
> Alternative solution is to make sys_sigreturn print mask
> on entry, not on exit. What is the reson it doesn't do that now?
>
> Please review.
Sorry, attached older path. Newer one is below.
BTW, this patch reduces bss by 6k:
text data bss dec hex filename
253464 652 55508 309624 4b978 strace.old
252760 652 49364 302776 49eb8 strace
--
vda
diff -d -urpN strace.5/defs.h strace.6/defs.h
--- strace.5/defs.h 2011-08-18 11:57:30.512416447 +0200
+++ strace.6/defs.h 2011-08-18 11:46:56.349540479 +0200
@@ -64,7 +64,7 @@
#define DEFAULT_ACOLUMN 40 /* default alignment column for results */
#endif
#ifndef MAX_ARGS
-# ifdef HPPA
+# if defined HPPA || defined X86_64 || defined I386
# define MAX_ARGS 6 /* maximum number of args to a syscall */
# else
/* Way too big. Switch your arch to saner size after you tested that it works */
diff -d -urpN strace.5/signal.c strace.6/signal.c
--- strace.5/signal.c 2011-08-18 11:57:57.596606238 +0200
+++ strace.6/signal.c 2011-08-18 12:14:31.683278152 +0200
@@ -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;
}
More information about the Strace-devel
mailing list