Commit | Line | Data |
---|---|---|
afaef01c AP |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef _LINUX_STACKLEAK_H | |
3 | #define _LINUX_STACKLEAK_H | |
4 | ||
5 | #include <linux/sched.h> | |
6 | #include <linux/sched/task_stack.h> | |
7 | ||
8 | /* | |
9 | * Check that the poison value points to the unused hole in the | |
10 | * virtual memory map for your platform. | |
11 | */ | |
12 | #define STACKLEAK_POISON -0xBEEF | |
13 | #define STACKLEAK_SEARCH_DEPTH 128 | |
14 | ||
15 | #ifdef CONFIG_GCC_PLUGIN_STACKLEAK | |
16 | #include <asm/stacktrace.h> | |
65120498 | 17 | #include <linux/linkage.h> |
afaef01c | 18 | |
9ec79840 MR |
19 | /* |
20 | * The lowest address on tsk's stack which we can plausibly erase. | |
21 | */ | |
22 | static __always_inline unsigned long | |
23 | stackleak_task_low_bound(const struct task_struct *tsk) | |
24 | { | |
25 | /* | |
26 | * The lowest unsigned long on the task stack contains STACK_END_MAGIC, | |
27 | * which we must not corrupt. | |
28 | */ | |
29 | return (unsigned long)end_of_stack(tsk) + sizeof(unsigned long); | |
30 | } | |
31 | ||
0cfa2ccd MR |
32 | /* |
33 | * The address immediately after the highest address on tsk's stack which we | |
34 | * can plausibly erase. | |
35 | */ | |
36 | static __always_inline unsigned long | |
37 | stackleak_task_high_bound(const struct task_struct *tsk) | |
38 | { | |
39 | /* | |
40 | * The task's pt_regs lives at the top of the task stack and will be | |
41 | * overwritten by exception entry, so there's no need to erase them. | |
42 | */ | |
43 | return (unsigned long)task_pt_regs(tsk); | |
44 | } | |
45 | ||
77cf2b6d MR |
46 | /* |
47 | * Find the address immediately above the poisoned region of the stack, where | |
48 | * that region falls between 'low' (inclusive) and 'high' (exclusive). | |
49 | */ | |
50 | static __always_inline unsigned long | |
51 | stackleak_find_top_of_poison(const unsigned long low, const unsigned long high) | |
52 | { | |
53 | const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long); | |
54 | unsigned int poison_count = 0; | |
55 | unsigned long poison_high = high; | |
56 | unsigned long sp = high; | |
57 | ||
58 | while (sp > low && poison_count < depth) { | |
59 | sp -= sizeof(unsigned long); | |
60 | ||
61 | if (*(unsigned long *)sp == STACKLEAK_POISON) { | |
62 | poison_count++; | |
63 | } else { | |
64 | poison_count = 0; | |
65 | poison_high = sp; | |
66 | } | |
67 | } | |
68 | ||
69 | return poison_high; | |
70 | } | |
71 | ||
afaef01c AP |
72 | static inline void stackleak_task_init(struct task_struct *t) |
73 | { | |
9ec79840 | 74 | t->lowest_stack = stackleak_task_low_bound(t); |
c8d12627 AP |
75 | # ifdef CONFIG_STACKLEAK_METRICS |
76 | t->prev_lowest_stack = t->lowest_stack; | |
77 | # endif | |
afaef01c | 78 | } |
964c9dff | 79 | |
65120498 AB |
80 | asmlinkage void noinstr stackleak_erase(void); |
81 | asmlinkage void noinstr stackleak_erase_on_task_stack(void); | |
82 | asmlinkage void noinstr stackleak_erase_off_task_stack(void); | |
83 | void __no_caller_saved_registers noinstr stackleak_track_stack(void); | |
84 | ||
afaef01c AP |
85 | #else /* !CONFIG_GCC_PLUGIN_STACKLEAK */ |
86 | static inline void stackleak_task_init(struct task_struct *t) { } | |
87 | #endif | |
88 | ||
89 | #endif |