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> | |
17 | ||
9ec79840 MR |
18 | /* |
19 | * The lowest address on tsk's stack which we can plausibly erase. | |
20 | */ | |
21 | static __always_inline unsigned long | |
22 | stackleak_task_low_bound(const struct task_struct *tsk) | |
23 | { | |
24 | /* | |
25 | * The lowest unsigned long on the task stack contains STACK_END_MAGIC, | |
26 | * which we must not corrupt. | |
27 | */ | |
28 | return (unsigned long)end_of_stack(tsk) + sizeof(unsigned long); | |
29 | } | |
30 | ||
0cfa2ccd MR |
31 | /* |
32 | * The address immediately after the highest address on tsk's stack which we | |
33 | * can plausibly erase. | |
34 | */ | |
35 | static __always_inline unsigned long | |
36 | stackleak_task_high_bound(const struct task_struct *tsk) | |
37 | { | |
38 | /* | |
39 | * The task's pt_regs lives at the top of the task stack and will be | |
40 | * overwritten by exception entry, so there's no need to erase them. | |
41 | */ | |
42 | return (unsigned long)task_pt_regs(tsk); | |
43 | } | |
44 | ||
77cf2b6d MR |
45 | /* |
46 | * Find the address immediately above the poisoned region of the stack, where | |
47 | * that region falls between 'low' (inclusive) and 'high' (exclusive). | |
48 | */ | |
49 | static __always_inline unsigned long | |
50 | stackleak_find_top_of_poison(const unsigned long low, const unsigned long high) | |
51 | { | |
52 | const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long); | |
53 | unsigned int poison_count = 0; | |
54 | unsigned long poison_high = high; | |
55 | unsigned long sp = high; | |
56 | ||
57 | while (sp > low && poison_count < depth) { | |
58 | sp -= sizeof(unsigned long); | |
59 | ||
60 | if (*(unsigned long *)sp == STACKLEAK_POISON) { | |
61 | poison_count++; | |
62 | } else { | |
63 | poison_count = 0; | |
64 | poison_high = sp; | |
65 | } | |
66 | } | |
67 | ||
68 | return poison_high; | |
69 | } | |
70 | ||
afaef01c AP |
71 | static inline void stackleak_task_init(struct task_struct *t) |
72 | { | |
9ec79840 | 73 | t->lowest_stack = stackleak_task_low_bound(t); |
c8d12627 AP |
74 | # ifdef CONFIG_STACKLEAK_METRICS |
75 | t->prev_lowest_stack = t->lowest_stack; | |
76 | # endif | |
afaef01c | 77 | } |
964c9dff | 78 | |
afaef01c AP |
79 | #else /* !CONFIG_GCC_PLUGIN_STACKLEAK */ |
80 | static inline void stackleak_task_init(struct task_struct *t) { } | |
81 | #endif | |
82 | ||
83 | #endif |