Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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: Harald Welte <laforge@gnumonks.org>
[ Fixed up types and added symbol exports ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Harald Welte and committed by
Linus Torvalds
46113830 094804c5

+44 -3
+9 -3
drivers/usb/core/devio.c
··· 30 30 * Revision history 31 31 * 22.12.1999 0.1 Initial release (split from proc_usb.c) 32 32 * 04.01.2000 0.2 Turned into its own filesystem 33 + * 30.09.2005 0.3 Fix user-triggerable oops in async URB delivery 34 + * (CAN-2005-3055) 33 35 */ 34 36 35 37 /*****************************************************************************/ ··· 60 58 struct async { 61 59 struct list_head asynclist; 62 60 struct dev_state *ps; 63 - struct task_struct *task; 61 + pid_t pid; 62 + uid_t uid, euid; 64 63 unsigned int signr; 65 64 unsigned int ifnum; 66 65 void __user *userbuffer; ··· 293 290 sinfo.si_errno = as->urb->status; 294 291 sinfo.si_code = SI_ASYNCIO; 295 292 sinfo.si_addr = as->userurb; 296 - send_sig_info(as->signr, &sinfo, as->task); 293 + kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid, 294 + as->euid); 297 295 } 298 296 wake_up(&ps->wait); 299 297 } ··· 992 988 as->userbuffer = NULL; 993 989 as->signr = uurb->signr; 994 990 as->ifnum = ifnum; 995 - as->task = current; 991 + as->pid = current->pid; 992 + as->uid = current->uid; 993 + as->euid = current->euid; 996 994 if (!(uurb->endpoint & USB_DIR_IN)) { 997 995 if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { 998 996 free_async(as);
+1
include/linux/sched.h
··· 1018 1018 extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp); 1019 1019 extern int kill_pg_info(int, struct siginfo *, pid_t); 1020 1020 extern int kill_proc_info(int, struct siginfo *, pid_t); 1021 + extern int kill_proc_info_as_uid(int, struct siginfo *, pid_t, uid_t, uid_t); 1021 1022 extern void do_notify_parent(struct task_struct *, int); 1022 1023 extern void force_sig(int, struct task_struct *); 1023 1024 extern void force_sig_specific(int, struct task_struct *);
+34
kernel/signal.c
··· 1193 1193 return error; 1194 1194 } 1195 1195 1196 + /* like kill_proc_info(), but doesn't use uid/euid of "current" */ 1197 + int kill_proc_info_as_uid(int sig, struct siginfo *info, pid_t pid, 1198 + uid_t uid, uid_t euid) 1199 + { 1200 + int ret = -EINVAL; 1201 + struct task_struct *p; 1202 + 1203 + if (!valid_signal(sig)) 1204 + return ret; 1205 + 1206 + read_lock(&tasklist_lock); 1207 + p = find_task_by_pid(pid); 1208 + if (!p) { 1209 + ret = -ESRCH; 1210 + goto out_unlock; 1211 + } 1212 + if ((!info || ((unsigned long)info != 1 && 1213 + (unsigned long)info != 2 && SI_FROMUSER(info))) 1214 + && (euid != p->suid) && (euid != p->uid) 1215 + && (uid != p->suid) && (uid != p->uid)) { 1216 + ret = -EPERM; 1217 + goto out_unlock; 1218 + } 1219 + if (sig && p->sighand) { 1220 + unsigned long flags; 1221 + spin_lock_irqsave(&p->sighand->siglock, flags); 1222 + ret = __group_send_sig_info(sig, info, p); 1223 + spin_unlock_irqrestore(&p->sighand->siglock, flags); 1224 + } 1225 + out_unlock: 1226 + read_unlock(&tasklist_lock); 1227 + return ret; 1228 + } 1229 + EXPORT_SYMBOL_GPL(kill_proc_info_as_uid); 1196 1230 1197 1231 /* 1198 1232 * kill_something_info() interprets pid in interesting ways just like kill(2).