1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Generic interfaces for unwinding user space
4 */
5 #include <linux/kernel.h>
6 #include <linux/sched.h>
7 #include <linux/sched/task_stack.h>
8 #include <linux/unwind_user.h>
9 #include <linux/uaccess.h>
10
11 #define for_each_user_frame(state) \
12 for (unwind_user_start(state); !(state)->done; unwind_user_next(state))
13
14 static inline int
get_user_word(unsigned long * word,unsigned long base,int off,unsigned int ws)15 get_user_word(unsigned long *word, unsigned long base, int off, unsigned int ws)
16 {
17 unsigned long __user *addr = (void __user *)base + off;
18 #ifdef CONFIG_COMPAT
19 if (ws == sizeof(int)) {
20 unsigned int data;
21 int ret = get_user(data, (unsigned int __user *)addr);
22 *word = data;
23 return ret;
24 }
25 #endif
26 return get_user(*word, addr);
27 }
28
unwind_user_next_common(struct unwind_user_state * state,const struct unwind_user_frame * frame)29 static int unwind_user_next_common(struct unwind_user_state *state,
30 const struct unwind_user_frame *frame)
31 {
32 unsigned long cfa, fp, ra;
33
34 /* Get the Canonical Frame Address (CFA) */
35 if (frame->use_fp) {
36 if (state->fp < state->sp)
37 return -EINVAL;
38 cfa = state->fp;
39 } else {
40 cfa = state->sp;
41 }
42 cfa += frame->cfa_off;
43
44 /* Make sure that stack is not going in wrong direction */
45 if (cfa <= state->sp)
46 return -EINVAL;
47
48 /* Make sure that the address is word aligned */
49 if (cfa & (state->ws - 1))
50 return -EINVAL;
51
52 /* Get the Return Address (RA) */
53 if (get_user_word(&ra, cfa, frame->ra_off, state->ws))
54 return -EINVAL;
55
56 /* Get the Frame Pointer (FP) */
57 if (frame->fp_off && get_user_word(&fp, cfa, frame->fp_off, state->ws))
58 return -EINVAL;
59
60 state->ip = ra;
61 state->sp = cfa;
62 if (frame->fp_off)
63 state->fp = fp;
64 state->topmost = false;
65 return 0;
66 }
67
unwind_user_next_fp(struct unwind_user_state * state)68 static int unwind_user_next_fp(struct unwind_user_state *state)
69 {
70 struct pt_regs *regs = task_pt_regs(current);
71
72 if (state->topmost && unwind_user_at_function_start(regs)) {
73 const struct unwind_user_frame fp_entry_frame = {
74 ARCH_INIT_USER_FP_ENTRY_FRAME(state->ws)
75 };
76 return unwind_user_next_common(state, &fp_entry_frame);
77 }
78
79 const struct unwind_user_frame fp_frame = {
80 ARCH_INIT_USER_FP_FRAME(state->ws)
81 };
82 return unwind_user_next_common(state, &fp_frame);
83 }
84
unwind_user_next(struct unwind_user_state * state)85 static int unwind_user_next(struct unwind_user_state *state)
86 {
87 unsigned long iter_mask = state->available_types;
88 unsigned int bit;
89
90 if (state->done)
91 return -EINVAL;
92
93 for_each_set_bit(bit, &iter_mask, NR_UNWIND_USER_TYPE_BITS) {
94 enum unwind_user_type type = BIT(bit);
95
96 state->current_type = type;
97 switch (type) {
98 case UNWIND_USER_TYPE_FP:
99 if (!unwind_user_next_fp(state))
100 return 0;
101 continue;
102 default:
103 WARN_ONCE(1, "Undefined unwind bit %d", bit);
104 break;
105 }
106 break;
107 }
108
109 /* No successful unwind method. */
110 state->current_type = UNWIND_USER_TYPE_NONE;
111 state->done = true;
112 return -EINVAL;
113 }
114
unwind_user_start(struct unwind_user_state * state)115 static int unwind_user_start(struct unwind_user_state *state)
116 {
117 struct pt_regs *regs = task_pt_regs(current);
118
119 memset(state, 0, sizeof(*state));
120
121 if ((current->flags & PF_KTHREAD) || !user_mode(regs)) {
122 state->done = true;
123 return -EINVAL;
124 }
125
126 if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
127 state->available_types |= UNWIND_USER_TYPE_FP;
128
129 state->ip = instruction_pointer(regs);
130 state->sp = user_stack_pointer(regs);
131 state->fp = frame_pointer(regs);
132 state->ws = unwind_user_word_size(regs);
133 if (!state->ws) {
134 state->done = true;
135 return -EINVAL;
136 }
137 state->topmost = true;
138
139 return 0;
140 }
141
unwind_user(struct unwind_stacktrace * trace,unsigned int max_entries)142 int unwind_user(struct unwind_stacktrace *trace, unsigned int max_entries)
143 {
144 struct unwind_user_state state;
145
146 trace->nr = 0;
147
148 if (!max_entries)
149 return -EINVAL;
150
151 if (current->flags & PF_KTHREAD)
152 return 0;
153
154 for_each_user_frame(&state) {
155 trace->entries[trace->nr++] = state.ip;
156 if (trace->nr >= max_entries)
157 break;
158 }
159
160 return 0;
161 }
162