summaryrefslogtreecommitdiff
path: root/arch/arm64/include/asm/stacktrace/common.h
blob: 18046a7248a2fef9184e73a73d276ce55716f672 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Common arm64 stack unwinder code.
 *
 * To implement a new arm64 stack unwinder:
 *     1) Include this header
 *
 *     2) Provide implementations for the following functions:
 *          on_overflow_stack():   Returns true if SP is on the overflow
 *                                 stack.
 *          on_accessible_stack(): Returns true is SP is on any accessible
 *                                 stack.
 *          unwind_next():         Performs validation checks on the frame
 *                                 pointer, and transitions unwind_state
 *                                 to the next frame.
 *
 *         See: arch/arm64/include/asm/stacktrace.h for reference
 *              implementations.
 *
 * Copyright (C) 2012 ARM Ltd.
 */
#ifndef __ASM_STACKTRACE_COMMON_H
#define __ASM_STACKTRACE_COMMON_H

#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/kprobes.h>
#include <linux/types.h>

enum stack_type {
	STACK_TYPE_UNKNOWN,
	STACK_TYPE_TASK,
	STACK_TYPE_IRQ,
	STACK_TYPE_OVERFLOW,
	STACK_TYPE_SDEI_NORMAL,
	STACK_TYPE_SDEI_CRITICAL,
	STACK_TYPE_HYP,
	__NR_STACK_TYPES
};

struct stack_info {
	unsigned long low;
	unsigned long high;
	enum stack_type type;
};

/*
 * A snapshot of a frame record or fp/lr register values, along with some
 * accounting information necessary for robust unwinding.
 *
 * @fp:          The fp value in the frame record (or the real fp)
 * @pc:          The lr value in the frame record (or the real lr)
 *
 * @stacks_done: Stacks which have been entirely unwound, for which it is no
 *               longer valid to unwind to.
 *
 * @prev_fp:     The fp that pointed to this frame record, or a synthetic value
 *               of 0. This is used to ensure that within a stack, each
 *               subsequent frame record is at an increasing address.
 * @prev_type:   The type of stack this frame record was on, or a synthetic
 *               value of STACK_TYPE_UNKNOWN. This is used to detect a
 *               transition from one stack to another.
 *
 * @kr_cur:      When KRETPROBES is selected, holds the kretprobe instance
 *               associated with the most recently encountered replacement lr
 *               value.
 *
 * @task:        The task being unwound.
 */
struct unwind_state {
	unsigned long fp;
	unsigned long pc;
	DECLARE_BITMAP(stacks_done, __NR_STACK_TYPES);
	unsigned long prev_fp;
	enum stack_type prev_type;
#ifdef CONFIG_KRETPROBES
	struct llist_node *kr_cur;
#endif
	struct task_struct *task;
};

static inline bool on_stack(unsigned long sp, unsigned long size,
			    unsigned long low, unsigned long high,
			    enum stack_type type, struct stack_info *info)
{
	if (!low)
		return false;

	if (sp < low || sp + size < sp || sp + size > high)
		return false;

	if (info) {
		info->low = low;
		info->high = high;
		info->type = type;
	}
	return true;
}

static inline void unwind_init_common(struct unwind_state *state,
				      struct task_struct *task)
{
	state->task = task;
#ifdef CONFIG_KRETPROBES
	state->kr_cur = NULL;
#endif

	/*
	 * Prime the first unwind.
	 *
	 * In unwind_next() we'll check that the FP points to a valid stack,
	 * which can't be STACK_TYPE_UNKNOWN, and the first unwind will be
	 * treated as a transition to whichever stack that happens to be. The
	 * prev_fp value won't be used, but we set it to 0 such that it is
	 * definitely not an accessible stack address.
	 */
	bitmap_zero(state->stacks_done, __NR_STACK_TYPES);
	state->prev_fp = 0;
	state->prev_type = STACK_TYPE_UNKNOWN;
}

/*
 * stack_trace_translate_fp_fn() - Translates a non-kernel frame pointer to
 * a kernel address.
 *
 * @fp:   the frame pointer to be updated to its kernel address.
 * @type: the stack type associated with frame pointer @fp
 *
 * Returns true and success and @fp is updated to the corresponding
 * kernel virtual address; otherwise returns false.
 */
typedef bool (*stack_trace_translate_fp_fn)(unsigned long *fp,
					    enum stack_type type);

/*
 * on_accessible_stack_fn() - Check whether a stack range is on any
 * of the possible stacks.
 *
 * @tsk:  task whose stack is being unwound
 * @sp:   stack address being checked
 * @size: size of the stack range being checked
 * @info: stack unwinding context
 */
typedef bool (*on_accessible_stack_fn)(const struct task_struct *tsk,
				       unsigned long sp, unsigned long size,
				       struct stack_info *info);

static inline int unwind_next_common(struct unwind_state *state,
				     struct stack_info *info,
				     on_accessible_stack_fn accessible,
				     stack_trace_translate_fp_fn translate_fp)
{
	unsigned long fp = state->fp, kern_fp = fp;
	struct task_struct *tsk = state->task;

	if (fp & 0x7)
		return -EINVAL;

	if (!accessible(tsk, fp, 16, info))
		return -EINVAL;

	if (test_bit(info->type, state->stacks_done))
		return -EINVAL;

	/*
	 * If fp is not from the current address space perform the necessary
	 * translation before dereferencing it to get the next fp.
	 */
	if (translate_fp && !translate_fp(&kern_fp, info->type))
		return -EINVAL;

	/*
	 * As stacks grow downward, any valid record on the same stack must be
	 * at a strictly higher address than the prior record.
	 *
	 * Stacks can nest in several valid orders, e.g.
	 *
	 * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL
	 * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW
	 * HYP -> OVERFLOW
	 *
	 * ... but the nesting itself is strict. Once we transition from one
	 * stack to another, it's never valid to unwind back to that first
	 * stack.
	 */
	if (info->type == state->prev_type) {
		if (fp <= state->prev_fp)
			return -EINVAL;
	} else {
		__set_bit(state->prev_type, state->stacks_done);
	}

	/*
	 * Record this frame record's values and location. The prev_fp and
	 * prev_type are only meaningful to the next unwind_next() invocation.
	 */
	state->fp = READ_ONCE(*(unsigned long *)(kern_fp));
	state->pc = READ_ONCE(*(unsigned long *)(kern_fp + 8));
	state->prev_fp = fp;
	state->prev_type = info->type;

	return 0;
}

#endif	/* __ASM_STACKTRACE_COMMON_H */