diff options
| -rw-r--r-- | include/linux/sched.h | 9 | ||||
| -rw-r--r-- | kernel/signal.c | 38 | 
2 files changed, 33 insertions, 14 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index bbcfc873bd98..ca1fd31aae97 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h  | |||
| @@ -1225,6 +1225,15 @@ static inline void task_unlock(struct task_struct *p) | |||
| 1225 | spin_unlock(&p->alloc_lock); | 1225 | spin_unlock(&p->alloc_lock); | 
| 1226 | } | 1226 | } | 
| 1227 | 1227 | ||
| 1228 | extern struct sighand_struct *lock_task_sighand(struct task_struct *tsk, | ||
| 1229 | unsigned long *flags); | ||
| 1230 | |||
| 1231 | static inline void unlock_task_sighand(struct task_struct *tsk, | ||
| 1232 | unsigned long *flags) | ||
| 1233 | { | ||
| 1234 | spin_unlock_irqrestore(&tsk->sighand->siglock, *flags); | ||
| 1235 | } | ||
| 1236 | |||
| 1228 | #ifndef __HAVE_THREAD_FUNCTIONS | 1237 | #ifndef __HAVE_THREAD_FUNCTIONS | 
| 1229 | 1238 | ||
| 1230 | #define task_thread_info(task) (task)->thread_info | 1239 | #define task_thread_info(task) (task)->thread_info | 
diff --git a/kernel/signal.c b/kernel/signal.c index b0b1ca9daa33..819fa49aa70a 100644 --- a/kernel/signal.c +++ b/kernel/signal.c  | |||
| @@ -1120,27 +1120,37 @@ void zap_other_threads(struct task_struct *p) | |||
| 1120 | /* | 1120 | /* | 
| 1121 | * Must be called under rcu_read_lock() or with tasklist_lock read-held. | 1121 | * Must be called under rcu_read_lock() or with tasklist_lock read-held. | 
| 1122 | */ | 1122 | */ | 
| 1123 | struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long *flags) | ||
| 1124 | { | ||
| 1125 | struct sighand_struct *sighand; | ||
| 1126 | |||
| 1127 | for (;;) { | ||
| 1128 | sighand = rcu_dereference(tsk->sighand); | ||
| 1129 | if (unlikely(sighand == NULL)) | ||
| 1130 | break; | ||
| 1131 | |||
| 1132 | spin_lock_irqsave(&sighand->siglock, *flags); | ||
| 1133 | if (likely(sighand == tsk->sighand)) | ||
| 1134 | break; | ||
| 1135 | spin_unlock_irqrestore(&sighand->siglock, *flags); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | return sighand; | ||
| 1139 | } | ||
| 1140 | |||
| 1123 | int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) | 1141 | int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) | 
| 1124 | { | 1142 | { | 
| 1125 | unsigned long flags; | 1143 | unsigned long flags; | 
| 1126 | struct sighand_struct *sp; | ||
| 1127 | int ret; | 1144 | int ret; | 
| 1128 | 1145 | ||
| 1129 | retry: | ||
| 1130 | ret = check_kill_permission(sig, info, p); | 1146 | ret = check_kill_permission(sig, info, p); | 
| 1131 | if (!ret && sig && (sp = rcu_dereference(p->sighand))) { | 1147 | |
| 1132 | spin_lock_irqsave(&sp->siglock, flags); | 1148 | if (!ret && sig) { | 
| 1133 | if (p->sighand != sp) { | 1149 | ret = -ESRCH; | 
| 1134 | spin_unlock_irqrestore(&sp->siglock, flags); | 1150 | if (lock_task_sighand(p, &flags)) { | 
| 1135 | goto retry; | 1151 | ret = __group_send_sig_info(sig, info, p); | 
| 1136 | } | 1152 | unlock_task_sighand(p, &flags); | 
| 1137 | if ((atomic_read(&sp->count) == 0) || | ||
| 1138 | (atomic_read(&p->usage) == 0)) { | ||
| 1139 | spin_unlock_irqrestore(&sp->siglock, flags); | ||
| 1140 | return -ESRCH; | ||
| 1141 | } | 1153 | } | 
| 1142 | ret = __group_send_sig_info(sig, info, p); | ||
| 1143 | spin_unlock_irqrestore(&sp->siglock, flags); | ||
| 1144 | } | 1154 | } | 
| 1145 | 1155 | ||
| 1146 | return ret; | 1156 | return ret; | 
