File Index
Files and directories
Sign in
FILE

user.c

File preview with syntax highlighting.

linux-6.19.14/kernel/unwind/user.c C
  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
 14static inline int
 15get_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
 29static 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
 68static 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
 89static 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
119static 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
146int 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}
~/ Read-only file preview.