]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
dfec072e VN |
2 | #include <linux/interrupt.h> |
3 | #include <linux/kdebug.h> | |
4 | #include <linux/kmemcheck.h> | |
5 | #include <linux/kernel.h> | |
6 | #include <linux/types.h> | |
7 | #include <linux/ptrace.h> | |
8 | #include <linux/stacktrace.h> | |
9 | #include <linux/string.h> | |
10 | ||
11 | #include "error.h" | |
12 | #include "shadow.h" | |
13 | ||
14 | enum kmemcheck_error_type { | |
15 | KMEMCHECK_ERROR_INVALID_ACCESS, | |
16 | KMEMCHECK_ERROR_BUG, | |
17 | }; | |
18 | ||
19 | #define SHADOW_COPY_SIZE (1 << CONFIG_KMEMCHECK_SHADOW_COPY_SHIFT) | |
20 | ||
21 | struct kmemcheck_error { | |
22 | enum kmemcheck_error_type type; | |
23 | ||
24 | union { | |
25 | /* KMEMCHECK_ERROR_INVALID_ACCESS */ | |
26 | struct { | |
27 | /* Kind of access that caused the error */ | |
28 | enum kmemcheck_shadow state; | |
29 | /* Address and size of the erroneous read */ | |
30 | unsigned long address; | |
31 | unsigned int size; | |
32 | }; | |
33 | }; | |
34 | ||
35 | struct pt_regs regs; | |
36 | struct stack_trace trace; | |
37 | unsigned long trace_entries[32]; | |
38 | ||
39 | /* We compress it to a char. */ | |
40 | unsigned char shadow_copy[SHADOW_COPY_SIZE]; | |
41 | unsigned char memory_copy[SHADOW_COPY_SIZE]; | |
42 | }; | |
43 | ||
44 | /* | |
45 | * Create a ring queue of errors to output. We can't call printk() directly | |
46 | * from the kmemcheck traps, since this may call the console drivers and | |
47 | * result in a recursive fault. | |
48 | */ | |
49 | static struct kmemcheck_error error_fifo[CONFIG_KMEMCHECK_QUEUE_SIZE]; | |
50 | static unsigned int error_count; | |
51 | static unsigned int error_rd; | |
52 | static unsigned int error_wr; | |
53 | static unsigned int error_missed_count; | |
54 | ||
55 | static struct kmemcheck_error *error_next_wr(void) | |
56 | { | |
57 | struct kmemcheck_error *e; | |
58 | ||
59 | if (error_count == ARRAY_SIZE(error_fifo)) { | |
60 | ++error_missed_count; | |
61 | return NULL; | |
62 | } | |
63 | ||
64 | e = &error_fifo[error_wr]; | |
65 | if (++error_wr == ARRAY_SIZE(error_fifo)) | |
66 | error_wr = 0; | |
67 | ++error_count; | |
68 | return e; | |
69 | } | |
70 | ||
71 | static struct kmemcheck_error *error_next_rd(void) | |
72 | { | |
73 | struct kmemcheck_error *e; | |
74 | ||
75 | if (error_count == 0) | |
76 | return NULL; | |
77 | ||
78 | e = &error_fifo[error_rd]; | |
79 | if (++error_rd == ARRAY_SIZE(error_fifo)) | |
80 | error_rd = 0; | |
81 | --error_count; | |
82 | return e; | |
83 | } | |
84 | ||
6a196387 PE |
85 | void kmemcheck_error_recall(void) |
86 | { | |
87 | static const char *desc[] = { | |
88 | [KMEMCHECK_SHADOW_UNALLOCATED] = "unallocated", | |
89 | [KMEMCHECK_SHADOW_UNINITIALIZED] = "uninitialized", | |
90 | [KMEMCHECK_SHADOW_INITIALIZED] = "initialized", | |
91 | [KMEMCHECK_SHADOW_FREED] = "freed", | |
92 | }; | |
93 | ||
94 | static const char short_desc[] = { | |
95 | [KMEMCHECK_SHADOW_UNALLOCATED] = 'a', | |
96 | [KMEMCHECK_SHADOW_UNINITIALIZED] = 'u', | |
97 | [KMEMCHECK_SHADOW_INITIALIZED] = 'i', | |
98 | [KMEMCHECK_SHADOW_FREED] = 'f', | |
99 | }; | |
100 | ||
101 | struct kmemcheck_error *e; | |
102 | unsigned int i; | |
103 | ||
104 | e = error_next_rd(); | |
105 | if (!e) | |
106 | return; | |
107 | ||
108 | switch (e->type) { | |
109 | case KMEMCHECK_ERROR_INVALID_ACCESS: | |
c0ca9da4 | 110 | printk(KERN_WARNING "WARNING: kmemcheck: Caught %d-bit read from %s memory (%p)\n", |
6a196387 PE |
111 | 8 * e->size, e->state < ARRAY_SIZE(desc) ? |
112 | desc[e->state] : "(invalid shadow state)", | |
113 | (void *) e->address); | |
114 | ||
c0ca9da4 | 115 | printk(KERN_WARNING); |
6a196387 | 116 | for (i = 0; i < SHADOW_COPY_SIZE; ++i) |
c0ca9da4 PE |
117 | printk(KERN_CONT "%02x", e->memory_copy[i]); |
118 | printk(KERN_CONT "\n"); | |
6a196387 | 119 | |
c0ca9da4 | 120 | printk(KERN_WARNING); |
6a196387 PE |
121 | for (i = 0; i < SHADOW_COPY_SIZE; ++i) { |
122 | if (e->shadow_copy[i] < ARRAY_SIZE(short_desc)) | |
c0ca9da4 | 123 | printk(KERN_CONT " %c", short_desc[e->shadow_copy[i]]); |
6a196387 | 124 | else |
c0ca9da4 | 125 | printk(KERN_CONT " ?"); |
6a196387 | 126 | } |
c0ca9da4 PE |
127 | printk(KERN_CONT "\n"); |
128 | printk(KERN_WARNING "%*c\n", 2 + 2 | |
6a196387 PE |
129 | * (int) (e->address & (SHADOW_COPY_SIZE - 1)), '^'); |
130 | break; | |
131 | case KMEMCHECK_ERROR_BUG: | |
132 | printk(KERN_EMERG "ERROR: kmemcheck: Fatal error\n"); | |
133 | break; | |
134 | } | |
135 | ||
136 | __show_regs(&e->regs, 1); | |
137 | print_stack_trace(&e->trace, 0); | |
138 | } | |
139 | ||
140 | static void do_wakeup(unsigned long data) | |
141 | { | |
142 | while (error_count > 0) | |
143 | kmemcheck_error_recall(); | |
144 | ||
145 | if (error_missed_count > 0) { | |
146 | printk(KERN_WARNING "kmemcheck: Lost %d error reports because " | |
147 | "the queue was too small\n", error_missed_count); | |
148 | error_missed_count = 0; | |
149 | } | |
150 | } | |
151 | ||
dfec072e VN |
152 | static DECLARE_TASKLET(kmemcheck_tasklet, &do_wakeup, 0); |
153 | ||
154 | /* | |
155 | * Save the context of an error report. | |
156 | */ | |
157 | void kmemcheck_error_save(enum kmemcheck_shadow state, | |
158 | unsigned long address, unsigned int size, struct pt_regs *regs) | |
159 | { | |
160 | static unsigned long prev_ip; | |
161 | ||
162 | struct kmemcheck_error *e; | |
163 | void *shadow_copy; | |
164 | void *memory_copy; | |
165 | ||
166 | /* Don't report several adjacent errors from the same EIP. */ | |
167 | if (regs->ip == prev_ip) | |
168 | return; | |
169 | prev_ip = regs->ip; | |
170 | ||
171 | e = error_next_wr(); | |
172 | if (!e) | |
173 | return; | |
174 | ||
175 | e->type = KMEMCHECK_ERROR_INVALID_ACCESS; | |
176 | ||
177 | e->state = state; | |
178 | e->address = address; | |
179 | e->size = size; | |
180 | ||
181 | /* Save regs */ | |
182 | memcpy(&e->regs, regs, sizeof(*regs)); | |
183 | ||
184 | /* Save stack trace */ | |
185 | e->trace.nr_entries = 0; | |
186 | e->trace.entries = e->trace_entries; | |
187 | e->trace.max_entries = ARRAY_SIZE(e->trace_entries); | |
188 | e->trace.skip = 0; | |
39581062 | 189 | save_stack_trace_regs(regs, &e->trace); |
dfec072e VN |
190 | |
191 | /* Round address down to nearest 16 bytes */ | |
192 | shadow_copy = kmemcheck_shadow_lookup(address | |
193 | & ~(SHADOW_COPY_SIZE - 1)); | |
194 | BUG_ON(!shadow_copy); | |
195 | ||
196 | memcpy(e->shadow_copy, shadow_copy, SHADOW_COPY_SIZE); | |
197 | ||
198 | kmemcheck_show_addr(address); | |
199 | memory_copy = (void *) (address & ~(SHADOW_COPY_SIZE - 1)); | |
200 | memcpy(e->memory_copy, memory_copy, SHADOW_COPY_SIZE); | |
201 | kmemcheck_hide_addr(address); | |
202 | ||
203 | tasklet_hi_schedule_first(&kmemcheck_tasklet); | |
204 | } | |
205 | ||
206 | /* | |
207 | * Save the context of a kmemcheck bug. | |
208 | */ | |
209 | void kmemcheck_error_save_bug(struct pt_regs *regs) | |
210 | { | |
211 | struct kmemcheck_error *e; | |
212 | ||
213 | e = error_next_wr(); | |
214 | if (!e) | |
215 | return; | |
216 | ||
217 | e->type = KMEMCHECK_ERROR_BUG; | |
218 | ||
219 | memcpy(&e->regs, regs, sizeof(*regs)); | |
220 | ||
221 | e->trace.nr_entries = 0; | |
222 | e->trace.entries = e->trace_entries; | |
223 | e->trace.max_entries = ARRAY_SIZE(e->trace_entries); | |
224 | e->trace.skip = 1; | |
225 | save_stack_trace(&e->trace); | |
226 | ||
227 | tasklet_hi_schedule_first(&kmemcheck_tasklet); | |
228 | } |