xref: /linux/kernel/unwind/user.c (revision 7fc2cd2e4b398c57c9cf961cfea05eadbf34c05c)
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
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 
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 	if (frame->use_fp) {
35 		if (state->fp < state->sp)
36 			return -EINVAL;
37 		cfa = state->fp;
38 	} else {
39 		cfa = state->sp;
40 	}
41 
42 	/* Get the Canonical Frame Address (CFA) */
43 	cfa += frame->cfa_off;
44 
45 	/* stack going in wrong direction? */
46 	if (cfa <= state->sp)
47 		return -EINVAL;
48 
49 	/* Make sure that the address is word aligned */
50 	if (cfa & (state->ws - 1))
51 		return -EINVAL;
52 
53 	/* Find the Return Address (RA) */
54 	if (get_user_word(&ra, cfa, frame->ra_off, state->ws))
55 		return -EINVAL;
56 
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 
68 static int unwind_user_next_fp(struct unwind_user_state *state)
69 {
70 #ifdef CONFIG_HAVE_UNWIND_USER_FP
71 	struct pt_regs *regs = task_pt_regs(current);
72 
73 	if (state->topmost && unwind_user_at_function_start(regs)) {
74 		const struct unwind_user_frame fp_entry_frame = {
75 			ARCH_INIT_USER_FP_ENTRY_FRAME(state->ws)
76 		};
77 		return unwind_user_next_common(state, &fp_entry_frame);
78 	}
79 
80 	const struct unwind_user_frame fp_frame = {
81 		ARCH_INIT_USER_FP_FRAME(state->ws)
82 	};
83 	return unwind_user_next_common(state, &fp_frame);
84 #else
85 	return -EINVAL;
86 #endif
87 }
88 
89 static int unwind_user_next(struct unwind_user_state *state)
90 {
91 	unsigned long iter_mask = state->available_types;
92 	unsigned int bit;
93 
94 	if (state->done)
95 		return -EINVAL;
96 
97 	for_each_set_bit(bit, &iter_mask, NR_UNWIND_USER_TYPE_BITS) {
98 		enum unwind_user_type type = BIT(bit);
99 
100 		state->current_type = type;
101 		switch (type) {
102 		case UNWIND_USER_TYPE_FP:
103 			if (!unwind_user_next_fp(state))
104 				return 0;
105 			continue;
106 		default:
107 			WARN_ONCE(1, "Undefined unwind bit %d", bit);
108 			break;
109 		}
110 		break;
111 	}
112 
113 	/* No successful unwind method. */
114 	state->current_type = UNWIND_USER_TYPE_NONE;
115 	state->done = true;
116 	return -EINVAL;
117 }
118 
119 static int unwind_user_start(struct unwind_user_state *state)
120 {
121 	struct pt_regs *regs = task_pt_regs(current);
122 
123 	memset(state, 0, sizeof(*state));
124 
125 	if ((current->flags & PF_KTHREAD) || !user_mode(regs)) {
126 		state->done = true;
127 		return -EINVAL;
128 	}
129 
130 	if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
131 		state->available_types |= UNWIND_USER_TYPE_FP;
132 
133 	state->ip = instruction_pointer(regs);
134 	state->sp = user_stack_pointer(regs);
135 	state->fp = frame_pointer(regs);
136 	state->ws = unwind_user_word_size(regs);
137 	if (!state->ws) {
138 		state->done = true;
139 		return -EINVAL;
140 	}
141 	state->topmost = true;
142 
143 	return 0;
144 }
145 
146 int unwind_user(struct unwind_stacktrace *trace, unsigned int max_entries)
147 {
148 	struct unwind_user_state state;
149 
150 	trace->nr = 0;
151 
152 	if (!max_entries)
153 		return -EINVAL;
154 
155 	if (current->flags & PF_KTHREAD)
156 		return 0;
157 
158 	for_each_user_frame(&state) {
159 		trace->entries[trace->nr++] = state.ip;
160 		if (trace->nr >= max_entries)
161 			break;
162 	}
163 
164 	return 0;
165 }
166