| .. | .. |
|---|
| 6 | 6 | * Copyright (C) 2016 Google, Inc. |
|---|
| 7 | 7 | * |
|---|
| 8 | 8 | * Based on code by Dmitry Chernenkov. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is free software; you can redistribute it and/or |
|---|
| 11 | | - * modify it under the terms of the GNU General Public License |
|---|
| 12 | | - * version 2 as published by the Free Software Foundation. |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 15 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 17 | | - * General Public License for more details. |
|---|
| 18 | | - * |
|---|
| 19 | 9 | */ |
|---|
| 20 | 10 | |
|---|
| 21 | 11 | #include <linux/gfp.h> |
|---|
| .. | .. |
|---|
| 29 | 19 | #include <linux/srcu.h> |
|---|
| 30 | 20 | #include <linux/string.h> |
|---|
| 31 | 21 | #include <linux/types.h> |
|---|
| 22 | +#include <linux/cpuhotplug.h> |
|---|
| 32 | 23 | |
|---|
| 33 | 24 | #include "../slab.h" |
|---|
| 34 | 25 | #include "kasan.h" |
|---|
| .. | .. |
|---|
| 43 | 34 | struct qlist_node *head; |
|---|
| 44 | 35 | struct qlist_node *tail; |
|---|
| 45 | 36 | size_t bytes; |
|---|
| 37 | + bool offline; |
|---|
| 46 | 38 | }; |
|---|
| 47 | 39 | |
|---|
| 48 | 40 | #define QLIST_INIT { NULL, NULL, 0 } |
|---|
| .. | .. |
|---|
| 145 | 137 | if (IS_ENABLED(CONFIG_SLAB)) |
|---|
| 146 | 138 | local_irq_save(flags); |
|---|
| 147 | 139 | |
|---|
| 140 | + /* |
|---|
| 141 | + * As the object now gets freed from the quaratine, assume that its |
|---|
| 142 | + * free track is no longer valid. |
|---|
| 143 | + */ |
|---|
| 144 | + *(u8 *)kasan_mem_to_shadow(object) = KASAN_KMALLOC_FREE; |
|---|
| 145 | + |
|---|
| 148 | 146 | ___cache_free(cache, object, _THIS_IP_); |
|---|
| 149 | 147 | |
|---|
| 150 | 148 | if (IS_ENABLED(CONFIG_SLAB)) |
|---|
| .. | .. |
|---|
| 170 | 168 | qlist_init(q); |
|---|
| 171 | 169 | } |
|---|
| 172 | 170 | |
|---|
| 173 | | -void quarantine_put(struct kasan_free_meta *info, struct kmem_cache *cache) |
|---|
| 171 | +bool kasan_quarantine_put(struct kmem_cache *cache, void *object) |
|---|
| 174 | 172 | { |
|---|
| 175 | 173 | unsigned long flags; |
|---|
| 176 | 174 | struct qlist_head *q; |
|---|
| 177 | 175 | struct qlist_head temp = QLIST_INIT; |
|---|
| 176 | + struct kasan_free_meta *meta = kasan_get_free_meta(cache, object); |
|---|
| 177 | + |
|---|
| 178 | + /* |
|---|
| 179 | + * If there's no metadata for this object, don't put it into |
|---|
| 180 | + * quarantine. |
|---|
| 181 | + */ |
|---|
| 182 | + if (!meta) |
|---|
| 183 | + return false; |
|---|
| 178 | 184 | |
|---|
| 179 | 185 | /* |
|---|
| 180 | 186 | * Note: irq must be disabled until after we move the batch to the |
|---|
| 181 | | - * global quarantine. Otherwise quarantine_remove_cache() can miss |
|---|
| 182 | | - * some objects belonging to the cache if they are in our local temp |
|---|
| 183 | | - * list. quarantine_remove_cache() executes on_each_cpu() at the |
|---|
| 184 | | - * beginning which ensures that it either sees the objects in per-cpu |
|---|
| 185 | | - * lists or in the global quarantine. |
|---|
| 187 | + * global quarantine. Otherwise kasan_quarantine_remove_cache() can |
|---|
| 188 | + * miss some objects belonging to the cache if they are in our local |
|---|
| 189 | + * temp list. kasan_quarantine_remove_cache() executes on_each_cpu() |
|---|
| 190 | + * at the beginning which ensures that it either sees the objects in |
|---|
| 191 | + * per-cpu lists or in the global quarantine. |
|---|
| 186 | 192 | */ |
|---|
| 187 | 193 | local_irq_save(flags); |
|---|
| 188 | 194 | |
|---|
| 189 | 195 | q = this_cpu_ptr(&cpu_quarantine); |
|---|
| 190 | | - qlist_put(q, &info->quarantine_link, cache->size); |
|---|
| 196 | + if (q->offline) { |
|---|
| 197 | + local_irq_restore(flags); |
|---|
| 198 | + return false; |
|---|
| 199 | + } |
|---|
| 200 | + qlist_put(q, &meta->quarantine_link, cache->size); |
|---|
| 191 | 201 | if (unlikely(q->bytes > QUARANTINE_PERCPU_SIZE)) { |
|---|
| 192 | 202 | qlist_move_all(q, &temp); |
|---|
| 193 | 203 | |
|---|
| .. | .. |
|---|
| 208 | 218 | } |
|---|
| 209 | 219 | |
|---|
| 210 | 220 | local_irq_restore(flags); |
|---|
| 221 | + |
|---|
| 222 | + return true; |
|---|
| 211 | 223 | } |
|---|
| 212 | 224 | |
|---|
| 213 | | -void quarantine_reduce(void) |
|---|
| 225 | +void kasan_quarantine_reduce(void) |
|---|
| 214 | 226 | { |
|---|
| 215 | 227 | size_t total_size, new_quarantine_size, percpu_quarantines; |
|---|
| 216 | 228 | unsigned long flags; |
|---|
| .. | .. |
|---|
| 222 | 234 | return; |
|---|
| 223 | 235 | |
|---|
| 224 | 236 | /* |
|---|
| 225 | | - * srcu critical section ensures that quarantine_remove_cache() |
|---|
| 237 | + * srcu critical section ensures that kasan_quarantine_remove_cache() |
|---|
| 226 | 238 | * will not miss objects belonging to the cache while they are in our |
|---|
| 227 | 239 | * local to_free list. srcu is chosen because (1) it gives us private |
|---|
| 228 | 240 | * grace period domain that does not interfere with anything else, |
|---|
| .. | .. |
|---|
| 237 | 249 | * Update quarantine size in case of hotplug. Allocate a fraction of |
|---|
| 238 | 250 | * the installed memory to quarantine minus per-cpu queue limits. |
|---|
| 239 | 251 | */ |
|---|
| 240 | | - total_size = (READ_ONCE(totalram_pages) << PAGE_SHIFT) / |
|---|
| 252 | + total_size = (totalram_pages() << PAGE_SHIFT) / |
|---|
| 241 | 253 | QUARANTINE_FRACTION; |
|---|
| 242 | 254 | percpu_quarantines = QUARANTINE_PERCPU_SIZE * num_online_cpus(); |
|---|
| 243 | 255 | new_quarantine_size = (total_size < percpu_quarantines) ? |
|---|
| .. | .. |
|---|
| 292 | 304 | struct qlist_head *q; |
|---|
| 293 | 305 | |
|---|
| 294 | 306 | q = this_cpu_ptr(&cpu_quarantine); |
|---|
| 307 | + /* |
|---|
| 308 | + * Ensure the ordering between the writing to q->offline and |
|---|
| 309 | + * per_cpu_remove_cache. Prevent cpu_quarantine from being corrupted |
|---|
| 310 | + * by interrupt. |
|---|
| 311 | + */ |
|---|
| 312 | + if (READ_ONCE(q->offline)) |
|---|
| 313 | + return; |
|---|
| 295 | 314 | qlist_move_cache(q, &to_free, cache); |
|---|
| 296 | 315 | qlist_free_all(&to_free, cache); |
|---|
| 297 | 316 | } |
|---|
| 298 | 317 | |
|---|
| 299 | 318 | /* Free all quarantined objects belonging to cache. */ |
|---|
| 300 | | -void quarantine_remove_cache(struct kmem_cache *cache) |
|---|
| 319 | +void kasan_quarantine_remove_cache(struct kmem_cache *cache) |
|---|
| 301 | 320 | { |
|---|
| 302 | 321 | unsigned long flags, i; |
|---|
| 303 | 322 | struct qlist_head to_free = QLIST_INIT; |
|---|
| 304 | 323 | |
|---|
| 305 | 324 | /* |
|---|
| 306 | 325 | * Must be careful to not miss any objects that are being moved from |
|---|
| 307 | | - * per-cpu list to the global quarantine in quarantine_put(), |
|---|
| 308 | | - * nor objects being freed in quarantine_reduce(). on_each_cpu() |
|---|
| 326 | + * per-cpu list to the global quarantine in kasan_quarantine_put(), |
|---|
| 327 | + * nor objects being freed in kasan_quarantine_reduce(). on_each_cpu() |
|---|
| 309 | 328 | * achieves the first goal, while synchronize_srcu() achieves the |
|---|
| 310 | 329 | * second. |
|---|
| 311 | 330 | */ |
|---|
| .. | .. |
|---|
| 327 | 346 | |
|---|
| 328 | 347 | synchronize_srcu(&remove_cache_srcu); |
|---|
| 329 | 348 | } |
|---|
| 349 | + |
|---|
| 350 | +static int kasan_cpu_online(unsigned int cpu) |
|---|
| 351 | +{ |
|---|
| 352 | + this_cpu_ptr(&cpu_quarantine)->offline = false; |
|---|
| 353 | + return 0; |
|---|
| 354 | +} |
|---|
| 355 | + |
|---|
| 356 | +static int kasan_cpu_offline(unsigned int cpu) |
|---|
| 357 | +{ |
|---|
| 358 | + struct qlist_head *q; |
|---|
| 359 | + |
|---|
| 360 | + q = this_cpu_ptr(&cpu_quarantine); |
|---|
| 361 | + /* Ensure the ordering between the writing to q->offline and |
|---|
| 362 | + * qlist_free_all. Otherwise, cpu_quarantine may be corrupted |
|---|
| 363 | + * by interrupt. |
|---|
| 364 | + */ |
|---|
| 365 | + WRITE_ONCE(q->offline, true); |
|---|
| 366 | + barrier(); |
|---|
| 367 | + qlist_free_all(q, NULL); |
|---|
| 368 | + return 0; |
|---|
| 369 | +} |
|---|
| 370 | + |
|---|
| 371 | +static int __init kasan_cpu_quarantine_init(void) |
|---|
| 372 | +{ |
|---|
| 373 | + int ret = 0; |
|---|
| 374 | + |
|---|
| 375 | + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mm/kasan:online", |
|---|
| 376 | + kasan_cpu_online, kasan_cpu_offline); |
|---|
| 377 | + if (ret < 0) |
|---|
| 378 | + pr_err("kasan cpu quarantine register failed [%d]\n", ret); |
|---|
| 379 | + return ret; |
|---|
| 380 | +} |
|---|
| 381 | +late_initcall(kasan_cpu_quarantine_init); |
|---|