xref: /linux/kernel/unwind/user.c (revision 37a93dd5c49b5fda807fd204edf2547c3493319c)
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 	/* 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 
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 
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 
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 
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