aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/seccomp.c97
1 files changed, 67 insertions, 30 deletions
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index d2596136b0d1..58125160417c 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -18,6 +18,7 @@
18#include <linux/compat.h> 18#include <linux/compat.h>
19#include <linux/sched.h> 19#include <linux/sched.h>
20#include <linux/seccomp.h> 20#include <linux/seccomp.h>
21#include <linux/slab.h>
21#include <linux/syscalls.h> 22#include <linux/syscalls.h>
22 23
23/* #define SECCOMP_DEBUG 1 */ 24/* #define SECCOMP_DEBUG 1 */
@@ -27,7 +28,6 @@
27#include <linux/filter.h> 28#include <linux/filter.h>
28#include <linux/ptrace.h> 29#include <linux/ptrace.h>
29#include <linux/security.h> 30#include <linux/security.h>
30#include <linux/slab.h>
31#include <linux/tracehook.h> 31#include <linux/tracehook.h>
32#include <linux/uaccess.h> 32#include <linux/uaccess.h>
33 33
@@ -213,27 +213,23 @@ static inline void seccomp_assign_mode(unsigned long seccomp_mode)
213 213
214#ifdef CONFIG_SECCOMP_FILTER 214#ifdef CONFIG_SECCOMP_FILTER
215/** 215/**
216 * seccomp_attach_filter: Attaches a seccomp filter to current. 216 * seccomp_prepare_filter: Prepares a seccomp filter for use.
217 * @fprog: BPF program to install 217 * @fprog: BPF program to install
218 * 218 *
219 * Returns 0 on success or an errno on failure. 219 * Returns filter on success or an ERR_PTR on failure.
220 */ 220 */
221static long seccomp_attach_filter(struct sock_fprog *fprog) 221static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
222{ 222{
223 struct seccomp_filter *filter; 223 struct seccomp_filter *filter;
224 unsigned long fp_size = fprog->len * sizeof(struct sock_filter); 224 unsigned long fp_size;
225 unsigned long total_insns = fprog->len;
226 struct sock_filter *fp; 225 struct sock_filter *fp;
227 int new_len; 226 int new_len;
228 long ret; 227 long ret;
229 228
230 if (fprog->len == 0 || fprog->len > BPF_MAXINSNS) 229 if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
231 return -EINVAL; 230 return ERR_PTR(-EINVAL);
232 231 BUG_ON(INT_MAX / fprog->len < sizeof(struct sock_filter));
233 for (filter = current->seccomp.filter; filter; filter = filter->prev) 232 fp_size = fprog->len * sizeof(struct sock_filter);
234 total_insns += filter->prog->len + 4; /* include a 4 instr penalty */
235 if (total_insns > MAX_INSNS_PER_PATH)
236 return -ENOMEM;
237 233
238 /* 234 /*
239 * Installing a seccomp filter requires that the task has 235 * Installing a seccomp filter requires that the task has
@@ -244,11 +240,11 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
244 if (!task_no_new_privs(current) && 240 if (!task_no_new_privs(current) &&
245 security_capable_noaudit(current_cred(), current_user_ns(), 241 security_capable_noaudit(current_cred(), current_user_ns(),
246 CAP_SYS_ADMIN) != 0) 242 CAP_SYS_ADMIN) != 0)
247 return -EACCES; 243 return ERR_PTR(-EACCES);
248 244
249 fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN); 245 fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN);
250 if (!fp) 246 if (!fp)
251 return -ENOMEM; 247 return ERR_PTR(-ENOMEM);
252 248
253 /* Copy the instructions from fprog. */ 249 /* Copy the instructions from fprog. */
254 ret = -EFAULT; 250 ret = -EFAULT;
@@ -292,13 +288,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
292 288
293 sk_filter_select_runtime(filter->prog); 289 sk_filter_select_runtime(filter->prog);
294 290
295 /* 291 return filter;
296 * If there is an existing filter, make it the prev and don't drop its
297 * task reference.
298 */
299 filter->prev = current->seccomp.filter;
300 current->seccomp.filter = filter;
301 return 0;
302 292
303free_filter_prog: 293free_filter_prog:
304 kfree(filter->prog); 294 kfree(filter->prog);
@@ -306,19 +296,20 @@ free_filter:
306 kfree(filter); 296 kfree(filter);
307free_prog: 297free_prog:
308 kfree(fp); 298 kfree(fp);
309 return ret; 299 return ERR_PTR(ret);
310} 300}
311 301
312/** 302/**
313 * seccomp_attach_user_filter - attaches a user-supplied sock_fprog 303 * seccomp_prepare_user_filter - prepares a user-supplied sock_fprog
314 * @user_filter: pointer to the user data containing a sock_fprog. 304 * @user_filter: pointer to the user data containing a sock_fprog.
315 * 305 *
316 * Returns 0 on success and non-zero otherwise. 306 * Returns 0 on success and non-zero otherwise.
317 */ 307 */
318static long seccomp_attach_user_filter(const char __user *user_filter) 308static struct seccomp_filter *
309seccomp_prepare_user_filter(const char __user *user_filter)
319{ 310{
320 struct sock_fprog fprog; 311 struct sock_fprog fprog;
321 long ret = -EFAULT; 312 struct seccomp_filter *filter = ERR_PTR(-EFAULT);
322 313
323#ifdef CONFIG_COMPAT 314#ifdef CONFIG_COMPAT
324 if (is_compat_task()) { 315 if (is_compat_task()) {
@@ -331,9 +322,39 @@ static long seccomp_attach_user_filter(const char __user *user_filter)
331#endif 322#endif
332 if (copy_from_user(&fprog, user_filter, sizeof(fprog))) 323 if (copy_from_user(&fprog, user_filter, sizeof(fprog)))
333 goto out; 324 goto out;
334 ret = seccomp_attach_filter(&fprog); 325 filter = seccomp_prepare_filter(&fprog);
335out: 326out:
336 return ret; 327 return filter;
328}
329
330/**
331 * seccomp_attach_filter: validate and attach filter
332 * @flags: flags to change filter behavior
333 * @filter: seccomp filter to add to the current process
334 *
335 * Returns 0 on success, -ve on error.
336 */
337static long seccomp_attach_filter(unsigned int flags,
338 struct seccomp_filter *filter)
339{
340 unsigned long total_insns;
341 struct seccomp_filter *walker;
342
343 /* Validate resulting filter length. */
344 total_insns = filter->prog->len;
345 for (walker = current->seccomp.filter; walker; walker = walker->prev)
346 total_insns += walker->prog->len + 4; /* 4 instr penalty */
347 if (total_insns > MAX_INSNS_PER_PATH)
348 return -ENOMEM;
349
350 /*
351 * If there is an existing filter, make it the prev and don't drop its
352 * task reference.
353 */
354 filter->prev = current->seccomp.filter;
355 current->seccomp.filter = filter;
356
357 return 0;
337} 358}
338 359
339/* get_seccomp_filter - increments the reference count of the filter on @tsk */ 360/* get_seccomp_filter - increments the reference count of the filter on @tsk */
@@ -346,6 +367,14 @@ void get_seccomp_filter(struct task_struct *tsk)
346 atomic_inc(&orig->usage); 367 atomic_inc(&orig->usage);
347} 368}
348 369
370static inline void seccomp_filter_free(struct seccomp_filter *filter)
371{
372 if (filter) {
373 sk_filter_free(filter->prog);
374 kfree(filter);
375 }
376}
377
349/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */ 378/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
350void put_seccomp_filter(struct task_struct *tsk) 379void put_seccomp_filter(struct task_struct *tsk)
351{ 380{
@@ -354,8 +383,7 @@ void put_seccomp_filter(struct task_struct *tsk)
354 while (orig && atomic_dec_and_test(&orig->usage)) { 383 while (orig && atomic_dec_and_test(&orig->usage)) {
355 struct seccomp_filter *freeme = orig; 384 struct seccomp_filter *freeme = orig;
356 orig = orig->prev; 385 orig = orig->prev;
357 sk_filter_free(freeme->prog); 386 seccomp_filter_free(freeme);
358 kfree(freeme);
359 } 387 }
360} 388}
361 389
@@ -533,21 +561,30 @@ static long seccomp_set_mode_filter(unsigned int flags,
533 const char __user *filter) 561 const char __user *filter)
534{ 562{
535 const unsigned long seccomp_mode = SECCOMP_MODE_FILTER; 563 const unsigned long seccomp_mode = SECCOMP_MODE_FILTER;
564 struct seccomp_filter *prepared = NULL;
536 long ret = -EINVAL; 565 long ret = -EINVAL;
537 566
538 /* Validate flags. */ 567 /* Validate flags. */
539 if (flags != 0) 568 if (flags != 0)
540 goto out; 569 goto out;
541 570
571 /* Prepare the new filter before holding any locks. */
572 prepared = seccomp_prepare_user_filter(filter);
573 if (IS_ERR(prepared))
574 return PTR_ERR(prepared);
575
542 if (!seccomp_may_assign_mode(seccomp_mode)) 576 if (!seccomp_may_assign_mode(seccomp_mode))
543 goto out; 577 goto out;
544 578
545 ret = seccomp_attach_user_filter(filter); 579 ret = seccomp_attach_filter(flags, prepared);
546 if (ret) 580 if (ret)
547 goto out; 581 goto out;
582 /* Do not free the successfully attached filter. */
583 prepared = NULL;
548 584
549 seccomp_assign_mode(seccomp_mode); 585 seccomp_assign_mode(seccomp_mode);
550out: 586out:
587 seccomp_filter_free(prepared);
551 return ret; 588 return ret;
552} 589}
553#else 590#else