| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
|---|
| 1 | 2 | /* |
|---|
| 2 | | - * CFI (Control Flow Integrity) error and slowpath handling |
|---|
| 3 | + * Clang Control Flow Integrity (CFI) error and slowpath handling. |
|---|
| 3 | 4 | * |
|---|
| 4 | | - * Copyright (C) 2017 Google, Inc. |
|---|
| 5 | + * Copyright (C) 2019 Google LLC |
|---|
| 5 | 6 | */ |
|---|
| 6 | 7 | |
|---|
| 7 | | -#include <linux/gfp.h> |
|---|
| 8 | +#include <linux/hardirq.h> |
|---|
| 9 | +#include <linux/kallsyms.h> |
|---|
| 8 | 10 | #include <linux/module.h> |
|---|
| 11 | +#include <linux/mutex.h> |
|---|
| 9 | 12 | #include <linux/printk.h> |
|---|
| 10 | 13 | #include <linux/ratelimit.h> |
|---|
| 11 | 14 | #include <linux/rcupdate.h> |
|---|
| 12 | | -#include <linux/spinlock.h> |
|---|
| 13 | | -#include <asm/bug.h> |
|---|
| 15 | +#include <linux/vmalloc.h> |
|---|
| 14 | 16 | #include <asm/cacheflush.h> |
|---|
| 15 | 17 | #include <asm/set_memory.h> |
|---|
| 16 | 18 | |
|---|
| .. | .. |
|---|
| 25 | 27 | |
|---|
| 26 | 28 | static inline void handle_cfi_failure(void *ptr) |
|---|
| 27 | 29 | { |
|---|
| 28 | | -#ifdef CONFIG_CFI_PERMISSIVE |
|---|
| 29 | | - WARN_RATELIMIT(1, "CFI failure (target: %pF):\n", ptr); |
|---|
| 30 | | -#else |
|---|
| 31 | | - pr_err("CFI failure (target: %pF):\n", ptr); |
|---|
| 32 | | - BUG(); |
|---|
| 33 | | -#endif |
|---|
| 30 | + if (IS_ENABLED(CONFIG_CFI_PERMISSIVE)) |
|---|
| 31 | + WARN_RATELIMIT(1, "CFI failure (target: %pS):\n", ptr); |
|---|
| 32 | + else |
|---|
| 33 | + panic("CFI failure (target: %pS)\n", ptr); |
|---|
| 34 | 34 | } |
|---|
| 35 | 35 | |
|---|
| 36 | 36 | #ifdef CONFIG_MODULES |
|---|
| 37 | 37 | #ifdef CONFIG_CFI_CLANG_SHADOW |
|---|
| 38 | | -struct shadow_range { |
|---|
| 39 | | - /* Module address range */ |
|---|
| 40 | | - unsigned long mod_min_addr; |
|---|
| 41 | | - unsigned long mod_max_addr; |
|---|
| 42 | | - /* Module page range */ |
|---|
| 43 | | - unsigned long min_page; |
|---|
| 44 | | - unsigned long max_page; |
|---|
| 45 | | -}; |
|---|
| 46 | | - |
|---|
| 47 | | -#define SHADOW_ORDER 1 |
|---|
| 48 | | -#define SHADOW_PAGES (1 << SHADOW_ORDER) |
|---|
| 49 | | -#define SHADOW_SIZE \ |
|---|
| 50 | | - ((SHADOW_PAGES * PAGE_SIZE - sizeof(struct shadow_range)) / sizeof(u16)) |
|---|
| 51 | | -#define SHADOW_INVALID 0xFFFF |
|---|
| 38 | +/* |
|---|
| 39 | + * Index type. A 16-bit index can address at most (2^16)-2 pages (taking |
|---|
| 40 | + * into account SHADOW_INVALID), i.e. ~256M with 4k pages. |
|---|
| 41 | + */ |
|---|
| 42 | +typedef u16 shadow_t; |
|---|
| 43 | +#define SHADOW_INVALID ((shadow_t)~0UL) |
|---|
| 52 | 44 | |
|---|
| 53 | 45 | struct cfi_shadow { |
|---|
| 54 | | - /* Page range covered by the shadow */ |
|---|
| 55 | | - struct shadow_range r; |
|---|
| 56 | | - /* Page offsets to __cfi_check functions in modules */ |
|---|
| 57 | | - u16 shadow[SHADOW_SIZE]; |
|---|
| 58 | | -}; |
|---|
| 46 | + /* Page index for the beginning of the shadow */ |
|---|
| 47 | + unsigned long base; |
|---|
| 48 | + /* An array of __cfi_check locations (as indices to the shadow) */ |
|---|
| 49 | + shadow_t shadow[1]; |
|---|
| 50 | +} __packed; |
|---|
| 59 | 51 | |
|---|
| 60 | | -static DEFINE_SPINLOCK(shadow_update_lock); |
|---|
| 61 | | -static struct cfi_shadow __rcu *cfi_shadow __read_mostly = NULL; |
|---|
| 52 | +/* |
|---|
| 53 | + * The shadow covers ~128M from the beginning of the module region. If |
|---|
| 54 | + * the region is larger, we fall back to __module_address for the rest. |
|---|
| 55 | + */ |
|---|
| 56 | +#define __SHADOW_RANGE (_UL(SZ_128M) >> PAGE_SHIFT) |
|---|
| 62 | 57 | |
|---|
| 58 | +/* The in-memory size of struct cfi_shadow, always at least one page */ |
|---|
| 59 | +#define __SHADOW_PAGES ((__SHADOW_RANGE * sizeof(shadow_t)) >> PAGE_SHIFT) |
|---|
| 60 | +#define SHADOW_PAGES max(1UL, __SHADOW_PAGES) |
|---|
| 61 | +#define SHADOW_SIZE (SHADOW_PAGES << PAGE_SHIFT) |
|---|
| 62 | + |
|---|
| 63 | +/* The actual size of the shadow array, minus metadata */ |
|---|
| 64 | +#define SHADOW_ARR_SIZE (SHADOW_SIZE - offsetof(struct cfi_shadow, shadow)) |
|---|
| 65 | +#define SHADOW_ARR_SLOTS (SHADOW_ARR_SIZE / sizeof(shadow_t)) |
|---|
| 66 | + |
|---|
| 67 | +static DEFINE_MUTEX(shadow_update_lock); |
|---|
| 68 | +static struct cfi_shadow __rcu *cfi_shadow __read_mostly; |
|---|
| 69 | + |
|---|
| 70 | +/* Returns the index in the shadow for the given address */ |
|---|
| 63 | 71 | static inline int ptr_to_shadow(const struct cfi_shadow *s, unsigned long ptr) |
|---|
| 64 | 72 | { |
|---|
| 65 | 73 | unsigned long index; |
|---|
| 66 | 74 | unsigned long page = ptr >> PAGE_SHIFT; |
|---|
| 67 | 75 | |
|---|
| 68 | | - if (unlikely(page < s->r.min_page)) |
|---|
| 76 | + if (unlikely(page < s->base)) |
|---|
| 69 | 77 | return -1; /* Outside of module area */ |
|---|
| 70 | 78 | |
|---|
| 71 | | - index = page - s->r.min_page; |
|---|
| 79 | + index = page - s->base; |
|---|
| 72 | 80 | |
|---|
| 73 | | - if (index >= SHADOW_SIZE) |
|---|
| 81 | + if (index >= SHADOW_ARR_SLOTS) |
|---|
| 74 | 82 | return -1; /* Cannot be addressed with shadow */ |
|---|
| 75 | 83 | |
|---|
| 76 | 84 | return (int)index; |
|---|
| 77 | 85 | } |
|---|
| 78 | 86 | |
|---|
| 87 | +/* Returns the page address for an index in the shadow */ |
|---|
| 79 | 88 | static inline unsigned long shadow_to_ptr(const struct cfi_shadow *s, |
|---|
| 80 | 89 | int index) |
|---|
| 81 | 90 | { |
|---|
| 82 | | - BUG_ON(index < 0 || index >= SHADOW_SIZE); |
|---|
| 91 | + if (unlikely(index < 0 || index >= SHADOW_ARR_SLOTS)) |
|---|
| 92 | + return 0; |
|---|
| 93 | + |
|---|
| 94 | + return (s->base + index) << PAGE_SHIFT; |
|---|
| 95 | +} |
|---|
| 96 | + |
|---|
| 97 | +/* Returns the __cfi_check function address for the given shadow location */ |
|---|
| 98 | +static inline unsigned long shadow_to_check_fn(const struct cfi_shadow *s, |
|---|
| 99 | + int index) |
|---|
| 100 | +{ |
|---|
| 101 | + if (unlikely(index < 0 || index >= SHADOW_ARR_SLOTS)) |
|---|
| 102 | + return 0; |
|---|
| 83 | 103 | |
|---|
| 84 | 104 | if (unlikely(s->shadow[index] == SHADOW_INVALID)) |
|---|
| 85 | 105 | return 0; |
|---|
| 86 | 106 | |
|---|
| 87 | | - return (s->r.min_page + s->shadow[index]) << PAGE_SHIFT; |
|---|
| 88 | | -} |
|---|
| 89 | | - |
|---|
| 90 | | -static inline unsigned long shadow_to_page(const struct cfi_shadow *s, |
|---|
| 91 | | - int index) |
|---|
| 92 | | -{ |
|---|
| 93 | | - BUG_ON(index < 0 || index >= SHADOW_SIZE); |
|---|
| 94 | | - |
|---|
| 95 | | - return (s->r.min_page + index) << PAGE_SHIFT; |
|---|
| 107 | + /* __cfi_check is always page aligned */ |
|---|
| 108 | + return (s->base + s->shadow[index]) << PAGE_SHIFT; |
|---|
| 96 | 109 | } |
|---|
| 97 | 110 | |
|---|
| 98 | 111 | static void prepare_next_shadow(const struct cfi_shadow __rcu *prev, |
|---|
| .. | .. |
|---|
| 101 | 114 | int i, index, check; |
|---|
| 102 | 115 | |
|---|
| 103 | 116 | /* Mark everything invalid */ |
|---|
| 104 | | - memset(next->shadow, 0xFF, sizeof(next->shadow)); |
|---|
| 117 | + memset(next->shadow, 0xFF, SHADOW_ARR_SIZE); |
|---|
| 105 | 118 | |
|---|
| 106 | 119 | if (!prev) |
|---|
| 107 | 120 | return; /* No previous shadow */ |
|---|
| 108 | 121 | |
|---|
| 109 | | - /* If the base address didn't change, update is not needed */ |
|---|
| 110 | | - if (prev->r.min_page == next->r.min_page) { |
|---|
| 111 | | - memcpy(next->shadow, prev->shadow, sizeof(next->shadow)); |
|---|
| 122 | + /* If the base address didn't change, an update is not needed */ |
|---|
| 123 | + if (prev->base == next->base) { |
|---|
| 124 | + memcpy(next->shadow, prev->shadow, SHADOW_ARR_SIZE); |
|---|
| 112 | 125 | return; |
|---|
| 113 | 126 | } |
|---|
| 114 | 127 | |
|---|
| 115 | 128 | /* Convert the previous shadow to the new address range */ |
|---|
| 116 | | - for (i = 0; i < SHADOW_SIZE; ++i) { |
|---|
| 129 | + for (i = 0; i < SHADOW_ARR_SLOTS; ++i) { |
|---|
| 117 | 130 | if (prev->shadow[i] == SHADOW_INVALID) |
|---|
| 118 | 131 | continue; |
|---|
| 119 | 132 | |
|---|
| 120 | | - index = ptr_to_shadow(next, shadow_to_page(prev, i)); |
|---|
| 133 | + index = ptr_to_shadow(next, shadow_to_ptr(prev, i)); |
|---|
| 121 | 134 | if (index < 0) |
|---|
| 122 | 135 | continue; |
|---|
| 123 | 136 | |
|---|
| 124 | 137 | check = ptr_to_shadow(next, |
|---|
| 125 | | - shadow_to_ptr(prev, prev->shadow[i])); |
|---|
| 138 | + shadow_to_check_fn(prev, prev->shadow[i])); |
|---|
| 126 | 139 | if (check < 0) |
|---|
| 127 | 140 | continue; |
|---|
| 128 | 141 | |
|---|
| 129 | | - next->shadow[index] = (u16)check; |
|---|
| 142 | + next->shadow[index] = (shadow_t)check; |
|---|
| 130 | 143 | } |
|---|
| 131 | 144 | } |
|---|
| 132 | 145 | |
|---|
| 133 | | -static void add_module_to_shadow(struct cfi_shadow *s, struct module *mod) |
|---|
| 146 | +static void add_module_to_shadow(struct cfi_shadow *s, struct module *mod, |
|---|
| 147 | + unsigned long min_addr, unsigned long max_addr) |
|---|
| 134 | 148 | { |
|---|
| 135 | | - unsigned long ptr; |
|---|
| 136 | | - unsigned long min_page_addr; |
|---|
| 137 | | - unsigned long max_page_addr; |
|---|
| 149 | + int check_index; |
|---|
| 138 | 150 | unsigned long check = (unsigned long)mod->cfi_check; |
|---|
| 139 | | - int check_index = ptr_to_shadow(s, check); |
|---|
| 151 | + unsigned long ptr; |
|---|
| 140 | 152 | |
|---|
| 141 | | - BUG_ON((check & PAGE_MASK) != check); /* Must be page aligned */ |
|---|
| 153 | + if (unlikely(!PAGE_ALIGNED(check))) { |
|---|
| 154 | + pr_warn("cfi: not using shadow for module %s\n", mod->name); |
|---|
| 155 | + return; |
|---|
| 156 | + } |
|---|
| 142 | 157 | |
|---|
| 158 | + check_index = ptr_to_shadow(s, check); |
|---|
| 143 | 159 | if (check_index < 0) |
|---|
| 144 | 160 | return; /* Module not addressable with shadow */ |
|---|
| 145 | 161 | |
|---|
| 146 | | - min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK; |
|---|
| 147 | | - max_page_addr = (unsigned long)mod->core_layout.base + |
|---|
| 148 | | - mod->core_layout.text_size; |
|---|
| 149 | | - max_page_addr &= PAGE_MASK; |
|---|
| 150 | | - |
|---|
| 151 | 162 | /* For each page, store the check function index in the shadow */ |
|---|
| 152 | | - for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) { |
|---|
| 163 | + for (ptr = min_addr; ptr <= max_addr; ptr += PAGE_SIZE) { |
|---|
| 153 | 164 | int index = ptr_to_shadow(s, ptr); |
|---|
| 165 | + |
|---|
| 154 | 166 | if (index >= 0) { |
|---|
| 155 | | - /* Assume a page only contains code for one module */ |
|---|
| 156 | | - BUG_ON(s->shadow[index] != SHADOW_INVALID); |
|---|
| 157 | | - s->shadow[index] = (u16)check_index; |
|---|
| 167 | + /* Each page must only contain one module */ |
|---|
| 168 | + WARN_ON_ONCE(s->shadow[index] != SHADOW_INVALID); |
|---|
| 169 | + s->shadow[index] = (shadow_t)check_index; |
|---|
| 158 | 170 | } |
|---|
| 159 | 171 | } |
|---|
| 160 | 172 | } |
|---|
| 161 | 173 | |
|---|
| 162 | | -static void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod) |
|---|
| 174 | +static void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod, |
|---|
| 175 | + unsigned long min_addr, unsigned long max_addr) |
|---|
| 163 | 176 | { |
|---|
| 164 | 177 | unsigned long ptr; |
|---|
| 165 | | - unsigned long min_page_addr; |
|---|
| 166 | | - unsigned long max_page_addr; |
|---|
| 167 | 178 | |
|---|
| 168 | | - min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK; |
|---|
| 169 | | - max_page_addr = (unsigned long)mod->core_layout.base + |
|---|
| 170 | | - mod->core_layout.text_size; |
|---|
| 171 | | - max_page_addr &= PAGE_MASK; |
|---|
| 172 | | - |
|---|
| 173 | | - for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) { |
|---|
| 179 | + for (ptr = min_addr; ptr <= max_addr; ptr += PAGE_SIZE) { |
|---|
| 174 | 180 | int index = ptr_to_shadow(s, ptr); |
|---|
| 181 | + |
|---|
| 175 | 182 | if (index >= 0) |
|---|
| 176 | 183 | s->shadow[index] = SHADOW_INVALID; |
|---|
| 177 | 184 | } |
|---|
| 178 | 185 | } |
|---|
| 179 | 186 | |
|---|
| 180 | | -typedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *); |
|---|
| 187 | +typedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *, |
|---|
| 188 | + unsigned long min_addr, unsigned long max_addr); |
|---|
| 181 | 189 | |
|---|
| 182 | | -static void update_shadow(struct module *mod, unsigned long min_addr, |
|---|
| 183 | | - unsigned long max_addr, update_shadow_fn fn) |
|---|
| 190 | +static void update_shadow(struct module *mod, unsigned long base_addr, |
|---|
| 191 | + update_shadow_fn fn) |
|---|
| 184 | 192 | { |
|---|
| 185 | 193 | struct cfi_shadow *prev; |
|---|
| 186 | | - struct cfi_shadow *next = (struct cfi_shadow *) |
|---|
| 187 | | - __get_free_pages(GFP_KERNEL, SHADOW_ORDER); |
|---|
| 194 | + struct cfi_shadow *next; |
|---|
| 195 | + unsigned long min_addr, max_addr; |
|---|
| 188 | 196 | |
|---|
| 189 | | - BUG_ON(!next); |
|---|
| 197 | + next = (struct cfi_shadow *)vmalloc(SHADOW_SIZE); |
|---|
| 198 | + WARN_ON(!next); |
|---|
| 190 | 199 | |
|---|
| 191 | | - next->r.mod_min_addr = min_addr; |
|---|
| 192 | | - next->r.mod_max_addr = max_addr; |
|---|
| 193 | | - next->r.min_page = min_addr >> PAGE_SHIFT; |
|---|
| 194 | | - next->r.max_page = max_addr >> PAGE_SHIFT; |
|---|
| 200 | + mutex_lock(&shadow_update_lock); |
|---|
| 201 | + prev = rcu_dereference_protected(cfi_shadow, |
|---|
| 202 | + mutex_is_locked(&shadow_update_lock)); |
|---|
| 195 | 203 | |
|---|
| 196 | | - spin_lock(&shadow_update_lock); |
|---|
| 197 | | - prev = rcu_dereference_protected(cfi_shadow, 1); |
|---|
| 198 | | - prepare_next_shadow(prev, next); |
|---|
| 204 | + if (next) { |
|---|
| 205 | + next->base = base_addr >> PAGE_SHIFT; |
|---|
| 206 | + prepare_next_shadow(prev, next); |
|---|
| 199 | 207 | |
|---|
| 200 | | - fn(next, mod); |
|---|
| 201 | | - set_memory_ro((unsigned long)next, SHADOW_PAGES); |
|---|
| 208 | + min_addr = (unsigned long)mod->core_layout.base; |
|---|
| 209 | + max_addr = min_addr + mod->core_layout.text_size; |
|---|
| 210 | + fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK); |
|---|
| 211 | + |
|---|
| 212 | + set_memory_ro((unsigned long)next, SHADOW_PAGES); |
|---|
| 213 | + } |
|---|
| 214 | + |
|---|
| 202 | 215 | rcu_assign_pointer(cfi_shadow, next); |
|---|
| 203 | | - |
|---|
| 204 | | - spin_unlock(&shadow_update_lock); |
|---|
| 205 | | - synchronize_rcu(); |
|---|
| 216 | + mutex_unlock(&shadow_update_lock); |
|---|
| 217 | + synchronize_rcu_expedited(); |
|---|
| 206 | 218 | |
|---|
| 207 | 219 | if (prev) { |
|---|
| 208 | 220 | set_memory_rw((unsigned long)prev, SHADOW_PAGES); |
|---|
| 209 | | - free_pages((unsigned long)prev, SHADOW_ORDER); |
|---|
| 221 | + vfree(prev); |
|---|
| 210 | 222 | } |
|---|
| 211 | 223 | } |
|---|
| 212 | 224 | |
|---|
| 213 | | -void cfi_module_add(struct module *mod, unsigned long min_addr, |
|---|
| 214 | | - unsigned long max_addr) |
|---|
| 225 | +void cfi_module_add(struct module *mod, unsigned long base_addr) |
|---|
| 215 | 226 | { |
|---|
| 216 | | - update_shadow(mod, min_addr, max_addr, add_module_to_shadow); |
|---|
| 227 | + update_shadow(mod, base_addr, add_module_to_shadow); |
|---|
| 217 | 228 | } |
|---|
| 218 | | -EXPORT_SYMBOL_GPL(cfi_module_add); |
|---|
| 219 | 229 | |
|---|
| 220 | | -void cfi_module_remove(struct module *mod, unsigned long min_addr, |
|---|
| 221 | | - unsigned long max_addr) |
|---|
| 230 | +void cfi_module_remove(struct module *mod, unsigned long base_addr) |
|---|
| 222 | 231 | { |
|---|
| 223 | | - update_shadow(mod, min_addr, max_addr, remove_module_from_shadow); |
|---|
| 232 | + update_shadow(mod, base_addr, remove_module_from_shadow); |
|---|
| 224 | 233 | } |
|---|
| 225 | | -EXPORT_SYMBOL_GPL(cfi_module_remove); |
|---|
| 226 | 234 | |
|---|
| 227 | 235 | static inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s, |
|---|
| 228 | 236 | unsigned long ptr) |
|---|
| .. | .. |
|---|
| 232 | 240 | if (unlikely(!s)) |
|---|
| 233 | 241 | return NULL; /* No shadow available */ |
|---|
| 234 | 242 | |
|---|
| 235 | | - if (ptr < s->r.mod_min_addr || ptr > s->r.mod_max_addr) |
|---|
| 236 | | - return NULL; /* Not in a mapped module */ |
|---|
| 237 | | - |
|---|
| 238 | 243 | index = ptr_to_shadow(s, ptr); |
|---|
| 239 | 244 | if (index < 0) |
|---|
| 240 | 245 | return NULL; /* Cannot be addressed with shadow */ |
|---|
| 241 | 246 | |
|---|
| 242 | | - return (cfi_check_fn)shadow_to_ptr(s, index); |
|---|
| 247 | + return (cfi_check_fn)shadow_to_check_fn(s, index); |
|---|
| 243 | 248 | } |
|---|
| 249 | + |
|---|
| 250 | +static inline cfi_check_fn __find_shadow_check_fn(unsigned long ptr) |
|---|
| 251 | +{ |
|---|
| 252 | + cfi_check_fn fn; |
|---|
| 253 | + |
|---|
| 254 | + rcu_read_lock_sched_notrace(); |
|---|
| 255 | + fn = ptr_to_check_fn(rcu_dereference_sched(cfi_shadow), ptr); |
|---|
| 256 | + rcu_read_unlock_sched_notrace(); |
|---|
| 257 | + |
|---|
| 258 | + return fn; |
|---|
| 259 | +} |
|---|
| 260 | + |
|---|
| 261 | +#else /* !CONFIG_CFI_CLANG_SHADOW */ |
|---|
| 262 | + |
|---|
| 263 | +static inline cfi_check_fn __find_shadow_check_fn(unsigned long ptr) |
|---|
| 264 | +{ |
|---|
| 265 | + return NULL; |
|---|
| 266 | +} |
|---|
| 267 | + |
|---|
| 244 | 268 | #endif /* CONFIG_CFI_CLANG_SHADOW */ |
|---|
| 245 | 269 | |
|---|
| 246 | | -static inline cfi_check_fn find_module_cfi_check(void *ptr) |
|---|
| 270 | +static inline cfi_check_fn __find_module_check_fn(unsigned long ptr) |
|---|
| 247 | 271 | { |
|---|
| 272 | + cfi_check_fn fn = NULL; |
|---|
| 248 | 273 | struct module *mod; |
|---|
| 249 | 274 | |
|---|
| 250 | | - preempt_disable(); |
|---|
| 251 | | - mod = __module_address((unsigned long)ptr); |
|---|
| 252 | | - preempt_enable(); |
|---|
| 253 | | - |
|---|
| 275 | + rcu_read_lock_sched_notrace(); |
|---|
| 276 | + mod = __module_address(ptr); |
|---|
| 254 | 277 | if (mod) |
|---|
| 255 | | - return mod->cfi_check; |
|---|
| 278 | + fn = mod->cfi_check; |
|---|
| 279 | + rcu_read_unlock_sched_notrace(); |
|---|
| 256 | 280 | |
|---|
| 257 | | - return CFI_CHECK_FN; |
|---|
| 281 | + return fn; |
|---|
| 258 | 282 | } |
|---|
| 259 | 283 | |
|---|
| 260 | | -static inline cfi_check_fn find_cfi_check(void *ptr) |
|---|
| 284 | +static inline cfi_check_fn find_check_fn(unsigned long ptr) |
|---|
| 261 | 285 | { |
|---|
| 262 | | -#ifdef CONFIG_CFI_CLANG_SHADOW |
|---|
| 263 | | - cfi_check_fn f; |
|---|
| 264 | | - |
|---|
| 265 | | - if (!rcu_access_pointer(cfi_shadow)) |
|---|
| 266 | | - return CFI_CHECK_FN; /* No loaded modules */ |
|---|
| 267 | | - |
|---|
| 268 | | - /* Look up the __cfi_check function to use */ |
|---|
| 269 | | - rcu_read_lock(); |
|---|
| 270 | | - f = ptr_to_check_fn(rcu_dereference(cfi_shadow), (unsigned long)ptr); |
|---|
| 271 | | - rcu_read_unlock(); |
|---|
| 272 | | - |
|---|
| 273 | | - if (f) |
|---|
| 274 | | - return f; |
|---|
| 286 | + bool rcu; |
|---|
| 287 | + cfi_check_fn fn = NULL; |
|---|
| 275 | 288 | |
|---|
| 276 | 289 | /* |
|---|
| 277 | | - * Fall back to find_module_cfi_check, which works also for a larger |
|---|
| 278 | | - * module address space, but is slower. |
|---|
| 290 | + * Indirect call checks can happen when RCU is not watching. Both |
|---|
| 291 | + * the shadow and __module_address use RCU, so we need to wake it |
|---|
| 292 | + * up before proceeding. Use rcu_nmi_enter/exit() as these calls |
|---|
| 293 | + * can happen anywhere. |
|---|
| 279 | 294 | */ |
|---|
| 280 | | -#endif /* CONFIG_CFI_CLANG_SHADOW */ |
|---|
| 295 | + rcu = rcu_is_watching(); |
|---|
| 296 | + if (!rcu) |
|---|
| 297 | + rcu_nmi_enter(); |
|---|
| 281 | 298 | |
|---|
| 282 | | - return find_module_cfi_check(ptr); |
|---|
| 299 | + if (IS_ENABLED(CONFIG_CFI_CLANG_SHADOW)) { |
|---|
| 300 | + fn = __find_shadow_check_fn(ptr); |
|---|
| 301 | + if (fn) |
|---|
| 302 | + goto out; |
|---|
| 303 | + } |
|---|
| 304 | + |
|---|
| 305 | + if (is_kernel_text(ptr)) { |
|---|
| 306 | + fn = __cfi_check; |
|---|
| 307 | + goto out; |
|---|
| 308 | + } |
|---|
| 309 | + |
|---|
| 310 | + fn = __find_module_check_fn(ptr); |
|---|
| 311 | + |
|---|
| 312 | +out: |
|---|
| 313 | + if (!rcu) |
|---|
| 314 | + rcu_nmi_exit(); |
|---|
| 315 | + |
|---|
| 316 | + return fn; |
|---|
| 283 | 317 | } |
|---|
| 284 | 318 | |
|---|
| 285 | 319 | void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag) |
|---|
| 286 | 320 | { |
|---|
| 287 | | - cfi_check_fn check = find_cfi_check(ptr); |
|---|
| 321 | + cfi_check_fn fn = find_check_fn((unsigned long)ptr); |
|---|
| 288 | 322 | |
|---|
| 289 | | - if (likely(check)) |
|---|
| 290 | | - check(id, ptr, diag); |
|---|
| 323 | + if (!IS_ENABLED(CONFIG_CFI_PERMISSIVE)) |
|---|
| 324 | + diag = NULL; |
|---|
| 325 | + |
|---|
| 326 | + if (likely(fn)) |
|---|
| 327 | + fn(id, ptr, diag); |
|---|
| 291 | 328 | else /* Don't allow unchecked modules */ |
|---|
| 292 | 329 | handle_cfi_failure(ptr); |
|---|
| 293 | 330 | } |
|---|
| 294 | | -EXPORT_SYMBOL_GPL(cfi_slowpath_handler); |
|---|
| 331 | + |
|---|
| 332 | +#else /* !CONFIG_MODULES */ |
|---|
| 333 | + |
|---|
| 334 | +void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag) |
|---|
| 335 | +{ |
|---|
| 336 | + handle_cfi_failure(ptr); /* No modules */ |
|---|
| 337 | +} |
|---|
| 338 | + |
|---|
| 295 | 339 | #endif /* CONFIG_MODULES */ |
|---|
| 340 | + |
|---|
| 341 | +EXPORT_SYMBOL(cfi_slowpath_handler); |
|---|
| 296 | 342 | |
|---|
| 297 | 343 | void cfi_failure_handler(void *data, void *ptr, void *vtable) |
|---|
| 298 | 344 | { |
|---|
| 299 | 345 | handle_cfi_failure(ptr); |
|---|
| 300 | 346 | } |
|---|
| 301 | | -EXPORT_SYMBOL_GPL(cfi_failure_handler); |
|---|
| 347 | +EXPORT_SYMBOL(cfi_failure_handler); |
|---|
| 302 | 348 | |
|---|
| 303 | 349 | void __cfi_check_fail(void *data, void *ptr) |
|---|
| 304 | 350 | { |
|---|