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