Commit 46113830 authored by Harald Welte's avatar Harald Welte Committed by Linus Torvalds

[PATCH] Fix signal sending in usbdevio on async URB completion

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.

In fact, there are three 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.

3) Even if the process is perfectly fine, permissions may have changed,
   and we can no longer send it a signal.

So what we do instead, is to save the PID and uid's of the process, and
introduce a new kill_proc_info_as_uid() function.
Signed-off-by: default avatarHarald Welte <laforge@gnumonks.org>
[ Fixed up types and added symbol exports ]
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 094804c5
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
* Revision history * Revision history
* 22.12.1999 0.1 Initial release (split from proc_usb.c) * 22.12.1999 0.1 Initial release (split from proc_usb.c)
* 04.01.2000 0.2 Turned into its own filesystem * 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; ...@@ -58,7 +60,8 @@ static struct class *usb_device_class;
struct async { struct async {
struct list_head asynclist; struct list_head asynclist;
struct dev_state *ps; struct dev_state *ps;
struct task_struct *task; pid_t pid;
uid_t uid, euid;
unsigned int signr; unsigned int signr;
unsigned int ifnum; unsigned int ifnum;
void __user *userbuffer; void __user *userbuffer;
...@@ -290,7 +293,8 @@ static void async_completed(struct urb *urb, struct pt_regs *regs) ...@@ -290,7 +293,8 @@ static void async_completed(struct urb *urb, struct pt_regs *regs)
sinfo.si_errno = as->urb->status; sinfo.si_errno = as->urb->status;
sinfo.si_code = SI_ASYNCIO; sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = as->userurb; 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); wake_up(&ps->wait);
} }
...@@ -988,7 +992,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ...@@ -988,7 +992,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->userbuffer = NULL; as->userbuffer = NULL;
as->signr = uurb->signr; as->signr = uurb->signr;
as->ifnum = ifnum; 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 (!(uurb->endpoint & USB_DIR_IN)) {
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) {
free_async(as); free_async(as);
......
...@@ -1018,6 +1018,7 @@ extern int force_sig_info(int, struct siginfo *, struct task_struct *); ...@@ -1018,6 +1018,7 @@ extern int force_sig_info(int, struct siginfo *, struct task_struct *);
extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp); 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_pg_info(int, struct siginfo *, pid_t);
extern int kill_proc_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 do_notify_parent(struct task_struct *, int);
extern void force_sig(int, struct task_struct *); extern void force_sig(int, struct task_struct *);
extern void force_sig_specific(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *);
......
...@@ -1193,6 +1193,40 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid) ...@@ -1193,6 +1193,40 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid)
return error; return error;
} }
/* like kill_proc_info(), but doesn't use uid/euid of "current" */
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;
}
EXPORT_SYMBOL_GPL(kill_proc_info_as_uid);
/* /*
* kill_something_info() interprets pid in interesting ways just like kill(2). * kill_something_info() interprets pid in interesting ways just like kill(2).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment