On Mon, Oct 10, 2005 at 11:07:45AM -0700, Chris Wright wrote: > * Harald Welte ([email protected]) wrote: > > diff --git a/kernel/signal.c b/kernel/signal.c > > --- a/kernel/signal.c > > +++ b/kernel/signal.c > > @@ -1193,6 +1193,40 @@ kill_proc_info(int sig, struct siginfo * > > return error; > > } > > > > +/* like kill_proc_info(), but doesn't use uid/euid of "current" */ > > Maybe additional comment reminding that you most likely don't want this > interface. > > Also, looks like there's same issue again: Mh, didn't hit that bug since I don't use disconnect signals. But it looks like it has the same issue. Please consider the patch below, it 1) changes pid_t to uid_t 2) exports the symbol 3) adresses the same task_struct referencing issue for disconnect signals I hope this now finally is the last take ;) [USBDEVIO] Fix Oops in usbdevio async URB completion (CAN-2005-3055) If a process issues an URB from userspace and (starts to) terminate before the URB comes back, we run into the issue described above. This is because the urb saves a pointer to "current" when it is posted to the device, but there's no guarantee that this pointer is still valid afterwards. This basically means that any userspace process with permissions to any arbitrary USB device can Ooops any kernel.(!) In fact, there are two separate issues: 1) the pointer to "current" can become invalid, since the task could be completely gone when the URB completion comes back from the device. 2) Even if the saved task pointer is still pointing to a valid task_struct, task_struct->sighand could have gone meanwhile. Therefore, the USB async URB completion handler needs to reliably check whether task_struct->sighand is still valid or not. In order to prevent a race with __exit_sighand(), it needs to grab a read_lock(&tasklist_lock). This strategy seems to work, since the send_sig_info() code uses the same protection. However, we now would need to export a __send_sig_info() function, one that expects to be called with read_lock(&tasklist_lock) already held by the caller. So what we do instead, is to save the PID of the process, and introduce a new kill_proc_info_as_uid() function. Yes, pid's can and will wrap, so this is just a short-term fix until usbfs2 will appear. Signed-off-by: Harald Welte <[email protected]> diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -30,6 +30,8 @@ * Revision history * 22.12.1999 0.1 Initial release (split from proc_usb.c) * 04.01.2000 0.2 Turned into its own filesystem + * 30.09.2005 0.3 Fix user-triggerable oops in async URB delivery + * (CAN-2005-3055) */ /*****************************************************************************/ @@ -58,7 +60,8 @@ static struct class *usb_device_class; struct async { struct list_head asynclist; struct dev_state *ps; - struct task_struct *task; + pid_t pid; + uid_t uid, euid; unsigned int signr; unsigned int ifnum; void __user *userbuffer; @@ -290,7 +293,8 @@ static void async_completed(struct urb * sinfo.si_errno = as->urb->status; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; - send_sig_info(as->signr, &sinfo, as->task); + kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid, + as->euid); } wake_up(&ps->wait); } @@ -525,9 +529,11 @@ static int usbdev_open(struct inode *ino INIT_LIST_HEAD(&ps->async_pending); INIT_LIST_HEAD(&ps->async_completed); init_waitqueue_head(&ps->wait); - ps->discsignr = 0; - ps->disctask = current; - ps->disccontext = NULL; + ps->disc.signr = 0; + ps->disc.pid = current->pid; + ps->disc.uid = current->uid; + ps->disc.euid = current->euid; + ps->disc.context = NULL; ps->ifclaimed = 0; wmb(); list_add_tail(&ps->list, &dev->filelist); @@ -988,7 +994,9 @@ static int proc_do_submiturb(struct dev_ as->userbuffer = NULL; as->signr = uurb->signr; as->ifnum = ifnum; - as->task = current; + as->pid = current->pid; + as->uid = current->uid; + as->euid = current->euid; if (!(uurb->endpoint & USB_DIR_IN)) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { free_async(as); @@ -1203,8 +1211,8 @@ static int proc_disconnectsignal(struct return -EFAULT; if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX)) return -EINVAL; - ps->discsignr = ds.signr; - ps->disccontext = ds.context; + ps->disc.signr = ds.signr; + ps->disc.context = ds.context; return 0; } diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -708,12 +708,14 @@ void usbfs_remove_device(struct usb_devi ds = list_entry(dev->filelist.next, struct dev_state, list); wake_up_all(&ds->wait); list_del_init(&ds->list); - if (ds->discsignr) { + if (ds->disc.signr) { sinfo.si_signo = SIGPIPE; sinfo.si_errno = EPIPE; sinfo.si_code = SI_ASYNCIO; - sinfo.si_addr = ds->disccontext; - send_sig_info(ds->discsignr, &sinfo, ds->disctask); + sinfo.si_addr = ds->disc.context; + kill_proc_info_as_uid(ds->disc.signr, &sinfo, + ds->disc.pid, ds->disc.uid, + ds->disc.euid); } } usbfs_update_special(); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -51,9 +51,12 @@ struct dev_state { struct list_head async_pending; struct list_head async_completed; wait_queue_head_t wait; /* wake up if a request completed */ - unsigned int discsignr; - struct task_struct *disctask; - void __user *disccontext; + struct { + unsigned int signr; + pid_t pid; + uid_t uid, euid; + void __user *context; + } disc; unsigned long ifclaimed; }; diff --git a/include/linux/sched.h b/include/linux/sched.h --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1018,6 +1018,7 @@ extern int force_sig_info(int, struct si extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp); extern int kill_pg_info(int, struct siginfo *, pid_t); extern int kill_proc_info(int, struct siginfo *, pid_t); +extern int kill_proc_info_as_uid(int, struct siginfo *, pid_t, uid_t, uid_t); extern void do_notify_parent(struct task_struct *, int); extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); diff --git a/kernel/signal.c b/kernel/signal.c --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1193,6 +1193,42 @@ kill_proc_info(int sig, struct siginfo * return error; } +/* This is like kill_proc_info(), but doesn't use uid/euid of "current". + * Most likely this function is not what you want, it was added as a special + * case for usbdevio */ +int +kill_proc_info_as_uid(int sig, struct siginfo *info, pid_t pid, + uid_t uid, uid_t euid) +{ + int ret = -EINVAL; + struct task_struct *p; + + if (!valid_signal(sig)) + return ret; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + if (!p) { + ret = -ESRCH; + goto out_unlock; + } + if ((!info || ((unsigned long)info != 1 && + (unsigned long)info != 2 && SI_FROMUSER(info))) + && (euid ^ p->suid) && (euid ^ p->uid) + && (uid ^ p->suid) && (uid ^ p->uid)) { + ret = -EPERM; + goto out_unlock; + } + if (sig && p->sighand) { + unsigned long flags; + spin_lock_irqsave(&p->sighand->siglock, flags); + ret = __group_send_sig_info(sig, info, p); + spin_unlock_irqrestore(&p->sighand->siglock, flags); + } +out_unlock: + read_unlock(&tasklist_lock); + return ret; +} /* * kill_something_info() interprets pid in interesting ways just like kill(2). @@ -1974,6 +2010,7 @@ EXPORT_SYMBOL(flush_signals); EXPORT_SYMBOL(force_sig); EXPORT_SYMBOL(kill_pg); EXPORT_SYMBOL(kill_proc); +EXPORT_SYMBOL_GPL(kill_proc_info_as_uid); EXPORT_SYMBOL(ptrace_notify); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); -- - Harald Welte <[email protected]> http://gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6)
Attachment:
pgpDhbC1bVfPN.pgp
Description: PGP signature
- Follow-Ups:
- References:
- Re: [BUG/PATCH/RFC] Oops while completing async USB via usbdevio
- From: Harald Welte <[email protected]>
- Re: [BUG/PATCH/RFC] Oops while completing async USB via usbdevio
- From: Chris Wright <[email protected]>
- Re: [BUG/PATCH/RFC] Oops while completing async USB via usbdevio
- Prev by Date: Re: [PATCH 2.6.14-rc2-mm2] core remove PageReserved
- Next by Date: Re: [linux-usb-devel] Re: [BUG/PATCH/RFC] Oops while completing async USB via usbdevio
- Previous by thread: Re: [BUG/PATCH/RFC] Oops while completing async USB via usbdevio
- Next by thread: Re: [vendor-sec] Re: [BUG/PATCH/RFC] Oops while completing async USB via usbdevio
- Index(es):