Blame view

mm/kasan/report.c 9.19 KB
0b24becc8   Andrey Ryabinin   kasan: add kernel...
1
2
3
4
  /*
   * This file contains error reporting code.
   *
   * Copyright (c) 2014 Samsung Electronics Co., Ltd.
2baf9e894   Andrey Ryabinin   .mailmap: Andrey ...
5
   * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
0b24becc8   Andrey Ryabinin   kasan: add kernel...
6
   *
5d0926efe   Andrey Konovalov   kasan: update ref...
7
   * Some code borrowed from https://github.com/xairy/kasan-prototype by
0b24becc8   Andrey Ryabinin   kasan: add kernel...
8
9
10
11
12
13
14
15
16
17
18
19
20
   *        Andrey Konovalov <adech.fo@gmail.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/printk.h>
  #include <linux/sched.h>
  #include <linux/slab.h>
cd11016e5   Alexander Potapenko   mm, kasan: stackd...
21
  #include <linux/stackdepot.h>
0b24becc8   Andrey Ryabinin   kasan: add kernel...
22
23
24
25
  #include <linux/stacktrace.h>
  #include <linux/string.h>
  #include <linux/types.h>
  #include <linux/kasan.h>
527f215b7   Aneesh Kumar K.V   mm/kasan: MODULE_...
26
  #include <linux/module.h>
0b24becc8   Andrey Ryabinin   kasan: add kernel...
27

bebf56a1b   Andrey Ryabinin   kasan: enable ins...
28
  #include <asm/sections.h>
0b24becc8   Andrey Ryabinin   kasan: add kernel...
29
  #include "kasan.h"
0316bec22   Andrey Ryabinin   mm: slub: add ker...
30
  #include "../slab.h"
0b24becc8   Andrey Ryabinin   kasan: add kernel...
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  
  /* Shadow layout customization. */
  #define SHADOW_BYTES_PER_BLOCK 1
  #define SHADOW_BLOCKS_PER_ROW 16
  #define SHADOW_BYTES_PER_ROW (SHADOW_BLOCKS_PER_ROW * SHADOW_BYTES_PER_BLOCK)
  #define SHADOW_ROWS_AROUND_ADDR 2
  
  static const void *find_first_bad_addr(const void *addr, size_t size)
  {
  	u8 shadow_val = *(u8 *)kasan_mem_to_shadow(addr);
  	const void *first_bad_addr = addr;
  
  	while (!shadow_val && first_bad_addr < addr + size) {
  		first_bad_addr += KASAN_SHADOW_SCALE_SIZE;
  		shadow_val = *(u8 *)kasan_mem_to_shadow(first_bad_addr);
  	}
  	return first_bad_addr;
  }
  
  static void print_error_description(struct kasan_access_info *info)
  {
0952d87fd   Andrey Konovalov   kasan: update rep...
52
  	const char *bug_type = "unknown-crash";
cdf6a273d   Andrey Konovalov   kasan: accurately...
53
  	u8 *shadow_addr;
0b24becc8   Andrey Ryabinin   kasan: add kernel...
54
55
56
  
  	info->first_bad_addr = find_first_bad_addr(info->access_addr,
  						info->access_size);
cdf6a273d   Andrey Konovalov   kasan: accurately...
57
  	shadow_addr = (u8 *)kasan_mem_to_shadow(info->first_bad_addr);
0b24becc8   Andrey Ryabinin   kasan: add kernel...
58

cdf6a273d   Andrey Konovalov   kasan: accurately...
59
60
61
62
63
64
65
66
  	/*
  	 * If shadow byte value is in [0, KASAN_SHADOW_SCALE_SIZE) we can look
  	 * at the next shadow byte to determine the type of the bad access.
  	 */
  	if (*shadow_addr > 0 && *shadow_addr <= KASAN_SHADOW_SCALE_SIZE - 1)
  		shadow_addr++;
  
  	switch (*shadow_addr) {
0952d87fd   Andrey Konovalov   kasan: update rep...
67
  	case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
cdf6a273d   Andrey Konovalov   kasan: accurately...
68
69
70
71
  		/*
  		 * In theory it's still possible to see these shadow values
  		 * due to a data race in the kernel code.
  		 */
0952d87fd   Andrey Konovalov   kasan: update rep...
72
  		bug_type = "out-of-bounds";
b8c73fc24   Andrey Ryabinin   mm: page_alloc: a...
73
  		break;
0316bec22   Andrey Ryabinin   mm: slub: add ker...
74
75
  	case KASAN_PAGE_REDZONE:
  	case KASAN_KMALLOC_REDZONE:
0952d87fd   Andrey Konovalov   kasan: update rep...
76
77
  		bug_type = "slab-out-of-bounds";
  		break;
bebf56a1b   Andrey Ryabinin   kasan: enable ins...
78
  	case KASAN_GLOBAL_REDZONE:
0952d87fd   Andrey Konovalov   kasan: update rep...
79
  		bug_type = "global-out-of-bounds";
0b24becc8   Andrey Ryabinin   kasan: add kernel...
80
  		break;
c420f167d   Andrey Ryabinin   kasan: enable sta...
81
82
83
84
  	case KASAN_STACK_LEFT:
  	case KASAN_STACK_MID:
  	case KASAN_STACK_RIGHT:
  	case KASAN_STACK_PARTIAL:
0952d87fd   Andrey Konovalov   kasan: update rep...
85
86
87
88
89
  		bug_type = "stack-out-of-bounds";
  		break;
  	case KASAN_FREE_PAGE:
  	case KASAN_KMALLOC_FREE:
  		bug_type = "use-after-free";
c420f167d   Andrey Ryabinin   kasan: enable sta...
90
  		break;
828347f8f   Dmitry Vyukov   kasan: support us...
91
92
93
  	case KASAN_USE_AFTER_SCOPE:
  		bug_type = "use-after-scope";
  		break;
0b24becc8   Andrey Ryabinin   kasan: add kernel...
94
  	}
25add7ec7   Andrey Konovalov   kasan: update log...
95
96
  	pr_err("BUG: KASAN: %s in %pS at addr %p
  ",
0b24becc8   Andrey Ryabinin   kasan: add kernel...
97
98
99
100
101
102
103
  		bug_type, (void *)info->ip,
  		info->access_addr);
  	pr_err("%s of size %zu by task %s/%d
  ",
  		info->is_write ? "Write" : "Read",
  		info->access_size, current->comm, task_pid_nr(current));
  }
bebf56a1b   Andrey Ryabinin   kasan: enable ins...
104
105
  static inline bool kernel_or_module_addr(const void *addr)
  {
527f215b7   Aneesh Kumar K.V   mm/kasan: MODULE_...
106
107
108
109
110
  	if (addr >= (void *)_stext && addr < (void *)_end)
  		return true;
  	if (is_module_address((unsigned long)addr))
  		return true;
  	return false;
bebf56a1b   Andrey Ryabinin   kasan: enable ins...
111
112
113
114
115
116
117
118
  }
  
  static inline bool init_task_stack_addr(const void *addr)
  {
  	return addr >= (void *)&init_thread_union.stack &&
  		(addr <= (void *)&init_thread_union.stack +
  			sizeof(init_thread_union.stack));
  }
7e0889789   Andrey Ryabinin   kasan: improve do...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  static DEFINE_SPINLOCK(report_lock);
  
  static void kasan_start_report(unsigned long *flags)
  {
  	/*
  	 * Make sure we don't end up in loop.
  	 */
  	kasan_disable_current();
  	spin_lock_irqsave(&report_lock, *flags);
  	pr_err("==================================================================
  ");
  }
  
  static void kasan_end_report(unsigned long *flags)
  {
  	pr_err("==================================================================
  ");
  	add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
  	spin_unlock_irqrestore(&report_lock, *flags);
  	kasan_enable_current();
  }
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
140
141
  static void print_track(struct kasan_track *track)
  {
cd11016e5   Alexander Potapenko   mm, kasan: stackd...
142
143
144
145
146
147
148
149
150
151
152
  	pr_err("PID = %u
  ", track->pid);
  	if (track->stack) {
  		struct stack_trace trace;
  
  		depot_fetch_stack(track->stack, &trace);
  		print_stack_trace(&trace, 0);
  	} else {
  		pr_err("(stack is not available)
  ");
  	}
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
153
  }
7e0889789   Andrey Ryabinin   kasan: improve do...
154
  static void kasan_object_err(struct kmem_cache *cache, void *object)
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
155
156
  {
  	struct kasan_alloc_meta *alloc_info = get_alloc_info(cache, object);
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
157
158
  
  	dump_stack();
47b5c2a0f   Andrey Ryabinin   mm/kasan: get rid...
159
160
161
  	pr_err("Object at %p, in cache %s size: %d
  ", object, cache->name,
  		cache->object_size);
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
162
163
  	if (!(cache->flags & SLAB_KASAN))
  		return;
b3cbd9bf7   Andrey Ryabinin   mm/kasan: get rid...
164
165
166
167
168
169
170
  
  	pr_err("Allocated:
  ");
  	print_track(&alloc_info->alloc_track);
  	pr_err("Freed:
  ");
  	print_track(&alloc_info->free_track);
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
171
  }
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
172

7e0889789   Andrey Ryabinin   kasan: improve do...
173
174
175
176
177
178
179
180
181
182
183
184
185
  void kasan_report_double_free(struct kmem_cache *cache, void *object,
  			s8 shadow)
  {
  	unsigned long flags;
  
  	kasan_start_report(&flags);
  	pr_err("BUG: Double free or freeing an invalid pointer
  ");
  	pr_err("Unexpected shadow byte: 0x%hhX
  ", shadow);
  	kasan_object_err(cache, object);
  	kasan_end_report(&flags);
  }
0b24becc8   Andrey Ryabinin   kasan: add kernel...
186
187
  static void print_address_description(struct kasan_access_info *info)
  {
b8c73fc24   Andrey Ryabinin   mm: page_alloc: a...
188
189
190
191
192
  	const void *addr = info->access_addr;
  
  	if ((addr >= (void *)PAGE_OFFSET) &&
  		(addr < high_memory)) {
  		struct page *page = virt_to_head_page(addr);
0316bec22   Andrey Ryabinin   mm: slub: add ker...
193
194
195
196
  
  		if (PageSlab(page)) {
  			void *object;
  			struct kmem_cache *cache = page->slab_cache;
7ed2f9e66   Alexander Potapenko   mm, kasan: SLAB s...
197
198
  			object = nearest_obj(cache, page,
  						(void *)info->access_addr);
7e0889789   Andrey Ryabinin   kasan: improve do...
199
  			kasan_object_err(cache, object);
0316bec22   Andrey Ryabinin   mm: slub: add ker...
200
201
  			return;
  		}
b8c73fc24   Andrey Ryabinin   mm: page_alloc: a...
202
203
  		dump_page(page, "kasan: bad access detected");
  	}
bebf56a1b   Andrey Ryabinin   kasan: enable ins...
204
205
206
207
208
  	if (kernel_or_module_addr(addr)) {
  		if (!init_task_stack_addr(addr))
  			pr_err("Address belongs to variable %pS
  ", addr);
  	}
0b24becc8   Andrey Ryabinin   kasan: add kernel...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  	dump_stack();
  }
  
  static bool row_is_guilty(const void *row, const void *guilty)
  {
  	return (row <= guilty) && (guilty < row + SHADOW_BYTES_PER_ROW);
  }
  
  static int shadow_pointer_offset(const void *row, const void *shadow)
  {
  	/* The length of ">ff00ff00ff00ff00: " is
  	 *    3 + (BITS_PER_LONG/8)*2 chars.
  	 */
  	return 3 + (BITS_PER_LONG/8)*2 + (shadow - row)*2 +
  		(shadow - row) / SHADOW_BYTES_PER_BLOCK + 1;
  }
  
  static void print_shadow_for_address(const void *addr)
  {
  	int i;
  	const void *shadow = kasan_mem_to_shadow(addr);
  	const void *shadow_row;
  
  	shadow_row = (void *)round_down((unsigned long)shadow,
  					SHADOW_BYTES_PER_ROW)
  		- SHADOW_ROWS_AROUND_ADDR * SHADOW_BYTES_PER_ROW;
  
  	pr_err("Memory state around the buggy address:
  ");
  
  	for (i = -SHADOW_ROWS_AROUND_ADDR; i <= SHADOW_ROWS_AROUND_ADDR; i++) {
  		const void *kaddr = kasan_shadow_to_mem(shadow_row);
  		char buffer[4 + (BITS_PER_LONG/8)*2];
f2377d4ea   Aneesh Kumar K.V   mm/kasan: don't u...
242
  		char shadow_buf[SHADOW_BYTES_PER_ROW];
0b24becc8   Andrey Ryabinin   kasan: add kernel...
243
244
245
  
  		snprintf(buffer, sizeof(buffer),
  			(i == 0) ? ">%p: " : " %p: ", kaddr);
f2377d4ea   Aneesh Kumar K.V   mm/kasan: don't u...
246
247
248
249
250
  		/*
  		 * We should not pass a shadow pointer to generic
  		 * function, because generic functions may try to
  		 * access kasan mapping for the passed address.
  		 */
f2377d4ea   Aneesh Kumar K.V   mm/kasan: don't u...
251
  		memcpy(shadow_buf, shadow_row, SHADOW_BYTES_PER_ROW);
0b24becc8   Andrey Ryabinin   kasan: add kernel...
252
253
  		print_hex_dump(KERN_ERR, buffer,
  			DUMP_PREFIX_NONE, SHADOW_BYTES_PER_ROW, 1,
f2377d4ea   Aneesh Kumar K.V   mm/kasan: don't u...
254
  			shadow_buf, SHADOW_BYTES_PER_ROW, 0);
0b24becc8   Andrey Ryabinin   kasan: add kernel...
255
256
257
258
259
260
261
262
263
264
  
  		if (row_is_guilty(shadow_row, shadow))
  			pr_err("%*c
  ",
  				shadow_pointer_offset(shadow_row, shadow),
  				'^');
  
  		shadow_row += SHADOW_BYTES_PER_ROW;
  	}
  }
e91210766   Andrey Konovalov   kasan: update rep...
265
  static void kasan_report_error(struct kasan_access_info *info)
0b24becc8   Andrey Ryabinin   kasan: add kernel...
266
267
  {
  	unsigned long flags;
e91210766   Andrey Konovalov   kasan: update rep...
268
  	const char *bug_type;
0b24becc8   Andrey Ryabinin   kasan: add kernel...
269

7e0889789   Andrey Ryabinin   kasan: improve do...
270
  	kasan_start_report(&flags);
e91210766   Andrey Konovalov   kasan: update rep...
271
272
273
274
275
276
277
278
  	if (info->access_addr <
  			kasan_shadow_to_mem((void *)KASAN_SHADOW_START)) {
  		if ((unsigned long)info->access_addr < PAGE_SIZE)
  			bug_type = "null-ptr-deref";
  		else if ((unsigned long)info->access_addr < TASK_SIZE)
  			bug_type = "user-memory-access";
  		else
  			bug_type = "wild-memory-access";
25add7ec7   Andrey Konovalov   kasan: update log...
279
280
  		pr_err("BUG: KASAN: %s on address %p
  ",
e91210766   Andrey Konovalov   kasan: update rep...
281
282
283
284
285
286
287
288
289
290
291
292
  			bug_type, info->access_addr);
  		pr_err("%s of size %zu by task %s/%d
  ",
  			info->is_write ? "Write" : "Read",
  			info->access_size, current->comm,
  			task_pid_nr(current));
  		dump_stack();
  	} else {
  		print_error_description(info);
  		print_address_description(info);
  		print_shadow_for_address(info->first_bad_addr);
  	}
7e0889789   Andrey Ryabinin   kasan: improve do...
293
294
  
  	kasan_end_report(&flags);
0b24becc8   Andrey Ryabinin   kasan: add kernel...
295
296
297
298
299
300
  }
  
  void kasan_report(unsigned long addr, size_t size,
  		bool is_write, unsigned long ip)
  {
  	struct kasan_access_info info;
0ba8663cb   Aneesh Kumar K.V   mm/kasan: rename ...
301
  	if (likely(!kasan_report_enabled()))
0b24becc8   Andrey Ryabinin   kasan: add kernel...
302
303
304
305
306
307
  		return;
  
  	info.access_addr = (void *)addr;
  	info.access_size = size;
  	info.is_write = is_write;
  	info.ip = ip;
e91210766   Andrey Konovalov   kasan: update rep...
308

0b24becc8   Andrey Ryabinin   kasan: add kernel...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  	kasan_report_error(&info);
  }
  
  
  #define DEFINE_ASAN_REPORT_LOAD(size)                     \
  void __asan_report_load##size##_noabort(unsigned long addr) \
  {                                                         \
  	kasan_report(addr, size, false, _RET_IP_);	  \
  }                                                         \
  EXPORT_SYMBOL(__asan_report_load##size##_noabort)
  
  #define DEFINE_ASAN_REPORT_STORE(size)                     \
  void __asan_report_store##size##_noabort(unsigned long addr) \
  {                                                          \
  	kasan_report(addr, size, true, _RET_IP_);	   \
  }                                                          \
  EXPORT_SYMBOL(__asan_report_store##size##_noabort)
  
  DEFINE_ASAN_REPORT_LOAD(1);
  DEFINE_ASAN_REPORT_LOAD(2);
  DEFINE_ASAN_REPORT_LOAD(4);
  DEFINE_ASAN_REPORT_LOAD(8);
  DEFINE_ASAN_REPORT_LOAD(16);
  DEFINE_ASAN_REPORT_STORE(1);
  DEFINE_ASAN_REPORT_STORE(2);
  DEFINE_ASAN_REPORT_STORE(4);
  DEFINE_ASAN_REPORT_STORE(8);
  DEFINE_ASAN_REPORT_STORE(16);
  
  void __asan_report_load_n_noabort(unsigned long addr, size_t size)
  {
  	kasan_report(addr, size, false, _RET_IP_);
  }
  EXPORT_SYMBOL(__asan_report_load_n_noabort);
  
  void __asan_report_store_n_noabort(unsigned long addr, size_t size)
  {
  	kasan_report(addr, size, true, _RET_IP_);
  }
  EXPORT_SYMBOL(__asan_report_store_n_noabort);