Commit | Line | Data |
---|---|---|
2e903b91 AK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * This file contains core hardware tag-based KASAN code. | |
4 | * | |
5 | * Copyright (c) 2020 Google, Inc. | |
6 | * Author: Andrey Konovalov <andreyknvl@google.com> | |
7 | */ | |
8 | ||
9 | #define pr_fmt(fmt) "kasan: " fmt | |
10 | ||
8028caac | 11 | #include <linux/init.h> |
2e903b91 AK |
12 | #include <linux/kasan.h> |
13 | #include <linux/kernel.h> | |
14 | #include <linux/memory.h> | |
15 | #include <linux/mm.h> | |
8028caac | 16 | #include <linux/static_key.h> |
2e903b91 AK |
17 | #include <linux/string.h> |
18 | #include <linux/types.h> | |
19 | ||
20 | #include "kasan.h" | |
21 | ||
76bc99e8 AK |
22 | enum kasan_arg { |
23 | KASAN_ARG_DEFAULT, | |
24 | KASAN_ARG_OFF, | |
25 | KASAN_ARG_ON, | |
8028caac AK |
26 | }; |
27 | ||
28 | enum kasan_arg_stacktrace { | |
29 | KASAN_ARG_STACKTRACE_DEFAULT, | |
30 | KASAN_ARG_STACKTRACE_OFF, | |
31 | KASAN_ARG_STACKTRACE_ON, | |
32 | }; | |
33 | ||
34 | enum kasan_arg_fault { | |
35 | KASAN_ARG_FAULT_DEFAULT, | |
36 | KASAN_ARG_FAULT_REPORT, | |
37 | KASAN_ARG_FAULT_PANIC, | |
38 | }; | |
39 | ||
76bc99e8 | 40 | static enum kasan_arg kasan_arg __ro_after_init; |
8028caac AK |
41 | static enum kasan_arg_stacktrace kasan_arg_stacktrace __ro_after_init; |
42 | static enum kasan_arg_fault kasan_arg_fault __ro_after_init; | |
43 | ||
44 | /* Whether KASAN is enabled at all. */ | |
45 | DEFINE_STATIC_KEY_FALSE(kasan_flag_enabled); | |
46 | EXPORT_SYMBOL(kasan_flag_enabled); | |
47 | ||
48 | /* Whether to collect alloc/free stack traces. */ | |
49 | DEFINE_STATIC_KEY_FALSE(kasan_flag_stacktrace); | |
50 | ||
7169487b | 51 | /* Whether to panic or print a report and disable tag checking on fault. */ |
8028caac AK |
52 | bool kasan_flag_panic __ro_after_init; |
53 | ||
76bc99e8 AK |
54 | /* kasan=off/on */ |
55 | static int __init early_kasan_flag(char *arg) | |
8028caac AK |
56 | { |
57 | if (!arg) | |
58 | return -EINVAL; | |
59 | ||
60 | if (!strcmp(arg, "off")) | |
76bc99e8 AK |
61 | kasan_arg = KASAN_ARG_OFF; |
62 | else if (!strcmp(arg, "on")) | |
63 | kasan_arg = KASAN_ARG_ON; | |
8028caac AK |
64 | else |
65 | return -EINVAL; | |
66 | ||
67 | return 0; | |
68 | } | |
76bc99e8 | 69 | early_param("kasan", early_kasan_flag); |
8028caac | 70 | |
76bc99e8 | 71 | /* kasan.stacktrace=off/on */ |
8028caac AK |
72 | static int __init early_kasan_flag_stacktrace(char *arg) |
73 | { | |
74 | if (!arg) | |
75 | return -EINVAL; | |
76 | ||
77 | if (!strcmp(arg, "off")) | |
78 | kasan_arg_stacktrace = KASAN_ARG_STACKTRACE_OFF; | |
79 | else if (!strcmp(arg, "on")) | |
80 | kasan_arg_stacktrace = KASAN_ARG_STACKTRACE_ON; | |
81 | else | |
82 | return -EINVAL; | |
83 | ||
84 | return 0; | |
85 | } | |
86 | early_param("kasan.stacktrace", early_kasan_flag_stacktrace); | |
87 | ||
88 | /* kasan.fault=report/panic */ | |
89 | static int __init early_kasan_fault(char *arg) | |
90 | { | |
91 | if (!arg) | |
92 | return -EINVAL; | |
93 | ||
94 | if (!strcmp(arg, "report")) | |
95 | kasan_arg_fault = KASAN_ARG_FAULT_REPORT; | |
96 | else if (!strcmp(arg, "panic")) | |
97 | kasan_arg_fault = KASAN_ARG_FAULT_PANIC; | |
98 | else | |
99 | return -EINVAL; | |
100 | ||
101 | return 0; | |
102 | } | |
103 | early_param("kasan.fault", early_kasan_fault); | |
104 | ||
2e903b91 AK |
105 | /* kasan_init_hw_tags_cpu() is called for each CPU. */ |
106 | void kasan_init_hw_tags_cpu(void) | |
107 | { | |
8028caac AK |
108 | /* |
109 | * There's no need to check that the hardware is MTE-capable here, | |
110 | * as this function is only called for MTE-capable hardware. | |
111 | */ | |
112 | ||
76bc99e8 AK |
113 | /* If KASAN is disabled via command line, don't initialize it. */ |
114 | if (kasan_arg == KASAN_ARG_OFF) | |
8028caac AK |
115 | return; |
116 | ||
2e903b91 AK |
117 | hw_init_tags(KASAN_TAG_MAX); |
118 | hw_enable_tagging(); | |
119 | } | |
120 | ||
121 | /* kasan_init_hw_tags() is called once on boot CPU. */ | |
122 | void __init kasan_init_hw_tags(void) | |
123 | { | |
76bc99e8 | 124 | /* If hardware doesn't support MTE, don't initialize KASAN. */ |
8028caac AK |
125 | if (!system_supports_mte()) |
126 | return; | |
127 | ||
76bc99e8 AK |
128 | /* If KASAN is disabled via command line, don't initialize it. */ |
129 | if (kasan_arg == KASAN_ARG_OFF) | |
8028caac | 130 | return; |
8028caac | 131 | |
76bc99e8 AK |
132 | /* Enable KASAN. */ |
133 | static_branch_enable(&kasan_flag_enabled); | |
8028caac AK |
134 | |
135 | switch (kasan_arg_stacktrace) { | |
136 | case KASAN_ARG_STACKTRACE_DEFAULT: | |
1cc4cdb5 AK |
137 | /* Default to enabling stack trace collection. */ |
138 | static_branch_enable(&kasan_flag_stacktrace); | |
8028caac AK |
139 | break; |
140 | case KASAN_ARG_STACKTRACE_OFF: | |
76bc99e8 | 141 | /* Do nothing, kasan_flag_stacktrace keeps its default value. */ |
8028caac AK |
142 | break; |
143 | case KASAN_ARG_STACKTRACE_ON: | |
144 | static_branch_enable(&kasan_flag_stacktrace); | |
145 | break; | |
146 | } | |
147 | ||
148 | switch (kasan_arg_fault) { | |
149 | case KASAN_ARG_FAULT_DEFAULT: | |
76bc99e8 AK |
150 | /* |
151 | * Default to no panic on report. | |
152 | * Do nothing, kasan_flag_panic keeps its default value. | |
153 | */ | |
8028caac AK |
154 | break; |
155 | case KASAN_ARG_FAULT_REPORT: | |
76bc99e8 | 156 | /* Do nothing, kasan_flag_panic keeps its default value. */ |
8028caac AK |
157 | break; |
158 | case KASAN_ARG_FAULT_PANIC: | |
76bc99e8 | 159 | /* Enable panic on report. */ |
8028caac AK |
160 | kasan_flag_panic = true; |
161 | break; | |
162 | } | |
163 | ||
2e903b91 AK |
164 | pr_info("KernelAddressSanitizer initialized\n"); |
165 | } | |
166 | ||
2e903b91 AK |
167 | void kasan_set_free_info(struct kmem_cache *cache, |
168 | void *object, u8 tag) | |
169 | { | |
170 | struct kasan_alloc_meta *alloc_meta; | |
171 | ||
6476792f | 172 | alloc_meta = kasan_get_alloc_meta(cache, object); |
97593cad AK |
173 | if (alloc_meta) |
174 | kasan_set_track(&alloc_meta->free_track[0], GFP_NOWAIT); | |
2e903b91 AK |
175 | } |
176 | ||
177 | struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, | |
178 | void *object, u8 tag) | |
179 | { | |
180 | struct kasan_alloc_meta *alloc_meta; | |
181 | ||
6476792f | 182 | alloc_meta = kasan_get_alloc_meta(cache, object); |
97593cad AK |
183 | if (!alloc_meta) |
184 | return NULL; | |
185 | ||
2e903b91 AK |
186 | return &alloc_meta->free_track[0]; |
187 | } | |
f05842cf AK |
188 | |
189 | #if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST) | |
190 | ||
191 | void kasan_set_tagging_report_once(bool state) | |
192 | { | |
193 | hw_set_tagging_report_once(state); | |
194 | } | |
195 | EXPORT_SYMBOL_GPL(kasan_set_tagging_report_once); | |
196 | ||
197 | void kasan_enable_tagging(void) | |
198 | { | |
199 | hw_enable_tagging(); | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(kasan_enable_tagging); | |
202 | ||
203 | #endif |