Soon, memory mapped for a kernel stack can be less than THREAD_SIZE. However, many parts of the kernel assume it is. Add new helpers for the kernel stack that can be called regardless of CONFIG_STACK_GROWSUP or the kernel stack size: - task_stack_low(): Lowest usable address on the stack. - task_stack_high(): Highest usable address on the stack. - task_stack_size(): The size of a task kernel stack, it is still THREAD_SIZE at the moment but that will change soon.
Signed-off-by: Mostafa Saleh <[email protected]> --- include/linux/sched/task_stack.h | 56 ++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h index 1fab7e9043a3..4a808f9c4631 100644 --- a/include/linux/sched/task_stack.h +++ b/include/linux/sched/task_stack.h @@ -13,6 +13,8 @@ #ifdef CONFIG_THREAD_INFO_IN_TASK +static __always_inline void *task_stack_low(const struct task_struct *task); + /* * When accessing the stack of a non-current task that might exit, use * try_get_task_stack() instead. task_stack_page will return a pointer @@ -30,7 +32,7 @@ static __always_inline unsigned long *end_of_stack(const struct task_struct *tas #ifdef CONFIG_STACK_GROWSUP return (unsigned long *)((unsigned long)task->stack + THREAD_SIZE) - 1; #else - return task->stack; + return task_stack_low(task); #endif } @@ -64,6 +66,53 @@ static inline unsigned long *end_of_stack(const struct task_struct *p) #endif +/* + * Kernel stack layout, higher addresses at the top. task_stack_page() is + * the start of the THREAD_SIZE area, task_stack_low() => task_stack_high() + * is the usable stack. + * + * Case 1) Stack grows down: Only the top task_stack_size() bytes are mapped: + * + * +-------------+ <- task_stack_high() = task_stack_page() + THREAD_SIZE + * | stack | + * | | | task_stack_size() bytes + * | v | + * +-------------+ <- task_stack_low() = end_of_stack() (*) + * | may be | + * | unmapped | THREAD_SIZE - task_stack_size() bytes + * +-------------+ <- task_stack_page() + * + * (*) Depending on CONFIG_THREAD_INFO_IN_TASK, end_of_stack() might be just + * above it. + * + * Case 2) Stack grows up (CONFIG_STACK_GROWSUP): The whole area is used. + * No support for partially mapped stacks. + * + * +-------------+ <- task_stack_high() = task_stack_page() + THREAD_SIZE + * | ^ | end_of_stack() is the last unsigned long below it + * | | | + * | stack | THREAD_SIZE bytes + * +-------------+ <- task_stack_low() = task_stack_page() + */ +static __always_inline unsigned long task_stack_size(const struct task_struct *task) +{ + return THREAD_SIZE; +} + +static __always_inline void *task_stack_low(const struct task_struct *task) +{ +#if defined(CONFIG_STACK_GROWSUP) + return task_stack_page(task); +#else + return task_stack_page(task) + THREAD_SIZE - task_stack_size(task); +#endif +} + +static __always_inline void *task_stack_high(const struct task_struct *task) +{ + return task_stack_low(task) + task_stack_size(task); +} + #ifdef CONFIG_THREAD_INFO_IN_TASK static inline void *try_get_task_stack(struct task_struct *tsk) { @@ -88,10 +137,11 @@ void exit_task_stack_account(struct task_struct *tsk); static inline int object_is_on_stack(const void *obj) { - void *stack = task_stack_page(current); + void *stack_base = task_stack_low(current); + void *stack_end = task_stack_high(current); obj = kasan_reset_tag(obj); - return (obj >= stack) && (obj < (stack + THREAD_SIZE)); + return (obj >= stack_base) && (obj < stack_end); } extern void thread_stack_cache_init(void); -- 2.56.0.rc1.315.gc6ed9934b7-goog

