Commit c6d308534aef6c99904bf5862066360ae067abc4
Committed by
Linus Torvalds
1 parent
68920c9732
Exists in
smarct4x-processor-sdk-04.01.00.06
and in
1 other branch
UBSAN: run-time undefined behavior sanity checker
UBSAN uses compile-time instrumentation to catch undefined behavior (UB). Compiler inserts code that perform certain kinds of checks before operations that could cause UB. If check fails (i.e. UB detected) __ubsan_handle_* function called to print error message. So the most of the work is done by compiler. This patch just implements ubsan handlers printing errors. GCC has this capability since 4.9.x [1] (see -fsanitize=undefined option and its suboptions). However GCC 5.x has more checkers implemented [2]. Article [3] has a bit more details about UBSAN in the GCC. [1] - https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Debugging-Options.html [2] - https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html [3] - http://developerblog.redhat.com/2014/10/16/gcc-undefined-behavior-sanitizer-ubsan/ Issues which UBSAN has found thus far are: Found bugs: * out-of-bounds access - 97840cb67ff5 ("netfilter: nfnetlink: fix insufficient validation in nfnetlink_bind") undefined shifts: * d48458d4a768 ("jbd2: use a better hash function for the revoke table") * 10632008b9e1 ("clockevents: Prevent shift out of bounds") * 'x << -1' shift in ext4 - http://lkml.kernel.org/r/<5444EF21.8020501@samsung.com> * undefined rol32(0) - http://lkml.kernel.org/r/<1449198241-20654-1-git-send-email-sasha.levin@oracle.com> * undefined dirty_ratelimit calculation - http://lkml.kernel.org/r/<566594E2.3050306@odin.com> * undefined roundown_pow_of_two(0) - http://lkml.kernel.org/r/<1449156616-11474-1-git-send-email-sasha.levin@oracle.com> * [WONTFIX] undefined shift in __bpf_prog_run - http://lkml.kernel.org/r/<CACT4Y+ZxoR3UjLgcNdUm4fECLMx2VdtfrENMtRRCdgHB2n0bJA@mail.gmail.com> WONTFIX here because it should be fixed in bpf program, not in kernel. signed overflows: * 32a8df4e0b33f ("sched: Fix odd values in effective_load() calculations") * mul overflow in ntp - http://lkml.kernel.org/r/<1449175608-1146-1-git-send-email-sasha.levin@oracle.com> * incorrect conversion into rtc_time in rtc_time64_to_tm() - http://lkml.kernel.org/r/<1449187944-11730-1-git-send-email-sasha.levin@oracle.com> * unvalidated timespec in io_getevents() - http://lkml.kernel.org/r/<CACT4Y+bBxVYLQ6LtOKrKtnLthqLHcw-BMp3aqP3mjdAvr9FULQ@mail.gmail.com> * [NOTABUG] signed overflow in ktime_add_safe() - http://lkml.kernel.org/r/<CACT4Y+aJ4muRnWxsUe1CMnA6P8nooO33kwG-c8YZg=0Xc8rJqw@mail.gmail.com> [akpm@linux-foundation.org: fix unused local warning] [akpm@linux-foundation.org: fix __int128 build woes] Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Sasha Levin <sasha.levin@oracle.com> Cc: Randy Dunlap <rdunlap@infradead.org> Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Michal Marek <mmarek@suse.cz> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Yury Gribov <y.gribov@samsung.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Konstantin Khlebnikov <koct9i@gmail.com> Cc: Kostya Serebryany <kcc@google.com> Cc: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 17 changed files with 693 additions and 1 deletions Side-by-side Diff
- Documentation/ubsan.txt
- Makefile
- arch/x86/Kconfig
- arch/x86/boot/Makefile
- arch/x86/boot/compressed/Makefile
- arch/x86/entry/vdso/Makefile
- arch/x86/realmode/rm/Makefile
- drivers/firmware/efi/libstub/Makefile
- include/linux/sched.h
- lib/Kconfig.debug
- lib/Kconfig.ubsan
- lib/Makefile
- lib/ubsan.c
- lib/ubsan.h
- mm/kasan/Makefile
- scripts/Makefile.lib
- scripts/Makefile.ubsan
Documentation/ubsan.txt
1 | +Undefined Behavior Sanitizer - UBSAN | |
2 | + | |
3 | +Overview | |
4 | +-------- | |
5 | + | |
6 | +UBSAN is a runtime undefined behaviour checker. | |
7 | + | |
8 | +UBSAN uses compile-time instrumentation to catch undefined behavior (UB). | |
9 | +Compiler inserts code that perform certain kinds of checks before operations | |
10 | +that may cause UB. If check fails (i.e. UB detected) __ubsan_handle_* | |
11 | +function called to print error message. | |
12 | + | |
13 | +GCC has that feature since 4.9.x [1] (see -fsanitize=undefined option and | |
14 | +its suboptions). GCC 5.x has more checkers implemented [2]. | |
15 | + | |
16 | +Report example | |
17 | +--------------- | |
18 | + | |
19 | + ================================================================================ | |
20 | + UBSAN: Undefined behaviour in ../include/linux/bitops.h:110:33 | |
21 | + shift exponent 32 is to large for 32-bit type 'unsigned int' | |
22 | + CPU: 0 PID: 0 Comm: swapper Not tainted 4.4.0-rc1+ #26 | |
23 | + 0000000000000000 ffffffff82403cc8 ffffffff815e6cd6 0000000000000001 | |
24 | + ffffffff82403cf8 ffffffff82403ce0 ffffffff8163a5ed 0000000000000020 | |
25 | + ffffffff82403d78 ffffffff8163ac2b ffffffff815f0001 0000000000000002 | |
26 | + Call Trace: | |
27 | + [<ffffffff815e6cd6>] dump_stack+0x45/0x5f | |
28 | + [<ffffffff8163a5ed>] ubsan_epilogue+0xd/0x40 | |
29 | + [<ffffffff8163ac2b>] __ubsan_handle_shift_out_of_bounds+0xeb/0x130 | |
30 | + [<ffffffff815f0001>] ? radix_tree_gang_lookup_slot+0x51/0x150 | |
31 | + [<ffffffff8173c586>] _mix_pool_bytes+0x1e6/0x480 | |
32 | + [<ffffffff83105653>] ? dmi_walk_early+0x48/0x5c | |
33 | + [<ffffffff8173c881>] add_device_randomness+0x61/0x130 | |
34 | + [<ffffffff83105b35>] ? dmi_save_one_device+0xaa/0xaa | |
35 | + [<ffffffff83105653>] dmi_walk_early+0x48/0x5c | |
36 | + [<ffffffff831066ae>] dmi_scan_machine+0x278/0x4b4 | |
37 | + [<ffffffff8111d58a>] ? vprintk_default+0x1a/0x20 | |
38 | + [<ffffffff830ad120>] ? early_idt_handler_array+0x120/0x120 | |
39 | + [<ffffffff830b2240>] setup_arch+0x405/0xc2c | |
40 | + [<ffffffff830ad120>] ? early_idt_handler_array+0x120/0x120 | |
41 | + [<ffffffff830ae053>] start_kernel+0x83/0x49a | |
42 | + [<ffffffff830ad120>] ? early_idt_handler_array+0x120/0x120 | |
43 | + [<ffffffff830ad386>] x86_64_start_reservations+0x2a/0x2c | |
44 | + [<ffffffff830ad4f3>] x86_64_start_kernel+0x16b/0x17a | |
45 | + ================================================================================ | |
46 | + | |
47 | +Usage | |
48 | +----- | |
49 | + | |
50 | +To enable UBSAN configure kernel with: | |
51 | + | |
52 | + CONFIG_UBSAN=y | |
53 | + | |
54 | +and to check the entire kernel: | |
55 | + | |
56 | + CONFIG_UBSAN_SANITIZE_ALL=y | |
57 | + | |
58 | +To enable instrumentation for specific files or directories, add a line | |
59 | +similar to the following to the respective kernel Makefile: | |
60 | + | |
61 | + For a single file (e.g. main.o): | |
62 | + UBSAN_SANITIZE_main.o := y | |
63 | + | |
64 | + For all files in one directory: | |
65 | + UBSAN_SANITIZE := y | |
66 | + | |
67 | +To exclude files from being instrumented even if | |
68 | +CONFIG_UBSAN_SANITIZE_ALL=y, use: | |
69 | + | |
70 | + UBSAN_SANITIZE_main.o := n | |
71 | + and: | |
72 | + UBSAN_SANITIZE := n | |
73 | + | |
74 | +Detection of unaligned accesses controlled through the separate option - | |
75 | +CONFIG_UBSAN_ALIGNMENT. It's off by default on architectures that support | |
76 | +unaligned accesses (CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y). One could | |
77 | +still enable it in config, just note that it will produce a lot of UBSAN | |
78 | +reports. | |
79 | + | |
80 | +References | |
81 | +---------- | |
82 | + | |
83 | +[1] - https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Debugging-Options.html | |
84 | +[2] - https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html |
Makefile
... | ... | @@ -411,7 +411,7 @@ |
411 | 411 | export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS |
412 | 412 | |
413 | 413 | export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS |
414 | -export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN | |
414 | +export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN CFLAGS_UBSAN | |
415 | 415 | export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE |
416 | 416 | export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE |
417 | 417 | export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL |
... | ... | @@ -784,6 +784,7 @@ |
784 | 784 | |
785 | 785 | include scripts/Makefile.kasan |
786 | 786 | include scripts/Makefile.extrawarn |
787 | +include scripts/Makefile.ubsan | |
787 | 788 | |
788 | 789 | # Add any arch overrides and user supplied CPPFLAGS, AFLAGS and CFLAGS as the |
789 | 790 | # last assignments |
arch/x86/Kconfig
arch/x86/boot/Makefile
arch/x86/boot/compressed/Makefile
arch/x86/entry/vdso/Makefile
arch/x86/realmode/rm/Makefile
drivers/firmware/efi/libstub/Makefile
include/linux/sched.h
lib/Kconfig.debug
lib/Kconfig.ubsan
1 | +config ARCH_HAS_UBSAN_SANITIZE_ALL | |
2 | + bool | |
3 | + | |
4 | +config UBSAN | |
5 | + bool "Undefined behaviour sanity checker" | |
6 | + help | |
7 | + This option enables undefined behaviour sanity checker | |
8 | + Compile-time instrumentation is used to detect various undefined | |
9 | + behaviours in runtime. Various types of checks may be enabled | |
10 | + via boot parameter ubsan_handle (see: Documentation/ubsan.txt). | |
11 | + | |
12 | +config UBSAN_SANITIZE_ALL | |
13 | + bool "Enable instrumentation for the entire kernel" | |
14 | + depends on UBSAN | |
15 | + depends on ARCH_HAS_UBSAN_SANITIZE_ALL | |
16 | + default y | |
17 | + help | |
18 | + This option activates instrumentation for the entire kernel. | |
19 | + If you don't enable this option, you have to explicitly specify | |
20 | + UBSAN_SANITIZE := y for the files/directories you want to check for UB. | |
21 | + | |
22 | +config UBSAN_ALIGNMENT | |
23 | + bool "Enable checking of pointers alignment" | |
24 | + depends on UBSAN | |
25 | + default y if !HAVE_EFFICIENT_UNALIGNED_ACCESS | |
26 | + help | |
27 | + This option enables detection of unaligned memory accesses. | |
28 | + Enabling this option on architectures that support unalligned | |
29 | + accesses may produce a lot of false positives. |
lib/Makefile
lib/ubsan.c
1 | +/* | |
2 | + * UBSAN error reporting functions | |
3 | + * | |
4 | + * Copyright (c) 2014 Samsung Electronics Co., Ltd. | |
5 | + * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify | |
8 | + * it under the terms of the GNU General Public License version 2 as | |
9 | + * published by the Free Software Foundation. | |
10 | + * | |
11 | + */ | |
12 | + | |
13 | +#include <linux/bitops.h> | |
14 | +#include <linux/bug.h> | |
15 | +#include <linux/ctype.h> | |
16 | +#include <linux/init.h> | |
17 | +#include <linux/kernel.h> | |
18 | +#include <linux/types.h> | |
19 | +#include <linux/sched.h> | |
20 | + | |
21 | +#include "ubsan.h" | |
22 | + | |
23 | +const char *type_check_kinds[] = { | |
24 | + "load of", | |
25 | + "store to", | |
26 | + "reference binding to", | |
27 | + "member access within", | |
28 | + "member call on", | |
29 | + "constructor call on", | |
30 | + "downcast of", | |
31 | + "downcast of" | |
32 | +}; | |
33 | + | |
34 | +#define REPORTED_BIT 31 | |
35 | + | |
36 | +#if (BITS_PER_LONG == 64) && defined(__BIG_ENDIAN) | |
37 | +#define COLUMN_MASK (~(1U << REPORTED_BIT)) | |
38 | +#define LINE_MASK (~0U) | |
39 | +#else | |
40 | +#define COLUMN_MASK (~0U) | |
41 | +#define LINE_MASK (~(1U << REPORTED_BIT)) | |
42 | +#endif | |
43 | + | |
44 | +#define VALUE_LENGTH 40 | |
45 | + | |
46 | +static bool was_reported(struct source_location *location) | |
47 | +{ | |
48 | + return test_and_set_bit(REPORTED_BIT, &location->reported); | |
49 | +} | |
50 | + | |
51 | +static void print_source_location(const char *prefix, | |
52 | + struct source_location *loc) | |
53 | +{ | |
54 | + pr_err("%s %s:%d:%d\n", prefix, loc->file_name, | |
55 | + loc->line & LINE_MASK, loc->column & COLUMN_MASK); | |
56 | +} | |
57 | + | |
58 | +static bool suppress_report(struct source_location *loc) | |
59 | +{ | |
60 | + return current->in_ubsan || was_reported(loc); | |
61 | +} | |
62 | + | |
63 | +static bool type_is_int(struct type_descriptor *type) | |
64 | +{ | |
65 | + return type->type_kind == type_kind_int; | |
66 | +} | |
67 | + | |
68 | +static bool type_is_signed(struct type_descriptor *type) | |
69 | +{ | |
70 | + WARN_ON(!type_is_int(type)); | |
71 | + return type->type_info & 1; | |
72 | +} | |
73 | + | |
74 | +static unsigned type_bit_width(struct type_descriptor *type) | |
75 | +{ | |
76 | + return 1 << (type->type_info >> 1); | |
77 | +} | |
78 | + | |
79 | +static bool is_inline_int(struct type_descriptor *type) | |
80 | +{ | |
81 | + unsigned inline_bits = sizeof(unsigned long)*8; | |
82 | + unsigned bits = type_bit_width(type); | |
83 | + | |
84 | + WARN_ON(!type_is_int(type)); | |
85 | + | |
86 | + return bits <= inline_bits; | |
87 | +} | |
88 | + | |
89 | +static s_max get_signed_val(struct type_descriptor *type, unsigned long val) | |
90 | +{ | |
91 | + if (is_inline_int(type)) { | |
92 | + unsigned extra_bits = sizeof(s_max)*8 - type_bit_width(type); | |
93 | + return ((s_max)val) << extra_bits >> extra_bits; | |
94 | + } | |
95 | + | |
96 | + if (type_bit_width(type) == 64) | |
97 | + return *(s64 *)val; | |
98 | + | |
99 | + return *(s_max *)val; | |
100 | +} | |
101 | + | |
102 | +static bool val_is_negative(struct type_descriptor *type, unsigned long val) | |
103 | +{ | |
104 | + return type_is_signed(type) && get_signed_val(type, val) < 0; | |
105 | +} | |
106 | + | |
107 | +static u_max get_unsigned_val(struct type_descriptor *type, unsigned long val) | |
108 | +{ | |
109 | + if (is_inline_int(type)) | |
110 | + return val; | |
111 | + | |
112 | + if (type_bit_width(type) == 64) | |
113 | + return *(u64 *)val; | |
114 | + | |
115 | + return *(u_max *)val; | |
116 | +} | |
117 | + | |
118 | +static void val_to_string(char *str, size_t size, struct type_descriptor *type, | |
119 | + unsigned long value) | |
120 | +{ | |
121 | + if (type_is_int(type)) { | |
122 | + if (type_bit_width(type) == 128) { | |
123 | +#if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__) | |
124 | + u_max val = get_unsigned_val(type, value); | |
125 | + | |
126 | + scnprintf(str, size, "0x%08x%08x%08x%08x", | |
127 | + (u32)(val >> 96), | |
128 | + (u32)(val >> 64), | |
129 | + (u32)(val >> 32), | |
130 | + (u32)(val)); | |
131 | +#else | |
132 | + WARN_ON(1); | |
133 | +#endif | |
134 | + } else if (type_is_signed(type)) { | |
135 | + scnprintf(str, size, "%lld", | |
136 | + (s64)get_signed_val(type, value)); | |
137 | + } else { | |
138 | + scnprintf(str, size, "%llu", | |
139 | + (u64)get_unsigned_val(type, value)); | |
140 | + } | |
141 | + } | |
142 | +} | |
143 | + | |
144 | +static bool location_is_valid(struct source_location *loc) | |
145 | +{ | |
146 | + return loc->file_name != NULL; | |
147 | +} | |
148 | + | |
149 | +static DEFINE_SPINLOCK(report_lock); | |
150 | + | |
151 | +static void ubsan_prologue(struct source_location *location, | |
152 | + unsigned long *flags) | |
153 | +{ | |
154 | + current->in_ubsan++; | |
155 | + spin_lock_irqsave(&report_lock, *flags); | |
156 | + | |
157 | + pr_err("========================================" | |
158 | + "========================================\n"); | |
159 | + print_source_location("UBSAN: Undefined behaviour in", location); | |
160 | +} | |
161 | + | |
162 | +static void ubsan_epilogue(unsigned long *flags) | |
163 | +{ | |
164 | + dump_stack(); | |
165 | + pr_err("========================================" | |
166 | + "========================================\n"); | |
167 | + spin_unlock_irqrestore(&report_lock, *flags); | |
168 | + current->in_ubsan--; | |
169 | +} | |
170 | + | |
171 | +static void handle_overflow(struct overflow_data *data, unsigned long lhs, | |
172 | + unsigned long rhs, char op) | |
173 | +{ | |
174 | + | |
175 | + struct type_descriptor *type = data->type; | |
176 | + unsigned long flags; | |
177 | + char lhs_val_str[VALUE_LENGTH]; | |
178 | + char rhs_val_str[VALUE_LENGTH]; | |
179 | + | |
180 | + if (suppress_report(&data->location)) | |
181 | + return; | |
182 | + | |
183 | + ubsan_prologue(&data->location, &flags); | |
184 | + | |
185 | + val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs); | |
186 | + val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs); | |
187 | + pr_err("%s integer overflow:\n", | |
188 | + type_is_signed(type) ? "signed" : "unsigned"); | |
189 | + pr_err("%s %c %s cannot be represented in type %s\n", | |
190 | + lhs_val_str, | |
191 | + op, | |
192 | + rhs_val_str, | |
193 | + type->type_name); | |
194 | + | |
195 | + ubsan_epilogue(&flags); | |
196 | +} | |
197 | + | |
198 | +void __ubsan_handle_add_overflow(struct overflow_data *data, | |
199 | + unsigned long lhs, | |
200 | + unsigned long rhs) | |
201 | +{ | |
202 | + | |
203 | + handle_overflow(data, lhs, rhs, '+'); | |
204 | +} | |
205 | +EXPORT_SYMBOL(__ubsan_handle_add_overflow); | |
206 | + | |
207 | +void __ubsan_handle_sub_overflow(struct overflow_data *data, | |
208 | + unsigned long lhs, | |
209 | + unsigned long rhs) | |
210 | +{ | |
211 | + handle_overflow(data, lhs, rhs, '-'); | |
212 | +} | |
213 | +EXPORT_SYMBOL(__ubsan_handle_sub_overflow); | |
214 | + | |
215 | +void __ubsan_handle_mul_overflow(struct overflow_data *data, | |
216 | + unsigned long lhs, | |
217 | + unsigned long rhs) | |
218 | +{ | |
219 | + handle_overflow(data, lhs, rhs, '*'); | |
220 | +} | |
221 | +EXPORT_SYMBOL(__ubsan_handle_mul_overflow); | |
222 | + | |
223 | +void __ubsan_handle_negate_overflow(struct overflow_data *data, | |
224 | + unsigned long old_val) | |
225 | +{ | |
226 | + unsigned long flags; | |
227 | + char old_val_str[VALUE_LENGTH]; | |
228 | + | |
229 | + if (suppress_report(&data->location)) | |
230 | + return; | |
231 | + | |
232 | + ubsan_prologue(&data->location, &flags); | |
233 | + | |
234 | + val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val); | |
235 | + | |
236 | + pr_err("negation of %s cannot be represented in type %s:\n", | |
237 | + old_val_str, data->type->type_name); | |
238 | + | |
239 | + ubsan_epilogue(&flags); | |
240 | +} | |
241 | +EXPORT_SYMBOL(__ubsan_handle_negate_overflow); | |
242 | + | |
243 | + | |
244 | +void __ubsan_handle_divrem_overflow(struct overflow_data *data, | |
245 | + unsigned long lhs, | |
246 | + unsigned long rhs) | |
247 | +{ | |
248 | + unsigned long flags; | |
249 | + char rhs_val_str[VALUE_LENGTH]; | |
250 | + | |
251 | + if (suppress_report(&data->location)) | |
252 | + return; | |
253 | + | |
254 | + ubsan_prologue(&data->location, &flags); | |
255 | + | |
256 | + val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs); | |
257 | + | |
258 | + if (type_is_signed(data->type) && get_signed_val(data->type, rhs) == -1) | |
259 | + pr_err("division of %s by -1 cannot be represented in type %s\n", | |
260 | + rhs_val_str, data->type->type_name); | |
261 | + else | |
262 | + pr_err("division by zero\n"); | |
263 | + | |
264 | + ubsan_epilogue(&flags); | |
265 | +} | |
266 | +EXPORT_SYMBOL(__ubsan_handle_divrem_overflow); | |
267 | + | |
268 | +static void handle_null_ptr_deref(struct type_mismatch_data *data) | |
269 | +{ | |
270 | + unsigned long flags; | |
271 | + | |
272 | + if (suppress_report(&data->location)) | |
273 | + return; | |
274 | + | |
275 | + ubsan_prologue(&data->location, &flags); | |
276 | + | |
277 | + pr_err("%s null pointer of type %s\n", | |
278 | + type_check_kinds[data->type_check_kind], | |
279 | + data->type->type_name); | |
280 | + | |
281 | + ubsan_epilogue(&flags); | |
282 | +} | |
283 | + | |
284 | +static void handle_missaligned_access(struct type_mismatch_data *data, | |
285 | + unsigned long ptr) | |
286 | +{ | |
287 | + unsigned long flags; | |
288 | + | |
289 | + if (suppress_report(&data->location)) | |
290 | + return; | |
291 | + | |
292 | + ubsan_prologue(&data->location, &flags); | |
293 | + | |
294 | + pr_err("%s misaligned address %p for type %s\n", | |
295 | + type_check_kinds[data->type_check_kind], | |
296 | + (void *)ptr, data->type->type_name); | |
297 | + pr_err("which requires %ld byte alignment\n", data->alignment); | |
298 | + | |
299 | + ubsan_epilogue(&flags); | |
300 | +} | |
301 | + | |
302 | +static void handle_object_size_mismatch(struct type_mismatch_data *data, | |
303 | + unsigned long ptr) | |
304 | +{ | |
305 | + unsigned long flags; | |
306 | + | |
307 | + if (suppress_report(&data->location)) | |
308 | + return; | |
309 | + | |
310 | + ubsan_prologue(&data->location, &flags); | |
311 | + pr_err("%s address %pk with insufficient space\n", | |
312 | + type_check_kinds[data->type_check_kind], | |
313 | + (void *) ptr); | |
314 | + pr_err("for an object of type %s\n", data->type->type_name); | |
315 | + ubsan_epilogue(&flags); | |
316 | +} | |
317 | + | |
318 | +void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, | |
319 | + unsigned long ptr) | |
320 | +{ | |
321 | + | |
322 | + if (!ptr) | |
323 | + handle_null_ptr_deref(data); | |
324 | + else if (data->alignment && !IS_ALIGNED(ptr, data->alignment)) | |
325 | + handle_missaligned_access(data, ptr); | |
326 | + else | |
327 | + handle_object_size_mismatch(data, ptr); | |
328 | +} | |
329 | +EXPORT_SYMBOL(__ubsan_handle_type_mismatch); | |
330 | + | |
331 | +void __ubsan_handle_nonnull_return(struct nonnull_return_data *data) | |
332 | +{ | |
333 | + unsigned long flags; | |
334 | + | |
335 | + if (suppress_report(&data->location)) | |
336 | + return; | |
337 | + | |
338 | + ubsan_prologue(&data->location, &flags); | |
339 | + | |
340 | + pr_err("null pointer returned from function declared to never return null\n"); | |
341 | + | |
342 | + if (location_is_valid(&data->attr_location)) | |
343 | + print_source_location("returns_nonnull attribute specified in", | |
344 | + &data->attr_location); | |
345 | + | |
346 | + ubsan_epilogue(&flags); | |
347 | +} | |
348 | +EXPORT_SYMBOL(__ubsan_handle_nonnull_return); | |
349 | + | |
350 | +void __ubsan_handle_vla_bound_not_positive(struct vla_bound_data *data, | |
351 | + unsigned long bound) | |
352 | +{ | |
353 | + unsigned long flags; | |
354 | + char bound_str[VALUE_LENGTH]; | |
355 | + | |
356 | + if (suppress_report(&data->location)) | |
357 | + return; | |
358 | + | |
359 | + ubsan_prologue(&data->location, &flags); | |
360 | + | |
361 | + val_to_string(bound_str, sizeof(bound_str), data->type, bound); | |
362 | + pr_err("variable length array bound value %s <= 0\n", bound_str); | |
363 | + | |
364 | + ubsan_epilogue(&flags); | |
365 | +} | |
366 | +EXPORT_SYMBOL(__ubsan_handle_vla_bound_not_positive); | |
367 | + | |
368 | +void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, | |
369 | + unsigned long index) | |
370 | +{ | |
371 | + unsigned long flags; | |
372 | + char index_str[VALUE_LENGTH]; | |
373 | + | |
374 | + if (suppress_report(&data->location)) | |
375 | + return; | |
376 | + | |
377 | + ubsan_prologue(&data->location, &flags); | |
378 | + | |
379 | + val_to_string(index_str, sizeof(index_str), data->index_type, index); | |
380 | + pr_err("index %s is out of range for type %s\n", index_str, | |
381 | + data->array_type->type_name); | |
382 | + ubsan_epilogue(&flags); | |
383 | +} | |
384 | +EXPORT_SYMBOL(__ubsan_handle_out_of_bounds); | |
385 | + | |
386 | +void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data, | |
387 | + unsigned long lhs, unsigned long rhs) | |
388 | +{ | |
389 | + unsigned long flags; | |
390 | + struct type_descriptor *rhs_type = data->rhs_type; | |
391 | + struct type_descriptor *lhs_type = data->lhs_type; | |
392 | + char rhs_str[VALUE_LENGTH]; | |
393 | + char lhs_str[VALUE_LENGTH]; | |
394 | + | |
395 | + if (suppress_report(&data->location)) | |
396 | + return; | |
397 | + | |
398 | + ubsan_prologue(&data->location, &flags); | |
399 | + | |
400 | + val_to_string(rhs_str, sizeof(rhs_str), rhs_type, rhs); | |
401 | + val_to_string(lhs_str, sizeof(lhs_str), lhs_type, lhs); | |
402 | + | |
403 | + if (val_is_negative(rhs_type, rhs)) | |
404 | + pr_err("shift exponent %s is negative\n", rhs_str); | |
405 | + | |
406 | + else if (get_unsigned_val(rhs_type, rhs) >= | |
407 | + type_bit_width(lhs_type)) | |
408 | + pr_err("shift exponent %s is too large for %u-bit type %s\n", | |
409 | + rhs_str, | |
410 | + type_bit_width(lhs_type), | |
411 | + lhs_type->type_name); | |
412 | + else if (val_is_negative(lhs_type, lhs)) | |
413 | + pr_err("left shift of negative value %s\n", | |
414 | + lhs_str); | |
415 | + else | |
416 | + pr_err("left shift of %s by %s places cannot be" | |
417 | + " represented in type %s\n", | |
418 | + lhs_str, rhs_str, | |
419 | + lhs_type->type_name); | |
420 | + | |
421 | + ubsan_epilogue(&flags); | |
422 | +} | |
423 | +EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds); | |
424 | + | |
425 | + | |
426 | +void __noreturn | |
427 | +__ubsan_handle_builtin_unreachable(struct unreachable_data *data) | |
428 | +{ | |
429 | + unsigned long flags; | |
430 | + | |
431 | + ubsan_prologue(&data->location, &flags); | |
432 | + pr_err("calling __builtin_unreachable()\n"); | |
433 | + ubsan_epilogue(&flags); | |
434 | + panic("can't return from __builtin_unreachable()"); | |
435 | +} | |
436 | +EXPORT_SYMBOL(__ubsan_handle_builtin_unreachable); | |
437 | + | |
438 | +void __ubsan_handle_load_invalid_value(struct invalid_value_data *data, | |
439 | + unsigned long val) | |
440 | +{ | |
441 | + unsigned long flags; | |
442 | + char val_str[VALUE_LENGTH]; | |
443 | + | |
444 | + if (suppress_report(&data->location)) | |
445 | + return; | |
446 | + | |
447 | + ubsan_prologue(&data->location, &flags); | |
448 | + | |
449 | + val_to_string(val_str, sizeof(val_str), data->type, val); | |
450 | + | |
451 | + pr_err("load of value %s is not a valid value for type %s\n", | |
452 | + val_str, data->type->type_name); | |
453 | + | |
454 | + ubsan_epilogue(&flags); | |
455 | +} | |
456 | +EXPORT_SYMBOL(__ubsan_handle_load_invalid_value); |
lib/ubsan.h
1 | +#ifndef _LIB_UBSAN_H | |
2 | +#define _LIB_UBSAN_H | |
3 | + | |
4 | +enum { | |
5 | + type_kind_int = 0, | |
6 | + type_kind_float = 1, | |
7 | + type_unknown = 0xffff | |
8 | +}; | |
9 | + | |
10 | +struct type_descriptor { | |
11 | + u16 type_kind; | |
12 | + u16 type_info; | |
13 | + char type_name[1]; | |
14 | +}; | |
15 | + | |
16 | +struct source_location { | |
17 | + const char *file_name; | |
18 | + union { | |
19 | + unsigned long reported; | |
20 | + struct { | |
21 | + u32 line; | |
22 | + u32 column; | |
23 | + }; | |
24 | + }; | |
25 | +}; | |
26 | + | |
27 | +struct overflow_data { | |
28 | + struct source_location location; | |
29 | + struct type_descriptor *type; | |
30 | +}; | |
31 | + | |
32 | +struct type_mismatch_data { | |
33 | + struct source_location location; | |
34 | + struct type_descriptor *type; | |
35 | + unsigned long alignment; | |
36 | + unsigned char type_check_kind; | |
37 | +}; | |
38 | + | |
39 | +struct nonnull_arg_data { | |
40 | + struct source_location location; | |
41 | + struct source_location attr_location; | |
42 | + int arg_index; | |
43 | +}; | |
44 | + | |
45 | +struct nonnull_return_data { | |
46 | + struct source_location location; | |
47 | + struct source_location attr_location; | |
48 | +}; | |
49 | + | |
50 | +struct vla_bound_data { | |
51 | + struct source_location location; | |
52 | + struct type_descriptor *type; | |
53 | +}; | |
54 | + | |
55 | +struct out_of_bounds_data { | |
56 | + struct source_location location; | |
57 | + struct type_descriptor *array_type; | |
58 | + struct type_descriptor *index_type; | |
59 | +}; | |
60 | + | |
61 | +struct shift_out_of_bounds_data { | |
62 | + struct source_location location; | |
63 | + struct type_descriptor *lhs_type; | |
64 | + struct type_descriptor *rhs_type; | |
65 | +}; | |
66 | + | |
67 | +struct unreachable_data { | |
68 | + struct source_location location; | |
69 | +}; | |
70 | + | |
71 | +struct invalid_value_data { | |
72 | + struct source_location location; | |
73 | + struct type_descriptor *type; | |
74 | +}; | |
75 | + | |
76 | +#if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__) | |
77 | +typedef __int128 s_max; | |
78 | +typedef unsigned __int128 u_max; | |
79 | +#else | |
80 | +typedef s64 s_max; | |
81 | +typedef u64 u_max; | |
82 | +#endif | |
83 | + | |
84 | +#endif |
mm/kasan/Makefile
scripts/Makefile.lib
... | ... | @@ -130,6 +130,12 @@ |
130 | 130 | $(CFLAGS_KASAN)) |
131 | 131 | endif |
132 | 132 | |
133 | +ifeq ($(CONFIG_UBSAN),y) | |
134 | +_c_flags += $(if $(patsubst n%,, \ | |
135 | + $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \ | |
136 | + $(CFLAGS_UBSAN)) | |
137 | +endif | |
138 | + | |
133 | 139 | # If building the kernel in a separate objtree expand all occurrences |
134 | 140 | # of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/'). |
135 | 141 |
scripts/Makefile.ubsan
1 | +ifdef CONFIG_UBSAN | |
2 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=shift) | |
3 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=integer-divide-by-zero) | |
4 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=unreachable) | |
5 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=vla-bound) | |
6 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=null) | |
7 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=signed-integer-overflow) | |
8 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=bounds) | |
9 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=object-size) | |
10 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=returns-nonnull-attribute) | |
11 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=bool) | |
12 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=enum) | |
13 | + | |
14 | +ifdef CONFIG_UBSAN_ALIGNMENT | |
15 | + CFLAGS_UBSAN += $(call cc-option, -fsanitize=alignment) | |
16 | +endif | |
17 | +endif |