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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
// SPDX-License-Identifier: GPL-2.0
/*
* KMSAN error reporting routines.
*
* Copyright (C) 2019-2022 Google LLC
* Author: Alexander Potapenko <glider@google.com>
*
*/
#include <linux/console.h>
#include <linux/moduleparam.h>
#include <linux/stackdepot.h>
#include <linux/stacktrace.h>
#include <linux/uaccess.h>
#include "kmsan.h"
static DEFINE_RAW_SPINLOCK(kmsan_report_lock);
#define DESCR_SIZE 128
/* Protected by kmsan_report_lock */
static char report_local_descr[DESCR_SIZE];
int panic_on_kmsan __read_mostly;
EXPORT_SYMBOL_GPL(panic_on_kmsan);
#ifdef MODULE_PARAM_PREFIX
#undef MODULE_PARAM_PREFIX
#endif
#define MODULE_PARAM_PREFIX "kmsan."
module_param_named(panic, panic_on_kmsan, int, 0);
/*
* Skip internal KMSAN frames.
*/
static int get_stack_skipnr(const unsigned long stack_entries[],
int num_entries)
{
int len, skip;
char buf[64];
for (skip = 0; skip < num_entries; ++skip) {
len = scnprintf(buf, sizeof(buf), "%ps",
(void *)stack_entries[skip]);
/* Never show __msan_* or kmsan_* functions. */
if ((strnstr(buf, "__msan_", len) == buf) ||
(strnstr(buf, "kmsan_", len) == buf))
continue;
/*
* No match for runtime functions -- @skip entries to skip to
* get to first frame of interest.
*/
break;
}
return skip;
}
/*
* Currently the descriptions of locals generated by Clang look as follows:
* ----local_name@function_name
* We want to print only the name of the local, as other information in that
* description can be confusing.
* The meaningful part of the description is copied to a global buffer to avoid
* allocating memory.
*/
static char *pretty_descr(char *descr)
{
int pos = 0, len = strlen(descr);
for (int i = 0; i < len; i++) {
if (descr[i] == '@')
break;
if (descr[i] == '-')
continue;
report_local_descr[pos] = descr[i];
if (pos + 1 == DESCR_SIZE)
break;
pos++;
}
report_local_descr[pos] = 0;
return report_local_descr;
}
void kmsan_print_origin(depot_stack_handle_t origin)
{
unsigned long *entries = NULL, *chained_entries = NULL;
unsigned int nr_entries, chained_nr_entries, skipnr;
void *pc1 = NULL, *pc2 = NULL;
depot_stack_handle_t head;
unsigned long magic;
char *descr = NULL;
unsigned int depth;
if (!origin)
return;
while (true) {
nr_entries = stack_depot_fetch(origin, &entries);
depth = kmsan_depth_from_eb(stack_depot_get_extra_bits(origin));
magic = nr_entries ? entries[0] : 0;
if ((nr_entries == 4) && (magic == KMSAN_ALLOCA_MAGIC_ORIGIN)) {
descr = (char *)entries[1];
pc1 = (void *)entries[2];
pc2 = (void *)entries[3];
pr_err("Local variable %s created at:\n",
pretty_descr(descr));
if (pc1)
pr_err(" %pSb\n", pc1);
if (pc2)
pr_err(" %pSb\n", pc2);
break;
}
if ((nr_entries == 3) && (magic == KMSAN_CHAIN_MAGIC_ORIGIN)) {
/*
* Origin chains deeper than KMSAN_MAX_ORIGIN_DEPTH are
* not stored, so the output may be incomplete.
*/
if (depth == KMSAN_MAX_ORIGIN_DEPTH)
pr_err("<Zero or more stacks not recorded to save memory>\n\n");
head = entries[1];
origin = entries[2];
pr_err("Uninit was stored to memory at:\n");
chained_nr_entries =
stack_depot_fetch(head, &chained_entries);
kmsan_internal_unpoison_memory(
chained_entries,
chained_nr_entries * sizeof(*chained_entries),
/*checked*/ false);
skipnr = get_stack_skipnr(chained_entries,
chained_nr_entries);
stack_trace_print(chained_entries + skipnr,
chained_nr_entries - skipnr, 0);
pr_err("\n");
continue;
}
pr_err("Uninit was created at:\n");
if (nr_entries) {
skipnr = get_stack_skipnr(entries, nr_entries);
stack_trace_print(entries + skipnr, nr_entries - skipnr,
0);
} else {
pr_err("(stack is not available)\n");
}
break;
}
}
void kmsan_report(depot_stack_handle_t origin, void *address, int size,
int off_first, int off_last, const void *user_addr,
enum kmsan_bug_reason reason)
{
unsigned long stack_entries[KMSAN_STACK_DEPTH];
int num_stack_entries, skipnr;
char *bug_type = NULL;
unsigned long ua_flags;
bool is_uaf;
if (!kmsan_enabled)
return;
if (!current->kmsan_ctx.allow_reporting)
return;
if (!origin)
return;
current->kmsan_ctx.allow_reporting = false;
ua_flags = user_access_save();
raw_spin_lock(&kmsan_report_lock);
pr_err("=====================================================\n");
is_uaf = kmsan_uaf_from_eb(stack_depot_get_extra_bits(origin));
switch (reason) {
case REASON_ANY:
bug_type = is_uaf ? "use-after-free" : "uninit-value";
break;
case REASON_COPY_TO_USER:
bug_type = is_uaf ? "kernel-infoleak-after-free" :
"kernel-infoleak";
break;
case REASON_SUBMIT_URB:
bug_type = is_uaf ? "kernel-usb-infoleak-after-free" :
"kernel-usb-infoleak";
break;
}
num_stack_entries =
stack_trace_save(stack_entries, KMSAN_STACK_DEPTH, 1);
skipnr = get_stack_skipnr(stack_entries, num_stack_entries);
pr_err("BUG: KMSAN: %s in %pSb\n", bug_type,
(void *)stack_entries[skipnr]);
stack_trace_print(stack_entries + skipnr, num_stack_entries - skipnr,
0);
pr_err("\n");
kmsan_print_origin(origin);
if (size) {
pr_err("\n");
if (off_first == off_last)
pr_err("Byte %d of %d is uninitialized\n", off_first,
size);
else
pr_err("Bytes %d-%d of %d are uninitialized\n",
off_first, off_last, size);
}
if (address)
pr_err("Memory access of size %d starts at %px\n", size,
address);
if (user_addr && reason == REASON_COPY_TO_USER)
pr_err("Data copied to user address %px\n", user_addr);
pr_err("\n");
dump_stack_print_info(KERN_ERR);
pr_err("=====================================================\n");
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
raw_spin_unlock(&kmsan_report_lock);
if (panic_on_kmsan)
panic("kmsan.panic set ...\n");
user_access_restore(ua_flags);
current->kmsan_ctx.allow_reporting = true;
}
|