summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOliver Neukum <oliver@neukum.org>2009-07-08 19:09:23 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2009-07-12 15:16:40 -0700
commit516a1a07f0219d6672fb6b8e49fb9d5d533c2e89 (patch)
tree538650864da3032195afa77ea808d11ae78e7c4e
parent7bae0a070db4bc2761dd9515f450cdfa3f3f248c (diff)
downloadlinux-sh-516a1a07f0219d6672fb6b8e49fb9d5d533c2e89.tar.gz
USB: fix race leading to a write after kfree in usbfs
this fixes a race between async_completed() and proc_reapurbnonblock(). CPU A CPU B spin_lock(&ps->lock); list_move_tail(&as->asynclist, &ps->async_completed); spin_unlock(&ps->lock); if (!(as = async_getcompleted(ps))) return -EAGAIN; return processcompl(as, (void __user * __user *)arg); processcompl() calls free_async() which calls kfree(as) as->status = urb->status; if (as->signr) { sinfo.si_signo = as->signr; sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid, as->euid, as->secid); } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb, as->userurb); write after kfree Signed-off-by: Oliver Neukum <oliver@neukum.org>
-rw-r--r--drivers/usb/core/devio.c21
1 files changed, 17 insertions, 4 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 706f18156af8..46ca2af5ef1c 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -325,21 +325,34 @@ static void async_completed(struct urb *urb)
struct async *as = urb->context;
struct dev_state *ps = as->ps;
struct siginfo sinfo;
+ struct pid *pid = NULL;
+ uid_t uid = 0;
+ uid_t euid = 0;
+ u32 secid = 0;
+ int signr;
spin_lock(&ps->lock);
list_move_tail(&as->asynclist, &ps->async_completed);
- spin_unlock(&ps->lock);
as->status = urb->status;
- if (as->signr) {
+ signr = as->signr;
+ if (signr) {
sinfo.si_signo = as->signr;
sinfo.si_errno = as->status;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = as->userurb;
- kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
- as->euid, as->secid);
+ pid = as->pid;
+ uid = as->uid;
+ euid = as->euid;
+ secid = as->secid;
}
snoop(&urb->dev->dev, "urb complete\n");
snoop_urb(urb, as->userurb);
+ spin_unlock(&ps->lock);
+
+ if (signr)
+ kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid,
+ euid, secid);
+
wake_up(&ps->wait);
}