aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorAndrey Ryabinin <a.ryabinin@samsung.com>2015-02-13 17:39:17 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-14 00:21:40 -0500
commit0b24becc810dc3be6e3f94103a866f214c282394 (patch)
treead4ca40742a383f4b9e7385cc7f2506eee1aa24b /mm
parentcb4188ac8e5779f66b9f55888ac2c75b391cde44 (diff)
kasan: add kernel address sanitizer infrastructure
Kernel Address sanitizer (KASan) is a dynamic memory error detector. It provides fast and comprehensive solution for finding use-after-free and out-of-bounds bugs. KASAN uses compile-time instrumentation for checking every memory access, therefore GCC > v4.9.2 required. v4.9.2 almost works, but has issues with putting symbol aliases into the wrong section, which breaks kasan instrumentation of globals. This patch only adds infrastructure for kernel address sanitizer. It's not available for use yet. The idea and some code was borrowed from [1]. Basic idea: The main idea of KASAN is to use shadow memory to record whether each byte of memory is safe to access or not, and use compiler's instrumentation to check the shadow memory on each memory access. Address sanitizer uses 1/8 of the memory addressable in kernel for shadow memory and uses direct mapping with a scale and offset to translate a memory address to its corresponding shadow address. Here is function to translate address to corresponding shadow address: unsigned long kasan_mem_to_shadow(unsigned long addr) { return (addr >> KASAN_SHADOW_SCALE_SHIFT) + KASAN_SHADOW_OFFSET; } where KASAN_SHADOW_SCALE_SHIFT = 3. So for every 8 bytes there is one corresponding byte of shadow memory. The following encoding used for each shadow byte: 0 means that all 8 bytes of the corresponding memory region are valid for access; k (1 <= k <= 7) means that the first k bytes are valid for access, and other (8 - k) bytes are not; Any negative value indicates that the entire 8-bytes are inaccessible. Different negative values used to distinguish between different kinds of inaccessible memory (redzones, freed memory) (see mm/kasan/kasan.h). To be able to detect accesses to bad memory we need a special compiler. Such compiler inserts a specific function calls (__asan_load*(addr), __asan_store*(addr)) before each memory access of size 1, 2, 4, 8 or 16. These functions check whether memory region is valid to access or not by checking corresponding shadow memory. If access is not valid an error printed. Historical background of the address sanitizer from Dmitry Vyukov: "We've developed the set of tools, AddressSanitizer (Asan), ThreadSanitizer and MemorySanitizer, for user space. We actively use them for testing inside of Google (continuous testing, fuzzing, running prod services). To date the tools have found more than 10'000 scary bugs in Chromium, Google internal codebase and various open-source projects (Firefox, OpenSSL, gcc, clang, ffmpeg, MySQL and lots of others): [2] [3] [4]. The tools are part of both gcc and clang compilers. We have not yet done massive testing under the Kernel AddressSanitizer (it's kind of chicken and egg problem, you need it to be upstream to start applying it extensively). To date it has found about 50 bugs. Bugs that we've found in upstream kernel are listed in [5]. We've also found ~20 bugs in out internal version of the kernel. Also people from Samsung and Oracle have found some. [...] As others noted, the main feature of AddressSanitizer is its performance due to inline compiler instrumentation and simple linear shadow memory. User-space Asan has ~2x slowdown on computational programs and ~2x memory consumption increase. Taking into account that kernel usually consumes only small fraction of CPU and memory when running real user-space programs, I would expect that kernel Asan will have ~10-30% slowdown and similar memory consumption increase (when we finish all tuning). I agree that Asan can well replace kmemcheck. We have plans to start working on Kernel MemorySanitizer that finds uses of unitialized memory. Asan+Msan will provide feature-parity with kmemcheck. As others noted, Asan will unlikely replace debug slab and pagealloc that can be enabled at runtime. Asan uses compiler instrumentation, so even if it is disabled, it still incurs visible overheads. Asan technology is easily portable to other architectures. Compiler instrumentation is fully portable. Runtime has some arch-dependent parts like shadow mapping and atomic operation interception. They are relatively easy to port." Comparison with other debugging features: ======================================== KMEMCHECK: - KASan can do almost everything that kmemcheck can. KASan uses compile-time instrumentation, which makes it significantly faster than kmemcheck. The only advantage of kmemcheck over KASan is detection of uninitialized memory reads. Some brief performance testing showed that kasan could be x500-x600 times faster than kmemcheck: $ netperf -l 30 MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to localhost (127.0.0.1) port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec no debug: 87380 16384 16384 30.00 41624.72 kasan inline: 87380 16384 16384 30.00 12870.54 kasan outline: 87380 16384 16384 30.00 10586.39 kmemcheck: 87380 16384 16384 30.03 20.23 - Also kmemcheck couldn't work on several CPUs. It always sets number of CPUs to 1. KASan doesn't have such limitation. DEBUG_PAGEALLOC: - KASan is slower than DEBUG_PAGEALLOC, but KASan works on sub-page granularity level, so it able to find more bugs. SLUB_DEBUG (poisoning, redzones): - SLUB_DEBUG has lower overhead than KASan. - SLUB_DEBUG in most cases are not able to detect bad reads, KASan able to detect both reads and writes. - In some cases (e.g. redzone overwritten) SLUB_DEBUG detect bugs only on allocation/freeing of object. KASan catch bugs right before it will happen, so we always know exact place of first bad read/write. [1] https://code.google.com/p/address-sanitizer/wiki/AddressSanitizerForKernel [2] https://code.google.com/p/address-sanitizer/wiki/FoundBugs [3] https://code.google.com/p/thread-sanitizer/wiki/FoundBugs [4] https://code.google.com/p/memory-sanitizer/wiki/FoundBugs [5] https://code.google.com/p/address-sanitizer/wiki/AddressSanitizerForKernel#Trophies Based on work by Andrey Konovalov. Signed-off-by: Andrey Ryabinin <a.ryabinin@samsung.com> Acked-by: Michal Marek <mmarek@suse.cz> Signed-off-by: Andrey Konovalov <adech.fo@gmail.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Konstantin Serebryany <kcc@google.com> Cc: Dmitry Chernenkov <dmitryc@google.com> Cc: Yuri Gribov <tetra2005@gmail.com> Cc: Konstantin Khlebnikov <koct9i@gmail.com> Cc: Sasha Levin <sasha.levin@oracle.com> Cc: Christoph Lameter <cl@linux.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Andi Kleen <andi@firstfloor.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Christoph Lameter <cl@linux.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Stephen Rothwell <sfr@canb.auug.org.au> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/Makefile1
-rw-r--r--mm/kasan/Makefile8
-rw-r--r--mm/kasan/kasan.c302
-rw-r--r--mm/kasan/kasan.h34
-rw-r--r--mm/kasan/report.c209
5 files changed, 554 insertions, 0 deletions
diff --git a/mm/Makefile b/mm/Makefile
index 3548460ab7b6..930b52df4aca 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_PAGE_POISONING) += debug-pagealloc.o
49obj-$(CONFIG_SLAB) += slab.o 49obj-$(CONFIG_SLAB) += slab.o
50obj-$(CONFIG_SLUB) += slub.o 50obj-$(CONFIG_SLUB) += slub.o
51obj-$(CONFIG_KMEMCHECK) += kmemcheck.o 51obj-$(CONFIG_KMEMCHECK) += kmemcheck.o
52obj-$(CONFIG_KASAN) += kasan/
52obj-$(CONFIG_FAILSLAB) += failslab.o 53obj-$(CONFIG_FAILSLAB) += failslab.o
53obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o 54obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o
54obj-$(CONFIG_FS_XIP) += filemap_xip.o 55obj-$(CONFIG_FS_XIP) += filemap_xip.o
diff --git a/mm/kasan/Makefile b/mm/kasan/Makefile
new file mode 100644
index 000000000000..bd837b8c2f41
--- /dev/null
+++ b/mm/kasan/Makefile
@@ -0,0 +1,8 @@
1KASAN_SANITIZE := n
2
3CFLAGS_REMOVE_kasan.o = -pg
4# Function splitter causes unnecessary splits in __asan_load1/__asan_store1
5# see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533
6CFLAGS_kasan.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector)
7
8obj-y := kasan.o report.o
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
new file mode 100644
index 000000000000..6dc1aa7cefcc
--- /dev/null
+++ b/mm/kasan/kasan.c
@@ -0,0 +1,302 @@
1/*
2 * This file contains shadow memory manipulation code.
3 *
4 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
5 * Author: Andrey Ryabinin <a.ryabinin@samsung.com>
6 *
7 * Some of code borrowed from https://github.com/xairy/linux by
8 * Andrey Konovalov <adech.fo@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 */
15
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17#define DISABLE_BRANCH_PROFILING
18
19#include <linux/export.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/memblock.h>
23#include <linux/mm.h>
24#include <linux/printk.h>
25#include <linux/sched.h>
26#include <linux/slab.h>
27#include <linux/stacktrace.h>
28#include <linux/string.h>
29#include <linux/types.h>
30#include <linux/kasan.h>
31
32#include "kasan.h"
33
34/*
35 * Poisons the shadow memory for 'size' bytes starting from 'addr'.
36 * Memory addresses should be aligned to KASAN_SHADOW_SCALE_SIZE.
37 */
38static void kasan_poison_shadow(const void *address, size_t size, u8 value)
39{
40 void *shadow_start, *shadow_end;
41
42 shadow_start = kasan_mem_to_shadow(address);
43 shadow_end = kasan_mem_to_shadow(address + size);
44
45 memset(shadow_start, value, shadow_end - shadow_start);
46}
47
48void kasan_unpoison_shadow(const void *address, size_t size)
49{
50 kasan_poison_shadow(address, size, 0);
51
52 if (size & KASAN_SHADOW_MASK) {
53 u8 *shadow = (u8 *)kasan_mem_to_shadow(address + size);
54 *shadow = size & KASAN_SHADOW_MASK;
55 }
56}
57
58
59/*
60 * All functions below always inlined so compiler could
61 * perform better optimizations in each of __asan_loadX/__assn_storeX
62 * depending on memory access size X.
63 */
64
65static __always_inline bool memory_is_poisoned_1(unsigned long addr)
66{
67 s8 shadow_value = *(s8 *)kasan_mem_to_shadow((void *)addr);
68
69 if (unlikely(shadow_value)) {
70 s8 last_accessible_byte = addr & KASAN_SHADOW_MASK;
71 return unlikely(last_accessible_byte >= shadow_value);
72 }
73
74 return false;
75}
76
77static __always_inline bool memory_is_poisoned_2(unsigned long addr)
78{
79 u16 *shadow_addr = (u16 *)kasan_mem_to_shadow((void *)addr);
80
81 if (unlikely(*shadow_addr)) {
82 if (memory_is_poisoned_1(addr + 1))
83 return true;
84
85 if (likely(((addr + 1) & KASAN_SHADOW_MASK) != 0))
86 return false;
87
88 return unlikely(*(u8 *)shadow_addr);
89 }
90
91 return false;
92}
93
94static __always_inline bool memory_is_poisoned_4(unsigned long addr)
95{
96 u16 *shadow_addr = (u16 *)kasan_mem_to_shadow((void *)addr);
97
98 if (unlikely(*shadow_addr)) {
99 if (memory_is_poisoned_1(addr + 3))
100 return true;
101
102 if (likely(((addr + 3) & KASAN_SHADOW_MASK) >= 3))
103 return false;
104
105 return unlikely(*(u8 *)shadow_addr);
106 }
107
108 return false;
109}
110
111static __always_inline bool memory_is_poisoned_8(unsigned long addr)
112{
113 u16 *shadow_addr = (u16 *)kasan_mem_to_shadow((void *)addr);
114
115 if (unlikely(*shadow_addr)) {
116 if (memory_is_poisoned_1(addr + 7))
117 return true;
118
119 if (likely(((addr + 7) & KASAN_SHADOW_MASK) >= 7))
120 return false;
121
122 return unlikely(*(u8 *)shadow_addr);
123 }
124
125 return false;
126}
127
128static __always_inline bool memory_is_poisoned_16(unsigned long addr)
129{
130 u32 *shadow_addr = (u32 *)kasan_mem_to_shadow((void *)addr);
131
132 if (unlikely(*shadow_addr)) {
133 u16 shadow_first_bytes = *(u16 *)shadow_addr;
134 s8 last_byte = (addr + 15) & KASAN_SHADOW_MASK;
135
136 if (unlikely(shadow_first_bytes))
137 return true;
138
139 if (likely(!last_byte))
140 return false;
141
142 return memory_is_poisoned_1(addr + 15);
143 }
144
145 return false;
146}
147
148static __always_inline unsigned long bytes_is_zero(const u8 *start,
149 size_t size)
150{
151 while (size) {
152 if (unlikely(*start))
153 return (unsigned long)start;
154 start++;
155 size--;
156 }
157
158 return 0;
159}
160
161static __always_inline unsigned long memory_is_zero(const void *start,
162 const void *end)
163{
164 unsigned int words;
165 unsigned long ret;
166 unsigned int prefix = (unsigned long)start % 8;
167
168 if (end - start <= 16)
169 return bytes_is_zero(start, end - start);
170
171 if (prefix) {
172 prefix = 8 - prefix;
173 ret = bytes_is_zero(start, prefix);
174 if (unlikely(ret))
175 return ret;
176 start += prefix;
177 }
178
179 words = (end - start) / 8;
180 while (words) {
181 if (unlikely(*(u64 *)start))
182 return bytes_is_zero(start, 8);
183 start += 8;
184 words--;
185 }
186
187 return bytes_is_zero(start, (end - start) % 8);
188}
189
190static __always_inline bool memory_is_poisoned_n(unsigned long addr,
191 size_t size)
192{
193 unsigned long ret;
194
195 ret = memory_is_zero(kasan_mem_to_shadow((void *)addr),
196 kasan_mem_to_shadow((void *)addr + size - 1) + 1);
197
198 if (unlikely(ret)) {
199 unsigned long last_byte = addr + size - 1;
200 s8 *last_shadow = (s8 *)kasan_mem_to_shadow((void *)last_byte);
201
202 if (unlikely(ret != (unsigned long)last_shadow ||
203 ((last_byte & KASAN_SHADOW_MASK) >= *last_shadow)))
204 return true;
205 }
206 return false;
207}
208
209static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
210{
211 if (__builtin_constant_p(size)) {
212 switch (size) {
213 case 1:
214 return memory_is_poisoned_1(addr);
215 case 2:
216 return memory_is_poisoned_2(addr);
217 case 4:
218 return memory_is_poisoned_4(addr);
219 case 8:
220 return memory_is_poisoned_8(addr);
221 case 16:
222 return memory_is_poisoned_16(addr);
223 default:
224 BUILD_BUG();
225 }
226 }
227
228 return memory_is_poisoned_n(addr, size);
229}
230
231
232static __always_inline void check_memory_region(unsigned long addr,
233 size_t size, bool write)
234{
235 struct kasan_access_info info;
236
237 if (unlikely(size == 0))
238 return;
239
240 if (unlikely((void *)addr <
241 kasan_shadow_to_mem((void *)KASAN_SHADOW_START))) {
242 info.access_addr = (void *)addr;
243 info.access_size = size;
244 info.is_write = write;
245 info.ip = _RET_IP_;
246 kasan_report_user_access(&info);
247 return;
248 }
249
250 if (likely(!memory_is_poisoned(addr, size)))
251 return;
252
253 kasan_report(addr, size, write, _RET_IP_);
254}
255
256#define DEFINE_ASAN_LOAD_STORE(size) \
257 void __asan_load##size(unsigned long addr) \
258 { \
259 check_memory_region(addr, size, false); \
260 } \
261 EXPORT_SYMBOL(__asan_load##size); \
262 __alias(__asan_load##size) \
263 void __asan_load##size##_noabort(unsigned long); \
264 EXPORT_SYMBOL(__asan_load##size##_noabort); \
265 void __asan_store##size(unsigned long addr) \
266 { \
267 check_memory_region(addr, size, true); \
268 } \
269 EXPORT_SYMBOL(__asan_store##size); \
270 __alias(__asan_store##size) \
271 void __asan_store##size##_noabort(unsigned long); \
272 EXPORT_SYMBOL(__asan_store##size##_noabort)
273
274DEFINE_ASAN_LOAD_STORE(1);
275DEFINE_ASAN_LOAD_STORE(2);
276DEFINE_ASAN_LOAD_STORE(4);
277DEFINE_ASAN_LOAD_STORE(8);
278DEFINE_ASAN_LOAD_STORE(16);
279
280void __asan_loadN(unsigned long addr, size_t size)
281{
282 check_memory_region(addr, size, false);
283}
284EXPORT_SYMBOL(__asan_loadN);
285
286__alias(__asan_loadN)
287void __asan_loadN_noabort(unsigned long, size_t);
288EXPORT_SYMBOL(__asan_loadN_noabort);
289
290void __asan_storeN(unsigned long addr, size_t size)
291{
292 check_memory_region(addr, size, true);
293}
294EXPORT_SYMBOL(__asan_storeN);
295
296__alias(__asan_storeN)
297void __asan_storeN_noabort(unsigned long, size_t);
298EXPORT_SYMBOL(__asan_storeN_noabort);
299
300/* to shut up compiler complaints */
301void __asan_handle_no_return(void) {}
302EXPORT_SYMBOL(__asan_handle_no_return);
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
new file mode 100644
index 000000000000..648b9c006f3f
--- /dev/null
+++ b/mm/kasan/kasan.h
@@ -0,0 +1,34 @@
1#ifndef __MM_KASAN_KASAN_H
2#define __MM_KASAN_KASAN_H
3
4#include <linux/kasan.h>
5
6#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
7#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1)
8
9struct kasan_access_info {
10 const void *access_addr;
11 const void *first_bad_addr;
12 size_t access_size;
13 bool is_write;
14 unsigned long ip;
15};
16
17void kasan_report_error(struct kasan_access_info *info);
18void kasan_report_user_access(struct kasan_access_info *info);
19
20static inline const void *kasan_shadow_to_mem(const void *shadow_addr)
21{
22 return (void *)(((unsigned long)shadow_addr - KASAN_SHADOW_OFFSET)
23 << KASAN_SHADOW_SCALE_SHIFT);
24}
25
26static inline bool kasan_enabled(void)
27{
28 return !current->kasan_depth;
29}
30
31void kasan_report(unsigned long addr, size_t size,
32 bool is_write, unsigned long ip);
33
34#endif
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
new file mode 100644
index 000000000000..5835d69563f5
--- /dev/null
+++ b/mm/kasan/report.c
@@ -0,0 +1,209 @@
1/*
2 * This file contains error reporting code.
3 *
4 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
5 * Author: Andrey Ryabinin <a.ryabinin@samsung.com>
6 *
7 * Some of code borrowed from https://github.com/xairy/linux by
8 * Andrey Konovalov <adech.fo@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 */
15
16#include <linux/kernel.h>
17#include <linux/mm.h>
18#include <linux/printk.h>
19#include <linux/sched.h>
20#include <linux/slab.h>
21#include <linux/stacktrace.h>
22#include <linux/string.h>
23#include <linux/types.h>
24#include <linux/kasan.h>
25
26#include "kasan.h"
27
28/* Shadow layout customization. */
29#define SHADOW_BYTES_PER_BLOCK 1
30#define SHADOW_BLOCKS_PER_ROW 16
31#define SHADOW_BYTES_PER_ROW (SHADOW_BLOCKS_PER_ROW * SHADOW_BYTES_PER_BLOCK)
32#define SHADOW_ROWS_AROUND_ADDR 2
33
34static const void *find_first_bad_addr(const void *addr, size_t size)
35{
36 u8 shadow_val = *(u8 *)kasan_mem_to_shadow(addr);
37 const void *first_bad_addr = addr;
38
39 while (!shadow_val && first_bad_addr < addr + size) {
40 first_bad_addr += KASAN_SHADOW_SCALE_SIZE;
41 shadow_val = *(u8 *)kasan_mem_to_shadow(first_bad_addr);
42 }
43 return first_bad_addr;
44}
45
46static void print_error_description(struct kasan_access_info *info)
47{
48 const char *bug_type = "unknown crash";
49 u8 shadow_val;
50
51 info->first_bad_addr = find_first_bad_addr(info->access_addr,
52 info->access_size);
53
54 shadow_val = *(u8 *)kasan_mem_to_shadow(info->first_bad_addr);
55
56 switch (shadow_val) {
57 case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
58 bug_type = "out of bounds access";
59 break;
60 }
61
62 pr_err("BUG: KASan: %s in %pS at addr %p\n",
63 bug_type, (void *)info->ip,
64 info->access_addr);
65 pr_err("%s of size %zu by task %s/%d\n",
66 info->is_write ? "Write" : "Read",
67 info->access_size, current->comm, task_pid_nr(current));
68}
69
70static void print_address_description(struct kasan_access_info *info)
71{
72 dump_stack();
73}
74
75static bool row_is_guilty(const void *row, const void *guilty)
76{
77 return (row <= guilty) && (guilty < row + SHADOW_BYTES_PER_ROW);
78}
79
80static int shadow_pointer_offset(const void *row, const void *shadow)
81{
82 /* The length of ">ff00ff00ff00ff00: " is
83 * 3 + (BITS_PER_LONG/8)*2 chars.
84 */
85 return 3 + (BITS_PER_LONG/8)*2 + (shadow - row)*2 +
86 (shadow - row) / SHADOW_BYTES_PER_BLOCK + 1;
87}
88
89static void print_shadow_for_address(const void *addr)
90{
91 int i;
92 const void *shadow = kasan_mem_to_shadow(addr);
93 const void *shadow_row;
94
95 shadow_row = (void *)round_down((unsigned long)shadow,
96 SHADOW_BYTES_PER_ROW)
97 - SHADOW_ROWS_AROUND_ADDR * SHADOW_BYTES_PER_ROW;
98
99 pr_err("Memory state around the buggy address:\n");
100
101 for (i = -SHADOW_ROWS_AROUND_ADDR; i <= SHADOW_ROWS_AROUND_ADDR; i++) {
102 const void *kaddr = kasan_shadow_to_mem(shadow_row);
103 char buffer[4 + (BITS_PER_LONG/8)*2];
104
105 snprintf(buffer, sizeof(buffer),
106 (i == 0) ? ">%p: " : " %p: ", kaddr);
107
108 kasan_disable_current();
109 print_hex_dump(KERN_ERR, buffer,
110 DUMP_PREFIX_NONE, SHADOW_BYTES_PER_ROW, 1,
111 shadow_row, SHADOW_BYTES_PER_ROW, 0);
112 kasan_enable_current();
113
114 if (row_is_guilty(shadow_row, shadow))
115 pr_err("%*c\n",
116 shadow_pointer_offset(shadow_row, shadow),
117 '^');
118
119 shadow_row += SHADOW_BYTES_PER_ROW;
120 }
121}
122
123static DEFINE_SPINLOCK(report_lock);
124
125void kasan_report_error(struct kasan_access_info *info)
126{
127 unsigned long flags;
128
129 spin_lock_irqsave(&report_lock, flags);
130 pr_err("================================="
131 "=================================\n");
132 print_error_description(info);
133 print_address_description(info);
134 print_shadow_for_address(info->first_bad_addr);
135 pr_err("================================="
136 "=================================\n");
137 spin_unlock_irqrestore(&report_lock, flags);
138}
139
140void kasan_report_user_access(struct kasan_access_info *info)
141{
142 unsigned long flags;
143
144 spin_lock_irqsave(&report_lock, flags);
145 pr_err("================================="
146 "=================================\n");
147 pr_err("BUG: KASan: user-memory-access on address %p\n",
148 info->access_addr);
149 pr_err("%s of size %zu by task %s/%d\n",
150 info->is_write ? "Write" : "Read",
151 info->access_size, current->comm, task_pid_nr(current));
152 dump_stack();
153 pr_err("================================="
154 "=================================\n");
155 spin_unlock_irqrestore(&report_lock, flags);
156}
157
158void kasan_report(unsigned long addr, size_t size,
159 bool is_write, unsigned long ip)
160{
161 struct kasan_access_info info;
162
163 if (likely(!kasan_enabled()))
164 return;
165
166 info.access_addr = (void *)addr;
167 info.access_size = size;
168 info.is_write = is_write;
169 info.ip = ip;
170 kasan_report_error(&info);
171}
172
173
174#define DEFINE_ASAN_REPORT_LOAD(size) \
175void __asan_report_load##size##_noabort(unsigned long addr) \
176{ \
177 kasan_report(addr, size, false, _RET_IP_); \
178} \
179EXPORT_SYMBOL(__asan_report_load##size##_noabort)
180
181#define DEFINE_ASAN_REPORT_STORE(size) \
182void __asan_report_store##size##_noabort(unsigned long addr) \
183{ \
184 kasan_report(addr, size, true, _RET_IP_); \
185} \
186EXPORT_SYMBOL(__asan_report_store##size##_noabort)
187
188DEFINE_ASAN_REPORT_LOAD(1);
189DEFINE_ASAN_REPORT_LOAD(2);
190DEFINE_ASAN_REPORT_LOAD(4);
191DEFINE_ASAN_REPORT_LOAD(8);
192DEFINE_ASAN_REPORT_LOAD(16);
193DEFINE_ASAN_REPORT_STORE(1);
194DEFINE_ASAN_REPORT_STORE(2);
195DEFINE_ASAN_REPORT_STORE(4);
196DEFINE_ASAN_REPORT_STORE(8);
197DEFINE_ASAN_REPORT_STORE(16);
198
199void __asan_report_load_n_noabort(unsigned long addr, size_t size)
200{
201 kasan_report(addr, size, false, _RET_IP_);
202}
203EXPORT_SYMBOL(__asan_report_load_n_noabort);
204
205void __asan_report_store_n_noabort(unsigned long addr, size_t size)
206{
207 kasan_report(addr, size, true, _RET_IP_);
208}
209EXPORT_SYMBOL(__asan_report_store_n_noabort);