That's what I thought about if the pad trick was not possible.
Now I thing we have severe security holes all around and I wonder whether it
would not be easier to plug them at a few places.
The issue is that the usual way of sending a siginfo is (example from
signal.c):
struct siginfo info;
...
info.si_signo = sig;
info.si_errno = 0;
info.si_pid = tsk->pid;
info.si_utime = tsk->times.tms_utime;
info.si_stime = tsk->times.tms_stime;
info.si_code = why;
...
send_sig_info(sig, &info, tsk);
but no trace of memset(&info, 0, sizeof(siginfo_t));
and from that time on, the siginfo_t is always taken as whole unit and
finally copied to userland either in rt_sigtimedwait or in arch's
setup_rt_frame, so userland will get kernel stack content, which is in my
eyes a big NO NO.
I see two ways of solving this:
either add memsets everywhere (now there are not many places where kernel
sends signal with info, but once info can be carried with a non-rt signal, every
arch's trap.c or fault.c will contain a bunch of them), or limit what is
copied into userland. The latter would avoid the memsets and copy_to_user
(X, X, 128) for SI_FROMKERNEL and si_code 0 codes but would allow some
branches in the path.
I'd think about siginfo.h defining
(of course, each architecture is free to optimize this)
extern inline int __copy_siginfo_to_user (siginfo_t *kinfo, siginfo_t *info)
{
int err;
if (kinfo->si_code < 0)
err = __copy_to_user (info, kinfo, sizeof (siginfo_t));
else {
err = __put_user (kinfo->si_signo, &info->si_signo);
err |= __put_user (kinfo->si_errno, &info->si_errno);
err |= __put_user (kinfo->si_code, &info->si_code);
if (kinfo->si_code == SI_USER) {
err |= __put_user (kinfo->si_pid, &info->si_pid);
err |= __put_user (kinfo->si_uid, &info->si_uid);
} else switch (kinfo->si_signo) {
case SIGPOLL:
err |= __put_user (kinfo->si_band, &info->si_band);
err |= __put_user (kinfo->si_fd, &info->si_fd);
break;
case SIGCHLD:
err |= __put_user (kinfo->si_pid, &info->si_pid);
err |= __put_user (kinfo->si_uid, &info->si_uid);
err |= __put_user (kinfo->si_status, &info->si_status);
err |= __put_user (kinfo->si_utime, &info->si_utime);
err |= __put_user (kinfo->si_stime, &info->si_stime);
break;
case SIGSEGV:
case SIGILL:
case SIGFPE:
case SIGBUS:
#ifdef SIGEMT
case SIGEMT:
#endif
err |= __put_user (kinfo->si_addr, &info->si_addr);
err |= __put_user (kinfo->si_trapno, &info->si_trapno);
break;
default:
err |= __put_user (kinfo->si_pid, &info->si_pid);
err |= __put_user (kinfo->si_uid, &info->si_uid);
break;
}
}
return err;
}
which could be used by sys_rt_sigtimedwait and arch's setup_rt_frame.
Code like POSIX timers would still have to memset the whole siginfo_t before
sending it. And at least si_errno should be initialized everywhere.
Cheers,
Jakub
___________________________________________________________________
Jakub Jelinek | jj@sunsite.mff.cuni.cz | http://sunsite.mff.cuni.cz
Administrator of SunSITE Czech Republic, MFF, Charles University
___________________________________________________________________
UltraLinux | http://ultra.linux.cz/ | http://ultra.penguin.cz/
Linux version 2.2.10 on a sparc64 machine (1343.49 BogoMips)
___________________________________________________________________
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/