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>

authored by Oliver Neukum and committed by Greg Kroah-Hartman 516a1a07 7bae0a07

+17 -4
+17 -4
drivers/usb/core/devio.c
··· 325 325 struct async *as = urb->context; 326 326 struct dev_state *ps = as->ps; 327 327 struct siginfo sinfo; 328 + struct pid *pid = NULL; 329 + uid_t uid = 0; 330 + uid_t euid = 0; 331 + u32 secid = 0; 332 + int signr; 328 333 329 334 spin_lock(&ps->lock); 330 335 list_move_tail(&as->asynclist, &ps->async_completed); 331 - spin_unlock(&ps->lock); 332 336 as->status = urb->status; 333 - if (as->signr) { 337 + signr = as->signr; 338 + if (signr) { 334 339 sinfo.si_signo = as->signr; 335 340 sinfo.si_errno = as->status; 336 341 sinfo.si_code = SI_ASYNCIO; 337 342 sinfo.si_addr = as->userurb; 338 - kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid, 339 - as->euid, as->secid); 343 + pid = as->pid; 344 + uid = as->uid; 345 + euid = as->euid; 346 + secid = as->secid; 340 347 } 341 348 snoop(&urb->dev->dev, "urb complete\n"); 342 349 snoop_urb(urb, as->userurb); 350 + spin_unlock(&ps->lock); 351 + 352 + if (signr) 353 + kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, 354 + euid, secid); 355 + 343 356 wake_up(&ps->wait); 344 357 } 345 358