1570af302Sopenharmony_ci/* 2570af302Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd. 3570af302Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4570af302Sopenharmony_ci * you may not use this file except in compliance with the License. 5570af302Sopenharmony_ci * You may obtain a copy of the License at 6570af302Sopenharmony_ci * 7570af302Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8570af302Sopenharmony_ci * 9570af302Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10570af302Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11570af302Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12570af302Sopenharmony_ci * See the License for the specific language governing permissions and 13570af302Sopenharmony_ci * limitations under the License. 14570af302Sopenharmony_ci */ 15570af302Sopenharmony_ci 16570af302Sopenharmony_ci#define _GNU_SOURCE 17570af302Sopenharmony_ci#include <sys/mman.h> 18570af302Sopenharmony_ci#include <sys/prctl.h> 19570af302Sopenharmony_ci#include "cfi.h" 20570af302Sopenharmony_ci#include "ld_log.h" 21570af302Sopenharmony_ci#include "namespace.h" 22570af302Sopenharmony_ci 23570af302Sopenharmony_ci/* This module provides support for LLVM CFI Cross-DSO by implementing the __cfi_slowpath() and __cfi_slowpath_diag() 24570af302Sopenharmony_ci * functions. These two functions will be called before visiting other dso's resources. The responsibility is to 25570af302Sopenharmony_ci * calculate the __cfi_check() of the target dso, and call it. So use CFI shadow and shadow value to store the 26570af302Sopenharmony_ci * relationship between dso and its __cfi_check addr while loading a dso. CFI shadow is an array which stores shadow 27570af302Sopenharmony_ci * values. Shadow value is used to store the relationship. A shadow value can map 1 LIBRARY_ALIGNMENT memory range. So 28570af302Sopenharmony_ci * each dso will be mapped to one or more shadow values in the CFI shadow, this depends on the address range of the 29570af302Sopenharmony_ci * dso. 30570af302Sopenharmony_ci * There are 3 types for shadow value: 31570af302Sopenharmony_ci * - invalid(0) : the target addr does not belongs to any loaded dso. 32570af302Sopenharmony_ci * - uncheck(1) : this LIBRARY_ALIGNMENT memory range belongs to a dso but it is no need to do the CFI check. 33570af302Sopenharmony_ci * - valid(2 - 0xFFFF) : this LIBRARY_ALIGNMENT memory range belongs to a dso and need to do the CFI check. 34570af302Sopenharmony_ci * The valid shadow value records the distance from the end of a LIBRARY_ALIGNMENT memory range to the __cfi_check addr 35570af302Sopenharmony_ci * of the dso (The unit is 4096, because the __cfi_check is aligned with 4096). 36570af302Sopenharmony_ci * The valid shadow value is calculated as below: 37570af302Sopenharmony_ci * sv = (AlignUp(__cfi_check, LIBRARY_ALIGNMENT) - __cfi_check + N * LIBRARY_ALIGNMENT) / 4096 + 2; 38570af302Sopenharmony_ci * 39570af302Sopenharmony_ci * N : starts at 0, is the index of LIBRARY_ALIGNMENT memory range that belongs to a dso. 40570af302Sopenharmony_ci * + 2 : to avoid conflict with invalid and uncheck shadow value. 41570af302Sopenharmony_ci * 42570af302Sopenharmony_ci * Below is a example for calculating shadow values of a dso. 43570af302Sopenharmony_ci * liba.so 44570af302Sopenharmony_ci * /\ 45570af302Sopenharmony_ci * /'''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''\ 46570af302Sopenharmony_ci * 0x40000 __cfi_check addr = 0x42000 0x80000 0xA0000 0xC0000 47570af302Sopenharmony_ci * +---------^----------------------------------------^-------------------------^-------------------------+ 48570af302Sopenharmony_ci * Memory | | | | | 49570af302Sopenharmony_ci * +------------------------------------------------------------------------------------------------------+ 50570af302Sopenharmony_ci * \........... LIBRARY_ALIGNMENT ..................../\........... LIBRARY_ALIGNMENT ..................../ 51570af302Sopenharmony_ci * \ / / 52570af302Sopenharmony_ci * \ / / 53570af302Sopenharmony_ci * \ / / 54570af302Sopenharmony_ci * \ / / 55570af302Sopenharmony_ci * \ / / 56570af302Sopenharmony_ci * +-----------------------------------------------------------------------------------------------------+ 57570af302Sopenharmony_ci * CFI shadow | invalid | sv1 | sv2 | invalid | 58570af302Sopenharmony_ci * +-----------------------------------------------------------------------------------------------------+ 59570af302Sopenharmony_ci * sv1 = (0x80000 - 0x42000 + 0 * LIBRARY_ALIGNMENT) / 4096 + 2 = 64 60570af302Sopenharmony_ci * sv2 = (0x80000 - 0x42000 + 1 * LIBRARY_ALIGNMENT) / 4096 + 2 = 126 61570af302Sopenharmony_ci * 62570af302Sopenharmony_ci * Calculating the __cfi_check address is a reverse process: 63570af302Sopenharmony_ci * - First align up the target addr with LIBRARY_ALIGNMENT to locate the corresponding shadow value. 64570af302Sopenharmony_ci * - Then calculate the __cfi_check addr. 65570af302Sopenharmony_ci * 66570af302Sopenharmony_ci * In order for the algorithm to work well, the start addr of each dso should be aligned with LIBRARY_ALIGNMENT. */ 67570af302Sopenharmony_ci 68570af302Sopenharmony_ci#define MAX(a, b) (((a) > (b)) ? (a) : (b)) 69570af302Sopenharmony_ci#define MIN(a, b) (((a) < (b)) ? (a) : (b)) 70570af302Sopenharmony_ci#define ALIGN_UP(a, b) (((a) + (b) - 1) & -(b)) 71570af302Sopenharmony_ci#define ALIGN_DOWN(a, b) ((a) & -(b)) 72570af302Sopenharmony_ci#if DL_FDPIC 73570af302Sopenharmony_ci#define LADDR(p, v) laddr((p), (v)) 74570af302Sopenharmony_ci#else 75570af302Sopenharmony_ci#define LADDR(p, v) (void *)((p)->base + (v)) 76570af302Sopenharmony_ci#endif 77570af302Sopenharmony_ci 78570af302Sopenharmony_ci/* Function ptr for __cfi_check() */ 79570af302Sopenharmony_citypedef int (*cfi_check_t)(uint64_t, void *, void *); 80570af302Sopenharmony_ci 81570af302Sopenharmony_cistatic const uintptr_t shadow_granularity = LIBRARY_ALIGNMENT_BITS; 82570af302Sopenharmony_cistatic const uintptr_t cfi_check_granularity = 12; 83570af302Sopenharmony_ci// __cfi_check should be 4k aligned. 84570af302Sopenharmony_cistatic const uintptr_t cfi_check_alignment = 1UL << cfi_check_granularity; 85570af302Sopenharmony_cistatic const uintptr_t shadow_alignment = 1UL << shadow_granularity; 86570af302Sopenharmony_cistatic const uint16_t shadow_value_step = 1 << (shadow_granularity - cfi_check_granularity); 87570af302Sopenharmony_ci 88570af302Sopenharmony_cistatic uintptr_t shadow_size = 0; 89570af302Sopenharmony_ci/* Start addr of the CFI shadow */ 90570af302Sopenharmony_cistatic char *cfi_shadow_start = NULL; 91570af302Sopenharmony_ci/* List head of all the DSOs loaded by the process */ 92570af302Sopenharmony_cistatic struct dso *dso_list_head = NULL; 93570af302Sopenharmony_ci 94570af302Sopenharmony_cistatic struct dso *pldso = NULL; 95570af302Sopenharmony_cistatic struct dso *r_app = NULL; 96570af302Sopenharmony_cistatic struct dso *r_vdso = NULL; 97570af302Sopenharmony_ci 98570af302Sopenharmony_ci/* Shadow value */ 99570af302Sopenharmony_ci/* The related shadow value(s) will be set to `sv_invalid` when: 100570af302Sopenharmony_ci * - init CFI shadow. 101570af302Sopenharmony_ci * - removing a dso. */ 102570af302Sopenharmony_cistatic const uint16_t sv_invalid = 0; 103570af302Sopenharmony_ci/* The related shadow value(s) will be set to `sv_uncheck` if: 104570af302Sopenharmony_ci * - the DSO does not enable CFI Cross-Dso. 105570af302Sopenharmony_ci * - the DSO enabled CFI Cross-Dso, but this DSO is larger than 16G, for the part of the dso that exceeds 16G, 106570af302Sopenharmony_ci * its shadow value will be set to `sv_uncheck`. */ 107570af302Sopenharmony_cistatic const uint16_t sv_uncheck = 1; 108570af302Sopenharmony_ci/* If a DSO enabled CFI Cross-Dso, the DSO's shadow value should be valid. Because of the defination of `sv_invalid` 109570af302Sopenharmony_ci * and `sv_unchecked`, the valid shadow value should be at least 2. */ 110570af302Sopenharmony_cistatic const uint16_t sv_valid_min = 2; 111570af302Sopenharmony_ci 112570af302Sopenharmony_ci#if defined(__LP64__) 113570af302Sopenharmony_cistatic const uintptr_t max_target_addr = 0xffffffffffff; 114570af302Sopenharmony_ci#else 115570af302Sopenharmony_cistatic const uintptr_t max_target_addr = 0xffffffff; 116570af302Sopenharmony_ci#endif 117570af302Sopenharmony_ci 118570af302Sopenharmony_ci/* Create a cfi shadow */ 119570af302Sopenharmony_cistatic int create_cfi_shadow(void); 120570af302Sopenharmony_ci 121570af302Sopenharmony_ci/* Map dsos to CFI shadow */ 122570af302Sopenharmony_cistatic int add_dso_to_cfi_shadow(struct dso *dso); 123570af302Sopenharmony_cistatic int fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type); 124570af302Sopenharmony_ci 125570af302Sopenharmony_ci/* Find the __cfi_check() of target dso and call it */ 126570af302Sopenharmony_civoid __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr); 127570af302Sopenharmony_civoid __cfi_slowpath_diag(uint64_t call_site_type_id, void *func_ptr, void *diag_data); 128570af302Sopenharmony_ci 129570af302Sopenharmony_cistatic inline uintptr_t addr_to_offset(uintptr_t addr, int bits) 130570af302Sopenharmony_ci{ 131570af302Sopenharmony_ci /* Convert addr to CFI shadow offset. 132570af302Sopenharmony_ci * Shift left 1 bit because the shadow value is uint16_t. */ 133570af302Sopenharmony_ci return (addr >> bits) << 1; 134570af302Sopenharmony_ci} 135570af302Sopenharmony_ci 136570af302Sopenharmony_cistatic struct symdef find_cfi_check_sym(struct dso *p) 137570af302Sopenharmony_ci{ 138570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 139570af302Sopenharmony_ci 140570af302Sopenharmony_ci struct verinfo verinfo = { .s = "__cfi_check", .v = "", .use_vna_hash = false }; 141570af302Sopenharmony_ci struct sym_info_pair s_info_p = gnu_hash(verinfo.s); 142570af302Sopenharmony_ci return find_sym_impl(p, &verinfo, s_info_p, 0, p->namespace); 143570af302Sopenharmony_ci} 144570af302Sopenharmony_ci 145570af302Sopenharmony_ci 146570af302Sopenharmony_cistatic int addr_in_dso(struct dso *dso, size_t addr) 147570af302Sopenharmony_ci{ 148570af302Sopenharmony_ci Phdr *ph = dso->phdr; 149570af302Sopenharmony_ci size_t phcnt = dso->phnum; 150570af302Sopenharmony_ci size_t entsz = dso->phentsize; 151570af302Sopenharmony_ci size_t base = (size_t)dso->base; 152570af302Sopenharmony_ci for (; phcnt--; ph = (void *)((char *)ph + entsz)) { 153570af302Sopenharmony_ci if (ph->p_type != PT_LOAD) continue; 154570af302Sopenharmony_ci if (addr - base - ph->p_vaddr < ph->p_memsz) 155570af302Sopenharmony_ci return 1; 156570af302Sopenharmony_ci } 157570af302Sopenharmony_ci return 0; 158570af302Sopenharmony_ci} 159570af302Sopenharmony_ci 160570af302Sopenharmony_cistatic int addr_in_kernel_mapped_dso(size_t addr) 161570af302Sopenharmony_ci{ 162570af302Sopenharmony_ci if (addr_in_dso(pldso, addr)) { 163570af302Sopenharmony_ci return 1; 164570af302Sopenharmony_ci } 165570af302Sopenharmony_ci 166570af302Sopenharmony_ci if (addr_in_dso(r_app, addr)) { 167570af302Sopenharmony_ci return 1; 168570af302Sopenharmony_ci } 169570af302Sopenharmony_ci 170570af302Sopenharmony_ci if (addr_in_dso(r_vdso, addr)) { 171570af302Sopenharmony_ci return 1; 172570af302Sopenharmony_ci } 173570af302Sopenharmony_ci return 0; 174570af302Sopenharmony_ci} 175570af302Sopenharmony_ci 176570af302Sopenharmony_cistatic uintptr_t get_cfi_check_addr(uint16_t value, void* func_ptr) 177570af302Sopenharmony_ci{ 178570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 179570af302Sopenharmony_ci 180570af302Sopenharmony_ci uintptr_t addr = (uintptr_t)func_ptr; 181570af302Sopenharmony_ci uintptr_t aligned_addr = ALIGN_DOWN(addr, shadow_alignment) + shadow_alignment; 182570af302Sopenharmony_ci uintptr_t cfi_check_func_addr = aligned_addr - ((uintptr_t)(value - sv_valid_min) << cfi_check_granularity); 183570af302Sopenharmony_ci#ifdef __arm__ 184570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] __arm__ defined!\n", __FUNCTION__); 185570af302Sopenharmony_ci cfi_check_func_addr++; 186570af302Sopenharmony_ci#endif 187570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] cfi_check_func_addr[%{public}p] in dso[%{public}s]\n", 188570af302Sopenharmony_ci __FUNCTION__, cfi_check_func_addr, ((struct dso *)addr2dso((size_t)cfi_check_func_addr))->name); 189570af302Sopenharmony_ci 190570af302Sopenharmony_ci return cfi_check_func_addr; 191570af302Sopenharmony_ci} 192570af302Sopenharmony_ci 193570af302Sopenharmony_cistatic inline void cfi_slowpath_common(uint64_t call_site_type_id, void *func_ptr, void *diag_data) 194570af302Sopenharmony_ci{ 195570af302Sopenharmony_ci uint16_t value = sv_invalid; 196570af302Sopenharmony_ci 197570af302Sopenharmony_ci if (func_ptr == NULL) { 198570af302Sopenharmony_ci return; 199570af302Sopenharmony_ci } 200570af302Sopenharmony_ci 201570af302Sopenharmony_ci#if defined(__aarch64__) 202570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] __aarch64__ defined!\n", __FUNCTION__); 203570af302Sopenharmony_ci uintptr_t addr = (uintptr_t)func_ptr & ((1ULL << 56) - 1); 204570af302Sopenharmony_ci#else 205570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] __aarch64__ not defined!\n", __FUNCTION__); 206570af302Sopenharmony_ci uintptr_t addr = func_ptr; 207570af302Sopenharmony_ci#endif 208570af302Sopenharmony_ci 209570af302Sopenharmony_ci /* Get shadow value */ 210570af302Sopenharmony_ci uintptr_t offset = addr_to_offset(addr, shadow_granularity); 211570af302Sopenharmony_ci 212570af302Sopenharmony_ci if (cfi_shadow_start == NULL) { 213570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] the cfi_shadow_start is null!\n", __FUNCTION__); 214570af302Sopenharmony_ci __builtin_trap(); 215570af302Sopenharmony_ci } 216570af302Sopenharmony_ci 217570af302Sopenharmony_ci if (offset > shadow_size) { 218570af302Sopenharmony_ci LD_LOGE("[CFI] set value to sv_invalid because offset(%{public}x) > shadow_size(%{public}x), " 219570af302Sopenharmony_ci "addr:%{public}p lr:%{public}p.\n", 220570af302Sopenharmony_ci offset, shadow_size, func_ptr, __builtin_return_address(0)); 221570af302Sopenharmony_ci value = sv_invalid; 222570af302Sopenharmony_ci } else { 223570af302Sopenharmony_ci value = *((uint16_t*)(cfi_shadow_start + offset)); 224570af302Sopenharmony_ci } 225570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] called from %{public}s to %{public}s func_ptr:0x%{public}p shadow value:%{public}d diag_data:0x%{public}p call_site_type_id[%{public}p.\n", 226570af302Sopenharmony_ci __FUNCTION__, 227570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)__builtin_return_address(0)))->name, 228570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)func_ptr))->name, 229570af302Sopenharmony_ci func_ptr, value, diag_data, call_site_type_id); 230570af302Sopenharmony_ci 231570af302Sopenharmony_ci struct dso *dso = NULL; 232570af302Sopenharmony_ci switch (value) 233570af302Sopenharmony_ci { 234570af302Sopenharmony_ci case sv_invalid: 235570af302Sopenharmony_ci // Kernel mapped sos don't guarantee the alignment requirements of the CFI, 236570af302Sopenharmony_ci // there will be potential to get to the wrong shadow value, For example: 237570af302Sopenharmony_ci // If another so is mapped to the same "LibraryAligment" as kernel mapped so, 238570af302Sopenharmony_ci // then they will use the same shadow, the shadow value will be set to invalid If this so is unloaded later, 239570af302Sopenharmony_ci // and then call the address in the kernel mapped so will get an invalid shadow value. 240570af302Sopenharmony_ci // We fall back to uncheck for this scene. 241570af302Sopenharmony_ci if (addr_in_kernel_mapped_dso((size_t)func_ptr)) { 242570af302Sopenharmony_ci LD_LOGI("[CFI] [%{public}s] uncheck for kernel mapped so.\n", __FUNCTION__); 243570af302Sopenharmony_ci return; 244570af302Sopenharmony_ci } 245570af302Sopenharmony_ci 246570af302Sopenharmony_ci LD_LOGE("[CFI] Invalid shadow value of address:%{public}p, lr:%{public}p.\n", 247570af302Sopenharmony_ci func_ptr, __builtin_return_address(0)); 248570af302Sopenharmony_ci 249570af302Sopenharmony_ci dso = (struct dso *)addr2dso((size_t)__builtin_return_address(0)); 250570af302Sopenharmony_ci if (dso == NULL) { 251570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] can not find matched dso of %{public}p !\n", 252570af302Sopenharmony_ci __FUNCTION__, __builtin_return_address(0)); 253570af302Sopenharmony_ci __builtin_trap(); 254570af302Sopenharmony_ci } 255570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] dso name[%{public}s]!\n", __FUNCTION__, dso->name); 256570af302Sopenharmony_ci 257570af302Sopenharmony_ci struct symdef cfi_check_sym = find_cfi_check_sym(dso); 258570af302Sopenharmony_ci if (!cfi_check_sym.sym) { 259570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] can not find the __cfi_check in the dso!\n", __FUNCTION__); 260570af302Sopenharmony_ci __builtin_trap(); 261570af302Sopenharmony_ci } 262570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] cfi_check addr[%{public}p]!\n", __FUNCTION__, 263570af302Sopenharmony_ci LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value)); 264570af302Sopenharmony_ci ((cfi_check_t)LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value))(call_site_type_id, func_ptr, diag_data); 265570af302Sopenharmony_ci break; 266570af302Sopenharmony_ci case sv_uncheck: 267570af302Sopenharmony_ci break; 268570af302Sopenharmony_ci default: 269570af302Sopenharmony_ci ((cfi_check_t)get_cfi_check_addr(value, func_ptr))(call_site_type_id, func_ptr, diag_data); 270570af302Sopenharmony_ci break; 271570af302Sopenharmony_ci } 272570af302Sopenharmony_ci 273570af302Sopenharmony_ci return; 274570af302Sopenharmony_ci} 275570af302Sopenharmony_ci 276570af302Sopenharmony_ciint init_cfi_shadow(struct dso *dso_list, struct dso *ldso, struct dso *app, struct dso *vdso) 277570af302Sopenharmony_ci{ 278570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 279570af302Sopenharmony_ci 280570af302Sopenharmony_ci if (dso_list == NULL) { 281570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] has null param!\n", __FUNCTION__); 282570af302Sopenharmony_ci return CFI_SUCCESS; 283570af302Sopenharmony_ci } 284570af302Sopenharmony_ci 285570af302Sopenharmony_ci /* Save the head node of dso list */ 286570af302Sopenharmony_ci dso_list_head = dso_list; 287570af302Sopenharmony_ci pldso = ldso; 288570af302Sopenharmony_ci r_app = app; 289570af302Sopenharmony_ci r_vdso = vdso; 290570af302Sopenharmony_ci 291570af302Sopenharmony_ci return map_dso_to_cfi_shadow(dso_list); 292570af302Sopenharmony_ci} 293570af302Sopenharmony_ci 294570af302Sopenharmony_ciint map_dso_to_cfi_shadow(struct dso *dso) 295570af302Sopenharmony_ci{ 296570af302Sopenharmony_ci bool has_cfi_check = false; 297570af302Sopenharmony_ci 298570af302Sopenharmony_ci if (dso == NULL) { 299570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] has null param!\n", __FUNCTION__); 300570af302Sopenharmony_ci return CFI_SUCCESS; 301570af302Sopenharmony_ci } 302570af302Sopenharmony_ci 303570af302Sopenharmony_ci /* If the cfi shadow does not exist, create it and map all the dsos and its dependents to it. */ 304570af302Sopenharmony_ci if (cfi_shadow_start == NULL) { 305570af302Sopenharmony_ci /* Find __cfi_check symbol in dso list */ 306570af302Sopenharmony_ci for (struct dso *p = dso; p; p = p->next) { 307570af302Sopenharmony_ci if (find_cfi_check_sym(p).sym) { 308570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] find __cfi_check function in dso %{public}s!\n", __FUNCTION__, p->name); 309570af302Sopenharmony_ci has_cfi_check = true; 310570af302Sopenharmony_ci break; 311570af302Sopenharmony_ci } 312570af302Sopenharmony_ci } 313570af302Sopenharmony_ci 314570af302Sopenharmony_ci if (has_cfi_check) { 315570af302Sopenharmony_ci if (create_cfi_shadow() == CFI_FAILED) { 316570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] create cfi shadow failed!\n", __FUNCTION__); 317570af302Sopenharmony_ci return CFI_FAILED; 318570af302Sopenharmony_ci } 319570af302Sopenharmony_ci 320570af302Sopenharmony_ci if (add_dso_to_cfi_shadow(dso_list_head) == CFI_FAILED) { 321570af302Sopenharmony_ci return CFI_FAILED; 322570af302Sopenharmony_ci } 323570af302Sopenharmony_ci 324570af302Sopenharmony_ci prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); 325570af302Sopenharmony_ci } 326570af302Sopenharmony_ci /* If the cfi shadow exists, map the current dso and its dependents to it. */ 327570af302Sopenharmony_ci } else { 328570af302Sopenharmony_ci if (add_dso_to_cfi_shadow(dso) == CFI_FAILED) { 329570af302Sopenharmony_ci return CFI_FAILED; 330570af302Sopenharmony_ci } 331570af302Sopenharmony_ci prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); 332570af302Sopenharmony_ci } 333570af302Sopenharmony_ci 334570af302Sopenharmony_ci return CFI_SUCCESS; 335570af302Sopenharmony_ci} 336570af302Sopenharmony_ci 337570af302Sopenharmony_civoid unmap_dso_from_cfi_shadow(struct dso *dso) 338570af302Sopenharmony_ci{ 339570af302Sopenharmony_ci if (dso == NULL) { 340570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] has null param!\n", __FUNCTION__); 341570af302Sopenharmony_ci return; 342570af302Sopenharmony_ci } 343570af302Sopenharmony_ci 344570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] unmap dso %{public}s from shadow!\n", __FUNCTION__, dso->name); 345570af302Sopenharmony_ci 346570af302Sopenharmony_ci if (cfi_shadow_start == NULL) 347570af302Sopenharmony_ci return; 348570af302Sopenharmony_ci 349570af302Sopenharmony_ci if (dso->map == 0 || dso->map_len == 0) 350570af302Sopenharmony_ci return; 351570af302Sopenharmony_ci 352570af302Sopenharmony_ci if (dso->is_mapped_to_shadow == false) 353570af302Sopenharmony_ci return; 354570af302Sopenharmony_ci 355570af302Sopenharmony_ci if (((size_t)dso->map & (LIBRARY_ALIGNMENT - 1)) != 0) { 356570af302Sopenharmony_ci if (!(dso == pldso || dso == r_app || dso == r_vdso)) { 357570af302Sopenharmony_ci LD_LOGE("[CFI] [warning] %{public}s isn't aligned to %{public}x" 358570af302Sopenharmony_ci "begin[%{public}x] end[%{public}x] cfi_check[%{public}x] type[%{public}x]!\n", 359570af302Sopenharmony_ci dso->name, LIBRARY_ALIGNMENT, dso->map, dso->map + dso->map_len, 0, sv_invalid); 360570af302Sopenharmony_ci } 361570af302Sopenharmony_ci } 362570af302Sopenharmony_ci 363570af302Sopenharmony_ci /* Set the dso's shadow value as invalid. */ 364570af302Sopenharmony_ci fill_shadow_value_to_shadow(dso->map, dso->map + dso->map_len, 0, sv_invalid); 365570af302Sopenharmony_ci dso->is_mapped_to_shadow = false; 366570af302Sopenharmony_ci prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); 367570af302Sopenharmony_ci 368570af302Sopenharmony_ci return; 369570af302Sopenharmony_ci} 370570af302Sopenharmony_ci 371570af302Sopenharmony_cistatic int create_cfi_shadow(void) 372570af302Sopenharmony_ci{ 373570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 374570af302Sopenharmony_ci 375570af302Sopenharmony_ci /* Each process can load up to (max_target_addr >> shadow_granularity) dsos. Shift left 1 bit because the shadow 376570af302Sopenharmony_ci * value is uint16_t. The size passed to mmap() should be aligned with 4096, so shadow_size should be aligned. */ 377570af302Sopenharmony_ci shadow_size = ALIGN_UP(((max_target_addr >> shadow_granularity) << 1), PAGE_SIZE); 378570af302Sopenharmony_ci 379570af302Sopenharmony_ci uintptr_t *mmap_addr = mmap(NULL, shadow_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); 380570af302Sopenharmony_ci 381570af302Sopenharmony_ci if (mmap_addr == MAP_FAILED) { 382570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] mmap failed!\n", __FUNCTION__); 383570af302Sopenharmony_ci return CFI_FAILED; 384570af302Sopenharmony_ci } 385570af302Sopenharmony_ci 386570af302Sopenharmony_ci cfi_shadow_start = (char*)mmap_addr; 387570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] the cfi_shadow_start addr is %{public}p!\n", __FUNCTION__, cfi_shadow_start); 388570af302Sopenharmony_ci 389570af302Sopenharmony_ci return CFI_SUCCESS; 390570af302Sopenharmony_ci} 391570af302Sopenharmony_ci 392570af302Sopenharmony_cistatic int add_dso_to_cfi_shadow(struct dso *dso) 393570af302Sopenharmony_ci{ 394570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start with %{public}s !\n", __FUNCTION__, dso->name); 395570af302Sopenharmony_ci for (struct dso *p = dso; p; p = p->next) { 396570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] adding %{public}s to cfi shadow!\n", __FUNCTION__, p->name); 397570af302Sopenharmony_ci if (p->map == 0 || p->map_len == 0) { 398570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] the dso has no data! map[%{public}p] map_len[0x%{public}x]\n", 399570af302Sopenharmony_ci __FUNCTION__, p->map, p->map_len); 400570af302Sopenharmony_ci continue; 401570af302Sopenharmony_ci } 402570af302Sopenharmony_ci 403570af302Sopenharmony_ci if (p->is_mapped_to_shadow == true) { 404570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] %{public}s is already in shadow!\n", __FUNCTION__, p->name); 405570af302Sopenharmony_ci continue; 406570af302Sopenharmony_ci } 407570af302Sopenharmony_ci 408570af302Sopenharmony_ci struct symdef cfi_check_sym = find_cfi_check_sym(p); 409570af302Sopenharmony_ci /* If the dso doesn't have __cfi_check(), set it's shadow value unchecked. */ 410570af302Sopenharmony_ci if (!cfi_check_sym.sym) { 411570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] %{public}s has no __cfi_check()!\n", __FUNCTION__, p->name); 412570af302Sopenharmony_ci 413570af302Sopenharmony_ci if (((size_t)dso->map & (LIBRARY_ALIGNMENT - 1)) != 0) { 414570af302Sopenharmony_ci if (!(dso == pldso || dso == r_app || dso == r_vdso)) { 415570af302Sopenharmony_ci LD_LOGE("[CFI] [warning] %{public}s isn't aligned to %{public}x " 416570af302Sopenharmony_ci "begin[%{public}x] end[%{public}x] cfi_check[%{public}x] type[%{public}x]!\n", 417570af302Sopenharmony_ci dso->name, LIBRARY_ALIGNMENT, dso->map, dso->map + dso->map_len, 0, sv_uncheck); 418570af302Sopenharmony_ci } 419570af302Sopenharmony_ci } 420570af302Sopenharmony_ci 421570af302Sopenharmony_ci if (fill_shadow_value_to_shadow(p->map, p->map + p->map_len, 0, sv_uncheck) == CFI_FAILED) { 422570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] add dso to cfi shadow failed!\n", __FUNCTION__); 423570af302Sopenharmony_ci return CFI_FAILED; 424570af302Sopenharmony_ci } 425570af302Sopenharmony_ci /* If the dso has __cfi_check(), set it's shadow value valid. */ 426570af302Sopenharmony_ci } else { 427570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] %{public}s has __cfi_check()!\n", __FUNCTION__, p->name); 428570af302Sopenharmony_ci uintptr_t end = p->map + p->map_len; 429570af302Sopenharmony_ci uintptr_t cfi_check = LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value); 430570af302Sopenharmony_ci 431570af302Sopenharmony_ci if (cfi_check == 0) { 432570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] %{public}s has null cfi_check func!\n", __FUNCTION__, p->name); 433570af302Sopenharmony_ci return CFI_FAILED; 434570af302Sopenharmony_ci } 435570af302Sopenharmony_ci#ifdef __arm__ 436570af302Sopenharmony_ci // cfi_check function address ends with 1 on the ARM platform. 437570af302Sopenharmony_ci if ((cfi_check & 1UL) != 1UL) { 438570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] __cfi_check address isn't a thumb function in %{public}s!\n", 439570af302Sopenharmony_ci __FUNCTION__, p->name); 440570af302Sopenharmony_ci return CFI_FAILED; 441570af302Sopenharmony_ci } 442570af302Sopenharmony_ci cfi_check &= ~1UL; 443570af302Sopenharmony_ci#endif 444570af302Sopenharmony_ci if ((cfi_check & (cfi_check_alignment - 1)) != 0) { 445570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] unaligned __cfi_check address in %{public}s!\n", __FUNCTION__, p->name); 446570af302Sopenharmony_ci return CFI_FAILED; 447570af302Sopenharmony_ci } 448570af302Sopenharmony_ci 449570af302Sopenharmony_ci if (((size_t)dso->map & (LIBRARY_ALIGNMENT - 1)) != 0) { 450570af302Sopenharmony_ci if (!(dso == pldso || dso == r_app || dso == r_vdso)) { 451570af302Sopenharmony_ci LD_LOGE("[CFI] [warning] %{public}s isn't aligned to %{public}x" 452570af302Sopenharmony_ci "begin[%{public}x] end[%{public}x] cfi_check[%{public}x] type[%{public}x]!\n", 453570af302Sopenharmony_ci dso->name, LIBRARY_ALIGNMENT, dso->map, dso->map + dso->map_len, cfi_check, sv_valid_min); 454570af302Sopenharmony_ci } 455570af302Sopenharmony_ci } 456570af302Sopenharmony_ci 457570af302Sopenharmony_ci if (fill_shadow_value_to_shadow(p->map, end, cfi_check, sv_valid_min) == CFI_FAILED) { 458570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] add %{public}s to cfi shadow failed!\n", __FUNCTION__, p->name); 459570af302Sopenharmony_ci return CFI_FAILED; 460570af302Sopenharmony_ci } 461570af302Sopenharmony_ci } 462570af302Sopenharmony_ci p->is_mapped_to_shadow = true; 463570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] add %{public}s to cfi shadow succeed.\n", __FUNCTION__, p->name); 464570af302Sopenharmony_ci } 465570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] %{public}s done.\n", __FUNCTION__, dso->name); 466570af302Sopenharmony_ci 467570af302Sopenharmony_ci return CFI_SUCCESS; 468570af302Sopenharmony_ci} 469570af302Sopenharmony_ci 470570af302Sopenharmony_cistatic int fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type) 471570af302Sopenharmony_ci{ 472570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] begin[%{public}x] end[%{public}x] cfi_check[%{public}x] type[%{public}x]!\n", 473570af302Sopenharmony_ci __FUNCTION__, begin, end, cfi_check, type); 474570af302Sopenharmony_ci 475570af302Sopenharmony_ci /* To ensure the atomicity of the CFI shadow operation, we create a temp_shadow, write the shadow value to 476570af302Sopenharmony_ci * the temp_shadow, and then write it back to the CFI shadow by mremap(). */ 477570af302Sopenharmony_ci begin = ALIGN_DOWN(MAX(begin, cfi_check), shadow_alignment); 478570af302Sopenharmony_ci char* shadow_begin = cfi_shadow_start + addr_to_offset(begin, LIBRARY_ALIGNMENT_BITS); 479570af302Sopenharmony_ci char* shadow_end = (char*)(((uint16_t*)(cfi_shadow_start + addr_to_offset(end - 1, LIBRARY_ALIGNMENT_BITS))) + 1); 480570af302Sopenharmony_ci char* aligned_shadow_begin = (char*)ALIGN_DOWN((uintptr_t)shadow_begin, PAGE_SIZE); 481570af302Sopenharmony_ci char* aligned_shadow_end = (char*)ALIGN_UP((uintptr_t)shadow_end, PAGE_SIZE); 482570af302Sopenharmony_ci 483570af302Sopenharmony_ci uint16_t tmp_shadow_size = aligned_shadow_end - aligned_shadow_begin; 484570af302Sopenharmony_ci uint16_t offset_begin = shadow_begin - aligned_shadow_begin; 485570af302Sopenharmony_ci uint16_t offset_end = shadow_end - aligned_shadow_begin; 486570af302Sopenharmony_ci 487570af302Sopenharmony_ci char* tmp_shadow_start = (char*)mmap(NULL, tmp_shadow_size, 488570af302Sopenharmony_ci PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 489570af302Sopenharmony_ci 490570af302Sopenharmony_ci if (tmp_shadow_start == MAP_FAILED) { 491570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] mmap failed!\n", __FUNCTION__); 492570af302Sopenharmony_ci return CFI_FAILED; 493570af302Sopenharmony_ci } 494570af302Sopenharmony_ci 495570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] tmp_shadow_start is %{public}p\t tmp_shadow_size is 0x%{public}x!\n", 496570af302Sopenharmony_ci __FUNCTION__, tmp_shadow_start, tmp_shadow_size); 497570af302Sopenharmony_ci if (mprotect(aligned_shadow_begin, tmp_shadow_size, PROT_READ) == -1) { 498570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] mprotect failed!\n", __FUNCTION__); 499570af302Sopenharmony_ci return CFI_FAILED; 500570af302Sopenharmony_ci } 501570af302Sopenharmony_ci if (type == sv_valid_min) { 502570af302Sopenharmony_ci // We need to copy the whole area because we will read the old value below. 503570af302Sopenharmony_ci memcpy(tmp_shadow_start, aligned_shadow_begin, tmp_shadow_size); 504570af302Sopenharmony_ci } else { 505570af302Sopenharmony_ci memcpy(tmp_shadow_start, aligned_shadow_begin, offset_begin); 506570af302Sopenharmony_ci memcpy(tmp_shadow_start + offset_end, shadow_end, aligned_shadow_end - shadow_end); 507570af302Sopenharmony_ci } 508570af302Sopenharmony_ci 509570af302Sopenharmony_ci /* If the dso has __cfi_check(), calculate valid shadow value */ 510570af302Sopenharmony_ci if (type == sv_valid_min) { 511570af302Sopenharmony_ci uint16_t shadow_value_begin = ((begin + shadow_alignment - cfi_check) 512570af302Sopenharmony_ci >> cfi_check_granularity) + sv_valid_min; 513570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] shadow_value_begin is 0x%{public}x!\n", __FUNCTION__, shadow_value_begin); 514570af302Sopenharmony_ci uint32_t shadow_value = shadow_value_begin; 515570af302Sopenharmony_ci /* Set shadow_value */ 516570af302Sopenharmony_ci for (uint16_t *shadow_addr = tmp_shadow_start + offset_begin; 517570af302Sopenharmony_ci shadow_addr != tmp_shadow_start + offset_end; shadow_addr++) { 518570af302Sopenharmony_ci // We fall back to uncheck if the length of so is larger than 256M((UINT16_MAX - 2) * cfi_check_alignment). 519570af302Sopenharmony_ci if (shadow_value > UINT16_MAX) { 520570af302Sopenharmony_ci *shadow_addr = sv_uncheck; 521570af302Sopenharmony_ci continue; 522570af302Sopenharmony_ci } 523570af302Sopenharmony_ci 524570af302Sopenharmony_ci *shadow_addr = (*shadow_addr == sv_invalid) ? (uint16_t)shadow_value : sv_uncheck; 525570af302Sopenharmony_ci shadow_value += shadow_value_step; 526570af302Sopenharmony_ci } 527570af302Sopenharmony_ci /* in these cases, shadow_value will always be sv_uncheck or sv_invalid */ 528570af302Sopenharmony_ci } else if (type == sv_uncheck || type == sv_invalid) { 529570af302Sopenharmony_ci /* Set shadow_value */ 530570af302Sopenharmony_ci for (uint16_t *shadow_addr = tmp_shadow_start + offset_begin; 531570af302Sopenharmony_ci shadow_addr != tmp_shadow_start + offset_end; shadow_addr++) { 532570af302Sopenharmony_ci *shadow_addr = type; 533570af302Sopenharmony_ci } 534570af302Sopenharmony_ci } else { 535570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] has error param!\n", __FUNCTION__); 536570af302Sopenharmony_ci munmap(tmp_shadow_start, tmp_shadow_size); 537570af302Sopenharmony_ci return CFI_FAILED; 538570af302Sopenharmony_ci } 539570af302Sopenharmony_ci 540570af302Sopenharmony_ci mprotect(tmp_shadow_start, tmp_shadow_size, PROT_READ); 541570af302Sopenharmony_ci /* Remap temp_shadow to CFI shadow. */ 542570af302Sopenharmony_ci uint16_t* mremap_addr = mremap(tmp_shadow_start, tmp_shadow_size, tmp_shadow_size, 543570af302Sopenharmony_ci MREMAP_MAYMOVE | MREMAP_FIXED, aligned_shadow_begin); 544570af302Sopenharmony_ci 545570af302Sopenharmony_ci if (mremap_addr == MAP_FAILED) { 546570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] mremap failed!\n", __FUNCTION__); 547570af302Sopenharmony_ci munmap(tmp_shadow_start, tmp_shadow_size); 548570af302Sopenharmony_ci return CFI_FAILED; 549570af302Sopenharmony_ci } 550570af302Sopenharmony_ci 551570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] fill completed!\n", __FUNCTION__); 552570af302Sopenharmony_ci return CFI_SUCCESS; 553570af302Sopenharmony_ci} 554570af302Sopenharmony_ci 555570af302Sopenharmony_civoid __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr) 556570af302Sopenharmony_ci{ 557570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] called from dso[%{public}s] to dso[%{public}s] func_ptr[%{public}p]\n", 558570af302Sopenharmony_ci __FUNCTION__, 559570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)__builtin_return_address(0)))->name, 560570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)func_ptr))->name, 561570af302Sopenharmony_ci func_ptr); 562570af302Sopenharmony_ci 563570af302Sopenharmony_ci cfi_slowpath_common(call_site_type_id, func_ptr, NULL); 564570af302Sopenharmony_ci return; 565570af302Sopenharmony_ci} 566570af302Sopenharmony_ci 567570af302Sopenharmony_civoid __cfi_slowpath_diag(uint64_t call_site_type_id, void *func_ptr, void *diag_data) 568570af302Sopenharmony_ci{ 569570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] called from dso[%{public}s] to dso[%{public}s] func_ptr[%{public}p]\n", 570570af302Sopenharmony_ci __FUNCTION__, 571570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)__builtin_return_address(0)))->name, 572570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)func_ptr))->name, 573570af302Sopenharmony_ci func_ptr); 574570af302Sopenharmony_ci 575570af302Sopenharmony_ci cfi_slowpath_common(call_site_type_id, func_ptr, diag_data); 576570af302Sopenharmony_ci return; 577570af302Sopenharmony_ci} 578