diff options
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 73 |
1 files changed, 47 insertions, 26 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 1814e68e4de3..dbd7fe073c55 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -159,6 +159,10 @@ void recalc_sigpending(void) | |||
159 | 159 | ||
160 | /* Given the mask, find the first available signal that should be serviced. */ | 160 | /* Given the mask, find the first available signal that should be serviced. */ |
161 | 161 | ||
162 | #define SYNCHRONOUS_MASK \ | ||
163 | (sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \ | ||
164 | sigmask(SIGTRAP) | sigmask(SIGFPE)) | ||
165 | |||
162 | int next_signal(struct sigpending *pending, sigset_t *mask) | 166 | int next_signal(struct sigpending *pending, sigset_t *mask) |
163 | { | 167 | { |
164 | unsigned long i, *s, *m, x; | 168 | unsigned long i, *s, *m, x; |
@@ -166,26 +170,39 @@ int next_signal(struct sigpending *pending, sigset_t *mask) | |||
166 | 170 | ||
167 | s = pending->signal.sig; | 171 | s = pending->signal.sig; |
168 | m = mask->sig; | 172 | m = mask->sig; |
173 | |||
174 | /* | ||
175 | * Handle the first word specially: it contains the | ||
176 | * synchronous signals that need to be dequeued first. | ||
177 | */ | ||
178 | x = *s &~ *m; | ||
179 | if (x) { | ||
180 | if (x & SYNCHRONOUS_MASK) | ||
181 | x &= SYNCHRONOUS_MASK; | ||
182 | sig = ffz(~x) + 1; | ||
183 | return sig; | ||
184 | } | ||
185 | |||
169 | switch (_NSIG_WORDS) { | 186 | switch (_NSIG_WORDS) { |
170 | default: | 187 | default: |
171 | for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m) | 188 | for (i = 1; i < _NSIG_WORDS; ++i) { |
172 | if ((x = *s &~ *m) != 0) { | 189 | x = *++s &~ *++m; |
173 | sig = ffz(~x) + i*_NSIG_BPW + 1; | 190 | if (!x) |
174 | break; | 191 | continue; |
175 | } | 192 | sig = ffz(~x) + i*_NSIG_BPW + 1; |
193 | break; | ||
194 | } | ||
176 | break; | 195 | break; |
177 | 196 | ||
178 | case 2: if ((x = s[0] &~ m[0]) != 0) | 197 | case 2: |
179 | sig = 1; | 198 | x = s[1] &~ m[1]; |
180 | else if ((x = s[1] &~ m[1]) != 0) | 199 | if (!x) |
181 | sig = _NSIG_BPW + 1; | ||
182 | else | ||
183 | break; | 200 | break; |
184 | sig += ffz(~x); | 201 | sig = ffz(~x) + _NSIG_BPW + 1; |
185 | break; | 202 | break; |
186 | 203 | ||
187 | case 1: if ((x = *s &~ *m) != 0) | 204 | case 1: |
188 | sig = ffz(~x) + 1; | 205 | /* Nothing to do */ |
189 | break; | 206 | break; |
190 | } | 207 | } |
191 | 208 | ||
@@ -218,17 +235,17 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi | |||
218 | struct user_struct *user; | 235 | struct user_struct *user; |
219 | 236 | ||
220 | /* | 237 | /* |
221 | * We won't get problems with the target's UID changing under us | 238 | * Protect access to @t credentials. This can go away when all |
222 | * because changing it requires RCU be used, and if t != current, the | 239 | * callers hold rcu read lock. |
223 | * caller must be holding the RCU readlock (by way of a spinlock) and | ||
224 | * we use RCU protection here | ||
225 | */ | 240 | */ |
241 | rcu_read_lock(); | ||
226 | user = get_uid(__task_cred(t)->user); | 242 | user = get_uid(__task_cred(t)->user); |
227 | atomic_inc(&user->sigpending); | 243 | atomic_inc(&user->sigpending); |
244 | rcu_read_unlock(); | ||
228 | 245 | ||
229 | if (override_rlimit || | 246 | if (override_rlimit || |
230 | atomic_read(&user->sigpending) <= | 247 | atomic_read(&user->sigpending) <= |
231 | t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) { | 248 | task_rlimit(t, RLIMIT_SIGPENDING)) { |
232 | q = kmem_cache_alloc(sigqueue_cachep, flags); | 249 | q = kmem_cache_alloc(sigqueue_cachep, flags); |
233 | } else { | 250 | } else { |
234 | print_dropped_signal(sig); | 251 | print_dropped_signal(sig); |
@@ -979,7 +996,8 @@ static void print_fatal_signal(struct pt_regs *regs, int signr) | |||
979 | for (i = 0; i < 16; i++) { | 996 | for (i = 0; i < 16; i++) { |
980 | unsigned char insn; | 997 | unsigned char insn; |
981 | 998 | ||
982 | __get_user(insn, (unsigned char *)(regs->ip + i)); | 999 | if (get_user(insn, (unsigned char *)(regs->ip + i))) |
1000 | break; | ||
983 | printk("%02x ", insn); | 1001 | printk("%02x ", insn); |
984 | } | 1002 | } |
985 | } | 1003 | } |
@@ -1179,11 +1197,12 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, | |||
1179 | int ret = -EINVAL; | 1197 | int ret = -EINVAL; |
1180 | struct task_struct *p; | 1198 | struct task_struct *p; |
1181 | const struct cred *pcred; | 1199 | const struct cred *pcred; |
1200 | unsigned long flags; | ||
1182 | 1201 | ||
1183 | if (!valid_signal(sig)) | 1202 | if (!valid_signal(sig)) |
1184 | return ret; | 1203 | return ret; |
1185 | 1204 | ||
1186 | read_lock(&tasklist_lock); | 1205 | rcu_read_lock(); |
1187 | p = pid_task(pid, PIDTYPE_PID); | 1206 | p = pid_task(pid, PIDTYPE_PID); |
1188 | if (!p) { | 1207 | if (!p) { |
1189 | ret = -ESRCH; | 1208 | ret = -ESRCH; |
@@ -1199,14 +1218,16 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, | |||
1199 | ret = security_task_kill(p, info, sig, secid); | 1218 | ret = security_task_kill(p, info, sig, secid); |
1200 | if (ret) | 1219 | if (ret) |
1201 | goto out_unlock; | 1220 | goto out_unlock; |
1202 | if (sig && p->sighand) { | 1221 | |
1203 | unsigned long flags; | 1222 | if (sig) { |
1204 | spin_lock_irqsave(&p->sighand->siglock, flags); | 1223 | if (lock_task_sighand(p, &flags)) { |
1205 | ret = __send_signal(sig, info, p, 1, 0); | 1224 | ret = __send_signal(sig, info, p, 1, 0); |
1206 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 1225 | unlock_task_sighand(p, &flags); |
1226 | } else | ||
1227 | ret = -ESRCH; | ||
1207 | } | 1228 | } |
1208 | out_unlock: | 1229 | out_unlock: |
1209 | read_unlock(&tasklist_lock); | 1230 | rcu_read_unlock(); |
1210 | return ret; | 1231 | return ret; |
1211 | } | 1232 | } |
1212 | EXPORT_SYMBOL_GPL(kill_pid_info_as_uid); | 1233 | EXPORT_SYMBOL_GPL(kill_pid_info_as_uid); |