18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Clang Control Flow Integrity (CFI) error and slowpath handling. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2021 Google LLC 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 98c2ecf20Sopenharmony_ci#include <linux/kallsyms.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/printk.h> 138c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 148c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 158c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 168c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 178c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Compiler-defined handler names */ 208c2ecf20Sopenharmony_ci#ifdef CONFIG_CFI_PERMISSIVE 218c2ecf20Sopenharmony_ci#define cfi_failure_handler __ubsan_handle_cfi_check_fail 228c2ecf20Sopenharmony_ci#else 238c2ecf20Sopenharmony_ci#define cfi_failure_handler __ubsan_handle_cfi_check_fail_abort 248c2ecf20Sopenharmony_ci#endif 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline void handle_cfi_failure(void *ptr) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CFI_PERMISSIVE)) 298c2ecf20Sopenharmony_ci WARN_RATELIMIT(1, "CFI failure (target: %pS):\n", ptr); 308c2ecf20Sopenharmony_ci else 318c2ecf20Sopenharmony_ci panic("CFI failure (target: %pS)\n", ptr); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 358c2ecf20Sopenharmony_ci#ifdef CONFIG_CFI_CLANG_SHADOW 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Index type. A 16-bit index can address at most (2^16)-2 pages (taking 388c2ecf20Sopenharmony_ci * into account SHADOW_INVALID), i.e. ~256M with 4k pages. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_citypedef u16 shadow_t; 418c2ecf20Sopenharmony_ci#define SHADOW_INVALID ((shadow_t)~0UL) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct cfi_shadow { 448c2ecf20Sopenharmony_ci /* Page index for the beginning of the shadow */ 458c2ecf20Sopenharmony_ci unsigned long base; 468c2ecf20Sopenharmony_ci /* An array of __cfi_check locations (as indices to the shadow) */ 478c2ecf20Sopenharmony_ci shadow_t shadow[1]; 488c2ecf20Sopenharmony_ci} __packed; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * The shadow covers ~128M from the beginning of the module region. If 528c2ecf20Sopenharmony_ci * the region is larger, we fall back to __module_address for the rest. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci#define __SHADOW_RANGE (_UL(SZ_128M) >> PAGE_SHIFT) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* The in-memory size of struct cfi_shadow, always at least one page */ 578c2ecf20Sopenharmony_ci#define __SHADOW_PAGES ((__SHADOW_RANGE * sizeof(shadow_t)) >> PAGE_SHIFT) 588c2ecf20Sopenharmony_ci#define SHADOW_PAGES max(1UL, __SHADOW_PAGES) 598c2ecf20Sopenharmony_ci#define SHADOW_SIZE (SHADOW_PAGES << PAGE_SHIFT) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* The actual size of the shadow array, minus metadata */ 628c2ecf20Sopenharmony_ci#define SHADOW_ARR_SIZE (SHADOW_SIZE - offsetof(struct cfi_shadow, shadow)) 638c2ecf20Sopenharmony_ci#define SHADOW_ARR_SLOTS (SHADOW_ARR_SIZE / sizeof(shadow_t)) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(shadow_update_lock); 668c2ecf20Sopenharmony_cistatic struct cfi_shadow __rcu *cfi_shadow __read_mostly; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Returns the index in the shadow for the given address */ 698c2ecf20Sopenharmony_cistatic inline int ptr_to_shadow(const struct cfi_shadow *s, unsigned long ptr) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci unsigned long index; 728c2ecf20Sopenharmony_ci unsigned long page = ptr >> PAGE_SHIFT; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (unlikely(page < s->base)) 758c2ecf20Sopenharmony_ci return -1; /* Outside of module area */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci index = page - s->base; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (index >= SHADOW_ARR_SLOTS) 808c2ecf20Sopenharmony_ci return -1; /* Cannot be addressed with shadow */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return (int)index; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Returns the page address for an index in the shadow */ 868c2ecf20Sopenharmony_cistatic inline unsigned long shadow_to_ptr(const struct cfi_shadow *s, 878c2ecf20Sopenharmony_ci int index) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci if (unlikely(index < 0 || index >= SHADOW_ARR_SLOTS)) 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return (s->base + index) << PAGE_SHIFT; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Returns the __cfi_check function address for the given shadow location */ 968c2ecf20Sopenharmony_cistatic inline unsigned long shadow_to_check_fn(const struct cfi_shadow *s, 978c2ecf20Sopenharmony_ci int index) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci if (unlikely(index < 0 || index >= SHADOW_ARR_SLOTS)) 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (unlikely(s->shadow[index] == SHADOW_INVALID)) 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* __cfi_check is always page aligned */ 1068c2ecf20Sopenharmony_ci return (s->base + s->shadow[index]) << PAGE_SHIFT; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void prepare_next_shadow(const struct cfi_shadow __rcu *prev, 1108c2ecf20Sopenharmony_ci struct cfi_shadow *next) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int i, index, check; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Mark everything invalid */ 1158c2ecf20Sopenharmony_ci memset(next->shadow, 0xFF, SHADOW_ARR_SIZE); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (!prev) 1188c2ecf20Sopenharmony_ci return; /* No previous shadow */ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* If the base address didn't change, an update is not needed */ 1218c2ecf20Sopenharmony_ci if (prev->base == next->base) { 1228c2ecf20Sopenharmony_ci memcpy(next->shadow, prev->shadow, SHADOW_ARR_SIZE); 1238c2ecf20Sopenharmony_ci return; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Convert the previous shadow to the new address range */ 1278c2ecf20Sopenharmony_ci for (i = 0; i < SHADOW_ARR_SLOTS; ++i) { 1288c2ecf20Sopenharmony_ci if (prev->shadow[i] == SHADOW_INVALID) 1298c2ecf20Sopenharmony_ci continue; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci index = ptr_to_shadow(next, shadow_to_ptr(prev, i)); 1328c2ecf20Sopenharmony_ci if (index < 0) 1338c2ecf20Sopenharmony_ci continue; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci check = ptr_to_shadow(next, 1368c2ecf20Sopenharmony_ci shadow_to_check_fn(prev, prev->shadow[i])); 1378c2ecf20Sopenharmony_ci if (check < 0) 1388c2ecf20Sopenharmony_ci continue; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci next->shadow[index] = (shadow_t)check; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void add_module_to_shadow(struct cfi_shadow *s, struct module *mod, 1458c2ecf20Sopenharmony_ci unsigned long min_addr, unsigned long max_addr) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int check_index; 1488c2ecf20Sopenharmony_ci unsigned long check = (unsigned long)mod->cfi_check; 1498c2ecf20Sopenharmony_ci unsigned long ptr; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (unlikely(!PAGE_ALIGNED(check))) { 1528c2ecf20Sopenharmony_ci pr_warn("cfi: not using shadow for module %s\n", mod->name); 1538c2ecf20Sopenharmony_ci return; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci check_index = ptr_to_shadow(s, check); 1578c2ecf20Sopenharmony_ci if (check_index < 0) 1588c2ecf20Sopenharmony_ci return; /* Module not addressable with shadow */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* For each page, store the check function index in the shadow */ 1618c2ecf20Sopenharmony_ci for (ptr = min_addr; ptr <= max_addr; ptr += PAGE_SIZE) { 1628c2ecf20Sopenharmony_ci int index = ptr_to_shadow(s, ptr); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (index >= 0) { 1658c2ecf20Sopenharmony_ci /* Each page must only contain one module */ 1668c2ecf20Sopenharmony_ci WARN_ON_ONCE(s->shadow[index] != SHADOW_INVALID); 1678c2ecf20Sopenharmony_ci s->shadow[index] = (shadow_t)check_index; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod, 1738c2ecf20Sopenharmony_ci unsigned long min_addr, unsigned long max_addr) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci unsigned long ptr; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (ptr = min_addr; ptr <= max_addr; ptr += PAGE_SIZE) { 1788c2ecf20Sopenharmony_ci int index = ptr_to_shadow(s, ptr); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (index >= 0) 1818c2ecf20Sopenharmony_ci s->shadow[index] = SHADOW_INVALID; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_citypedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *, 1868c2ecf20Sopenharmony_ci unsigned long min_addr, unsigned long max_addr); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void update_shadow(struct module *mod, unsigned long base_addr, 1898c2ecf20Sopenharmony_ci update_shadow_fn fn) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct cfi_shadow *prev; 1928c2ecf20Sopenharmony_ci struct cfi_shadow *next; 1938c2ecf20Sopenharmony_ci unsigned long min_addr, max_addr; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci next = vmalloc(SHADOW_SIZE); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mutex_lock(&shadow_update_lock); 1988c2ecf20Sopenharmony_ci prev = rcu_dereference_protected(cfi_shadow, 1998c2ecf20Sopenharmony_ci mutex_is_locked(&shadow_update_lock)); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (next) { 2028c2ecf20Sopenharmony_ci next->base = base_addr >> PAGE_SHIFT; 2038c2ecf20Sopenharmony_ci prepare_next_shadow(prev, next); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci min_addr = (unsigned long)mod->core_layout.base; 2068c2ecf20Sopenharmony_ci max_addr = min_addr + mod->core_layout.text_size; 2078c2ecf20Sopenharmony_ci fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci set_memory_ro((unsigned long)next, SHADOW_PAGES); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci rcu_assign_pointer(cfi_shadow, next); 2138c2ecf20Sopenharmony_ci mutex_unlock(&shadow_update_lock); 2148c2ecf20Sopenharmony_ci synchronize_rcu(); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (prev) { 2178c2ecf20Sopenharmony_ci set_memory_rw((unsigned long)prev, SHADOW_PAGES); 2188c2ecf20Sopenharmony_ci vfree(prev); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_civoid cfi_module_add(struct module *mod, unsigned long base_addr) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci update_shadow(mod, base_addr, add_module_to_shadow); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid cfi_module_remove(struct module *mod, unsigned long base_addr) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci update_shadow(mod, base_addr, remove_module_from_shadow); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s, 2338c2ecf20Sopenharmony_ci unsigned long ptr) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci int index; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (unlikely(!s)) 2388c2ecf20Sopenharmony_ci return NULL; /* No shadow available */ 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci index = ptr_to_shadow(s, ptr); 2418c2ecf20Sopenharmony_ci if (index < 0) 2428c2ecf20Sopenharmony_ci return NULL; /* Cannot be addressed with shadow */ 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return (cfi_check_fn)shadow_to_check_fn(s, index); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic inline cfi_check_fn find_shadow_check_fn(unsigned long ptr) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci cfi_check_fn fn; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci rcu_read_lock_sched_notrace(); 2528c2ecf20Sopenharmony_ci fn = ptr_to_check_fn(rcu_dereference_sched(cfi_shadow), ptr); 2538c2ecf20Sopenharmony_ci rcu_read_unlock_sched_notrace(); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return fn; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci#else /* !CONFIG_CFI_CLANG_SHADOW */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic inline cfi_check_fn find_shadow_check_fn(unsigned long ptr) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci return NULL; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#endif /* CONFIG_CFI_CLANG_SHADOW */ 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic inline cfi_check_fn find_module_check_fn(unsigned long ptr) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci cfi_check_fn fn = NULL; 2708c2ecf20Sopenharmony_ci struct module *mod; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci rcu_read_lock_sched_notrace(); 2738c2ecf20Sopenharmony_ci mod = __module_address(ptr); 2748c2ecf20Sopenharmony_ci if (mod) 2758c2ecf20Sopenharmony_ci fn = mod->cfi_check; 2768c2ecf20Sopenharmony_ci rcu_read_unlock_sched_notrace(); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return fn; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic inline cfi_check_fn find_check_fn(unsigned long ptr) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci cfi_check_fn fn = NULL; 2848c2ecf20Sopenharmony_ci unsigned long flags; 2858c2ecf20Sopenharmony_ci bool rcu_idle; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (is_kernel_text(ptr)) 2888c2ecf20Sopenharmony_ci return __cfi_check; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * Indirect call checks can happen when RCU is not watching. Both 2928c2ecf20Sopenharmony_ci * the shadow and __module_address use RCU, so we need to wake it 2938c2ecf20Sopenharmony_ci * up if necessary. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci rcu_idle = !rcu_is_watching(); 2968c2ecf20Sopenharmony_ci if (rcu_idle) { 2978c2ecf20Sopenharmony_ci local_irq_save(flags); 2988c2ecf20Sopenharmony_ci rcu_irq_enter(); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CFI_CLANG_SHADOW)) 3028c2ecf20Sopenharmony_ci fn = find_shadow_check_fn(ptr); 3038c2ecf20Sopenharmony_ci if (!fn) 3048c2ecf20Sopenharmony_ci fn = find_module_check_fn(ptr); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (rcu_idle) { 3078c2ecf20Sopenharmony_ci rcu_irq_exit(); 3088c2ecf20Sopenharmony_ci local_irq_restore(flags); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return fn; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_civoid __cfi_slowpath_diag(uint64_t id, void *ptr, void *diag) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci cfi_check_fn fn = find_check_fn((unsigned long)ptr); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (likely(fn)) 3198c2ecf20Sopenharmony_ci fn(id, ptr, diag); 3208c2ecf20Sopenharmony_ci else /* Don't allow unchecked modules */ 3218c2ecf20Sopenharmony_ci handle_cfi_failure(ptr); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cfi_slowpath_diag); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci#else /* !CONFIG_MODULES */ 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_civoid __cfi_slowpath_diag(uint64_t id, void *ptr, void *diag) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci handle_cfi_failure(ptr); /* No modules */ 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cfi_slowpath_diag); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci#endif /* CONFIG_MODULES */ 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_civoid cfi_failure_handler(void *data, void *ptr, void *vtable) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci handle_cfi_failure(ptr); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_failure_handler); 340