aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElena Reshetova <elena.reshetova@intel.com>2019-01-18 07:27:30 -0500
committerIngo Molnar <mingo@kernel.org>2019-02-04 02:53:56 -0500
commitf0b89d3958d73cd0785ec381f0ddf8efb6f183d8 (patch)
tree4592c3e286b545412053241a5e5f37941e1d9d7a
parentec1d281923cf81cc660343d0cb8ffc837ffb991d (diff)
sched/core: Convert task_struct.stack_refcount to refcount_t
atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable task_struct.stack_refcount is used as pure reference counter. Convert it to refcount_t and fix up the operations. ** Important note for maintainers: Some functions from refcount_t API defined in lib/refcount.c have different memory ordering guarantees than their atomic counterparts. The full comparison can be seen in https://lkml.org/lkml/2017/11/15/57 and it is hopefully soon in state to be merged to the documentation tree. Normally the differences should not matter since refcount_t provides enough guarantees to satisfy the refcounting use cases, but in some rare cases it might matter. Please double check that you don't have some undocumented memory guarantees for this variable usage. For the task_struct.stack_refcount it might make a difference in following places: - try_get_task_stack(): increment in refcount_inc_not_zero() only guarantees control dependency on success vs. fully ordered atomic counterpart - put_task_stack(): decrement in refcount_dec_and_test() only provides RELEASE ordering and control dependency on success vs. fully ordered atomic counterpart Suggested-by: Kees Cook <keescook@chromium.org> Signed-off-by: Elena Reshetova <elena.reshetova@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: David Windsor <dwindsor@gmail.com> Reviewed-by: Hans Liljestrand <ishkamiel@gmail.com> Reviewed-by: Andrea Parri <andrea.parri@amarulasolutions.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: akpm@linux-foundation.org Cc: viro@zeniv.linux.org.uk Link: https://lkml.kernel.org/r/1547814450-18902-6-git-send-email-elena.reshetova@intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--include/linux/init_task.h1
-rw-r--r--include/linux/sched.h2
-rw-r--r--include/linux/sched/task_stack.h2
-rw-r--r--init/init_task.c2
-rw-r--r--kernel/fork.c6
5 files changed, 7 insertions, 6 deletions
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index a7083a45a26c..6049baa5b8bc 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -13,6 +13,7 @@
13#include <linux/securebits.h> 13#include <linux/securebits.h>
14#include <linux/seqlock.h> 14#include <linux/seqlock.h>
15#include <linux/rbtree.h> 15#include <linux/rbtree.h>
16#include <linux/refcount.h>
16#include <linux/sched/autogroup.h> 17#include <linux/sched/autogroup.h>
17#include <net/net_namespace.h> 18#include <net/net_namespace.h>
18#include <linux/sched/rt.h> 19#include <linux/sched/rt.h>
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 9d14d6864ca6..628bf13cb5a5 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1194,7 +1194,7 @@ struct task_struct {
1194#endif 1194#endif
1195#ifdef CONFIG_THREAD_INFO_IN_TASK 1195#ifdef CONFIG_THREAD_INFO_IN_TASK
1196 /* A live task holds one reference: */ 1196 /* A live task holds one reference: */
1197 atomic_t stack_refcount; 1197 refcount_t stack_refcount;
1198#endif 1198#endif
1199#ifdef CONFIG_LIVEPATCH 1199#ifdef CONFIG_LIVEPATCH
1200 int patch_state; 1200 int patch_state;
diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h
index 6a841929073f..2413427e439c 100644
--- a/include/linux/sched/task_stack.h
+++ b/include/linux/sched/task_stack.h
@@ -61,7 +61,7 @@ static inline unsigned long *end_of_stack(struct task_struct *p)
61#ifdef CONFIG_THREAD_INFO_IN_TASK 61#ifdef CONFIG_THREAD_INFO_IN_TASK
62static inline void *try_get_task_stack(struct task_struct *tsk) 62static inline void *try_get_task_stack(struct task_struct *tsk)
63{ 63{
64 return atomic_inc_not_zero(&tsk->stack_refcount) ? 64 return refcount_inc_not_zero(&tsk->stack_refcount) ?
65 task_stack_page(tsk) : NULL; 65 task_stack_page(tsk) : NULL;
66} 66}
67 67
diff --git a/init/init_task.c b/init/init_task.c
index aca34c89529f..46dbf546264d 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -61,7 +61,7 @@ struct task_struct init_task
61= { 61= {
62#ifdef CONFIG_THREAD_INFO_IN_TASK 62#ifdef CONFIG_THREAD_INFO_IN_TASK
63 .thread_info = INIT_THREAD_INFO(init_task), 63 .thread_info = INIT_THREAD_INFO(init_task),
64 .stack_refcount = ATOMIC_INIT(1), 64 .stack_refcount = REFCOUNT_INIT(1),
65#endif 65#endif
66 .state = 0, 66 .state = 0,
67 .stack = init_stack, 67 .stack = init_stack,
diff --git a/kernel/fork.c b/kernel/fork.c
index 3f7e192e29f2..77059b211608 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -429,7 +429,7 @@ static void release_task_stack(struct task_struct *tsk)
429#ifdef CONFIG_THREAD_INFO_IN_TASK 429#ifdef CONFIG_THREAD_INFO_IN_TASK
430void put_task_stack(struct task_struct *tsk) 430void put_task_stack(struct task_struct *tsk)
431{ 431{
432 if (atomic_dec_and_test(&tsk->stack_refcount)) 432 if (refcount_dec_and_test(&tsk->stack_refcount))
433 release_task_stack(tsk); 433 release_task_stack(tsk);
434} 434}
435#endif 435#endif
@@ -447,7 +447,7 @@ void free_task(struct task_struct *tsk)
447 * If the task had a separate stack allocation, it should be gone 447 * If the task had a separate stack allocation, it should be gone
448 * by now. 448 * by now.
449 */ 449 */
450 WARN_ON_ONCE(atomic_read(&tsk->stack_refcount) != 0); 450 WARN_ON_ONCE(refcount_read(&tsk->stack_refcount) != 0);
451#endif 451#endif
452 rt_mutex_debug_task_free(tsk); 452 rt_mutex_debug_task_free(tsk);
453 ftrace_graph_exit_task(tsk); 453 ftrace_graph_exit_task(tsk);
@@ -867,7 +867,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
867 tsk->stack_vm_area = stack_vm_area; 867 tsk->stack_vm_area = stack_vm_area;
868#endif 868#endif
869#ifdef CONFIG_THREAD_INFO_IN_TASK 869#ifdef CONFIG_THREAD_INFO_IN_TASK
870 atomic_set(&tsk->stack_refcount, 1); 870 refcount_set(&tsk->stack_refcount, 1);
871#endif 871#endif
872 872
873 if (err) 873 if (err)