Commit | Line | Data |
---|---|---|
0b24becc AR |
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 | */ | |
38 | static 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 | ||
48 | void 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 | ||
65 | static __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 | ||
77 | static __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 | ||
94 | static __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 | ||
111 | static __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 | ||
128 | static __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 | ||
148 | static __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 | ||
161 | static __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 | ||
190 | static __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 | ||
209 | static __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 | ||
232 | static __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 | ||
274 | DEFINE_ASAN_LOAD_STORE(1); | |
275 | DEFINE_ASAN_LOAD_STORE(2); | |
276 | DEFINE_ASAN_LOAD_STORE(4); | |
277 | DEFINE_ASAN_LOAD_STORE(8); | |
278 | DEFINE_ASAN_LOAD_STORE(16); | |
279 | ||
280 | void __asan_loadN(unsigned long addr, size_t size) | |
281 | { | |
282 | check_memory_region(addr, size, false); | |
283 | } | |
284 | EXPORT_SYMBOL(__asan_loadN); | |
285 | ||
286 | __alias(__asan_loadN) | |
287 | void __asan_loadN_noabort(unsigned long, size_t); | |
288 | EXPORT_SYMBOL(__asan_loadN_noabort); | |
289 | ||
290 | void __asan_storeN(unsigned long addr, size_t size) | |
291 | { | |
292 | check_memory_region(addr, size, true); | |
293 | } | |
294 | EXPORT_SYMBOL(__asan_storeN); | |
295 | ||
296 | __alias(__asan_storeN) | |
297 | void __asan_storeN_noabort(unsigned long, size_t); | |
298 | EXPORT_SYMBOL(__asan_storeN_noabort); | |
299 | ||
300 | /* to shut up compiler complaints */ | |
301 | void __asan_handle_no_return(void) {} | |
302 | EXPORT_SYMBOL(__asan_handle_no_return); |