diff options
-rw-r--r-- | include/linux/seccomp.h | 6 | ||||
-rw-r--r-- | kernel/fork.c | 49 | ||||
-rw-r--r-- | kernel/seccomp.c | 16 |
3 files changed, 66 insertions, 5 deletions
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 4054b0994071..9ff98b4bfe2e 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h | |||
@@ -14,11 +14,11 @@ struct seccomp_filter; | |||
14 | * | 14 | * |
15 | * @mode: indicates one of the valid values above for controlled | 15 | * @mode: indicates one of the valid values above for controlled |
16 | * system calls available to a process. | 16 | * system calls available to a process. |
17 | * @filter: The metadata and ruleset for determining what system calls | 17 | * @filter: must always point to a valid seccomp-filter or NULL as it is |
18 | * are allowed for a task. | 18 | * accessed without locking during system call entry. |
19 | * | 19 | * |
20 | * @filter must only be accessed from the context of current as there | 20 | * @filter must only be accessed from the context of current as there |
21 | * is no locking. | 21 | * is no read locking. |
22 | */ | 22 | */ |
23 | struct seccomp { | 23 | struct seccomp { |
24 | int mode; | 24 | int mode; |
diff --git a/kernel/fork.c b/kernel/fork.c index 6a13c46cd87d..ed4bc339c9dc 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -315,6 +315,15 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) | |||
315 | goto free_ti; | 315 | goto free_ti; |
316 | 316 | ||
317 | tsk->stack = ti; | 317 | tsk->stack = ti; |
318 | #ifdef CONFIG_SECCOMP | ||
319 | /* | ||
320 | * We must handle setting up seccomp filters once we're under | ||
321 | * the sighand lock in case orig has changed between now and | ||
322 | * then. Until then, filter must be NULL to avoid messing up | ||
323 | * the usage counts on the error path calling free_task. | ||
324 | */ | ||
325 | tsk->seccomp.filter = NULL; | ||
326 | #endif | ||
318 | 327 | ||
319 | setup_thread_stack(tsk, orig); | 328 | setup_thread_stack(tsk, orig); |
320 | clear_user_return_notifier(tsk); | 329 | clear_user_return_notifier(tsk); |
@@ -1081,6 +1090,39 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) | |||
1081 | return 0; | 1090 | return 0; |
1082 | } | 1091 | } |
1083 | 1092 | ||
1093 | static void copy_seccomp(struct task_struct *p) | ||
1094 | { | ||
1095 | #ifdef CONFIG_SECCOMP | ||
1096 | /* | ||
1097 | * Must be called with sighand->lock held, which is common to | ||
1098 | * all threads in the group. Holding cred_guard_mutex is not | ||
1099 | * needed because this new task is not yet running and cannot | ||
1100 | * be racing exec. | ||
1101 | */ | ||
1102 | BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||
1103 | |||
1104 | /* Ref-count the new filter user, and assign it. */ | ||
1105 | get_seccomp_filter(current); | ||
1106 | p->seccomp = current->seccomp; | ||
1107 | |||
1108 | /* | ||
1109 | * Explicitly enable no_new_privs here in case it got set | ||
1110 | * between the task_struct being duplicated and holding the | ||
1111 | * sighand lock. The seccomp state and nnp must be in sync. | ||
1112 | */ | ||
1113 | if (task_no_new_privs(current)) | ||
1114 | task_set_no_new_privs(p); | ||
1115 | |||
1116 | /* | ||
1117 | * If the parent gained a seccomp mode after copying thread | ||
1118 | * flags and between before we held the sighand lock, we have | ||
1119 | * to manually enable the seccomp thread flag here. | ||
1120 | */ | ||
1121 | if (p->seccomp.mode != SECCOMP_MODE_DISABLED) | ||
1122 | set_tsk_thread_flag(p, TIF_SECCOMP); | ||
1123 | #endif | ||
1124 | } | ||
1125 | |||
1084 | SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr) | 1126 | SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr) |
1085 | { | 1127 | { |
1086 | current->clear_child_tid = tidptr; | 1128 | current->clear_child_tid = tidptr; |
@@ -1196,7 +1238,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1196 | goto fork_out; | 1238 | goto fork_out; |
1197 | 1239 | ||
1198 | ftrace_graph_init_task(p); | 1240 | ftrace_graph_init_task(p); |
1199 | get_seccomp_filter(p); | ||
1200 | 1241 | ||
1201 | rt_mutex_init_task(p); | 1242 | rt_mutex_init_task(p); |
1202 | 1243 | ||
@@ -1437,6 +1478,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1437 | spin_lock(¤t->sighand->siglock); | 1478 | spin_lock(¤t->sighand->siglock); |
1438 | 1479 | ||
1439 | /* | 1480 | /* |
1481 | * Copy seccomp details explicitly here, in case they were changed | ||
1482 | * before holding sighand lock. | ||
1483 | */ | ||
1484 | copy_seccomp(p); | ||
1485 | |||
1486 | /* | ||
1440 | * Process group and session signals need to be delivered to just the | 1487 | * Process group and session signals need to be delivered to just the |
1441 | * parent before the fork or both the parent and the child after the | 1488 | * parent before the fork or both the parent and the child after the |
1442 | * fork. Restart if a signal comes in before we add the new process to | 1489 | * fork. Restart if a signal comes in before we add the new process to |
diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 58125160417c..d5543e787e4e 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c | |||
@@ -199,6 +199,8 @@ static u32 seccomp_run_filters(int syscall) | |||
199 | 199 | ||
200 | static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) | 200 | static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) |
201 | { | 201 | { |
202 | BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||
203 | |||
202 | if (current->seccomp.mode && current->seccomp.mode != seccomp_mode) | 204 | if (current->seccomp.mode && current->seccomp.mode != seccomp_mode) |
203 | return false; | 205 | return false; |
204 | 206 | ||
@@ -207,6 +209,8 @@ static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) | |||
207 | 209 | ||
208 | static inline void seccomp_assign_mode(unsigned long seccomp_mode) | 210 | static inline void seccomp_assign_mode(unsigned long seccomp_mode) |
209 | { | 211 | { |
212 | BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||
213 | |||
210 | current->seccomp.mode = seccomp_mode; | 214 | current->seccomp.mode = seccomp_mode; |
211 | set_tsk_thread_flag(current, TIF_SECCOMP); | 215 | set_tsk_thread_flag(current, TIF_SECCOMP); |
212 | } | 216 | } |
@@ -332,6 +336,8 @@ out: | |||
332 | * @flags: flags to change filter behavior | 336 | * @flags: flags to change filter behavior |
333 | * @filter: seccomp filter to add to the current process | 337 | * @filter: seccomp filter to add to the current process |
334 | * | 338 | * |
339 | * Caller must be holding current->sighand->siglock lock. | ||
340 | * | ||
335 | * Returns 0 on success, -ve on error. | 341 | * Returns 0 on success, -ve on error. |
336 | */ | 342 | */ |
337 | static long seccomp_attach_filter(unsigned int flags, | 343 | static long seccomp_attach_filter(unsigned int flags, |
@@ -340,6 +346,8 @@ static long seccomp_attach_filter(unsigned int flags, | |||
340 | unsigned long total_insns; | 346 | unsigned long total_insns; |
341 | struct seccomp_filter *walker; | 347 | struct seccomp_filter *walker; |
342 | 348 | ||
349 | BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||
350 | |||
343 | /* Validate resulting filter length. */ | 351 | /* Validate resulting filter length. */ |
344 | total_insns = filter->prog->len; | 352 | total_insns = filter->prog->len; |
345 | for (walker = current->seccomp.filter; walker; walker = walker->prev) | 353 | for (walker = current->seccomp.filter; walker; walker = walker->prev) |
@@ -529,6 +537,8 @@ static long seccomp_set_mode_strict(void) | |||
529 | const unsigned long seccomp_mode = SECCOMP_MODE_STRICT; | 537 | const unsigned long seccomp_mode = SECCOMP_MODE_STRICT; |
530 | long ret = -EINVAL; | 538 | long ret = -EINVAL; |
531 | 539 | ||
540 | spin_lock_irq(¤t->sighand->siglock); | ||
541 | |||
532 | if (!seccomp_may_assign_mode(seccomp_mode)) | 542 | if (!seccomp_may_assign_mode(seccomp_mode)) |
533 | goto out; | 543 | goto out; |
534 | 544 | ||
@@ -539,6 +549,7 @@ static long seccomp_set_mode_strict(void) | |||
539 | ret = 0; | 549 | ret = 0; |
540 | 550 | ||
541 | out: | 551 | out: |
552 | spin_unlock_irq(¤t->sighand->siglock); | ||
542 | 553 | ||
543 | return ret; | 554 | return ret; |
544 | } | 555 | } |
@@ -566,13 +577,15 @@ static long seccomp_set_mode_filter(unsigned int flags, | |||
566 | 577 | ||
567 | /* Validate flags. */ | 578 | /* Validate flags. */ |
568 | if (flags != 0) | 579 | if (flags != 0) |
569 | goto out; | 580 | return -EINVAL; |
570 | 581 | ||
571 | /* Prepare the new filter before holding any locks. */ | 582 | /* Prepare the new filter before holding any locks. */ |
572 | prepared = seccomp_prepare_user_filter(filter); | 583 | prepared = seccomp_prepare_user_filter(filter); |
573 | if (IS_ERR(prepared)) | 584 | if (IS_ERR(prepared)) |
574 | return PTR_ERR(prepared); | 585 | return PTR_ERR(prepared); |
575 | 586 | ||
587 | spin_lock_irq(¤t->sighand->siglock); | ||
588 | |||
576 | if (!seccomp_may_assign_mode(seccomp_mode)) | 589 | if (!seccomp_may_assign_mode(seccomp_mode)) |
577 | goto out; | 590 | goto out; |
578 | 591 | ||
@@ -584,6 +597,7 @@ static long seccomp_set_mode_filter(unsigned int flags, | |||
584 | 597 | ||
585 | seccomp_assign_mode(seccomp_mode); | 598 | seccomp_assign_mode(seccomp_mode); |
586 | out: | 599 | out: |
600 | spin_unlock_irq(¤t->sighand->siglock); | ||
587 | seccomp_filter_free(prepared); | 601 | seccomp_filter_free(prepared); |
588 | return ret; | 602 | return ret; |
589 | } | 603 | } |