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_cistatic const uintptr_t shadow_alignment = 1UL << shadow_granularity; 84570af302Sopenharmony_cistatic uintptr_t shadow_size = 0; 85570af302Sopenharmony_ci/* Start addr of the CFI shadow */ 86570af302Sopenharmony_cistatic char *cfi_shadow_start = NULL; 87570af302Sopenharmony_ci/* List head of all the DSOs loaded by the process */ 88570af302Sopenharmony_cistatic struct dso *dso_list_head = NULL; 89570af302Sopenharmony_cistatic struct dso *pldso = NULL; 90570af302Sopenharmony_ci 91570af302Sopenharmony_ci/* Shadow value */ 92570af302Sopenharmony_ci/* The related shadow value(s) will be set to `sv_invalid` when: 93570af302Sopenharmony_ci * - init CFI shadow. 94570af302Sopenharmony_ci * - removing a dso. */ 95570af302Sopenharmony_cistatic const uint16_t sv_invalid = 0; 96570af302Sopenharmony_ci/* The related shadow value(s) will be set to `sv_uncheck` if: 97570af302Sopenharmony_ci * - the DSO does not enable CFI Cross-Dso. 98570af302Sopenharmony_ci * - the DSO enabled CFI Cross-Dso, but this DSO is larger than 16G, for the part of the dso that exceeds 16G, 99570af302Sopenharmony_ci * its shadow value will be set to `sv_uncheck`. */ 100570af302Sopenharmony_cistatic const uint16_t sv_uncheck = 1; 101570af302Sopenharmony_ci/* If a DSO enabled CFI Cross-Dso, the DSO's shadow value should be valid. Because of the defination of `sv_invalid` 102570af302Sopenharmony_ci * and `sv_unchecked`, the valid shadow value should be at least 2. */ 103570af302Sopenharmony_cistatic const uint16_t sv_valid_min = 2; 104570af302Sopenharmony_ci 105570af302Sopenharmony_ci#if defined(__LP64__) 106570af302Sopenharmony_cistatic const uintptr_t max_target_addr = 0xffffffffffff; 107570af302Sopenharmony_ci#else 108570af302Sopenharmony_cistatic const uintptr_t max_target_addr = 0xffffffff; 109570af302Sopenharmony_ci#endif 110570af302Sopenharmony_ci 111570af302Sopenharmony_ci/* Create a cfi shadow */ 112570af302Sopenharmony_cistatic int create_cfi_shadow(void); 113570af302Sopenharmony_ci 114570af302Sopenharmony_ci/* Map dsos to CFI shadow */ 115570af302Sopenharmony_cistatic int add_dso_to_cfi_shadow(struct dso *dso); 116570af302Sopenharmony_cistatic int fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type); 117570af302Sopenharmony_ci 118570af302Sopenharmony_ci/* Find the __cfi_check() of target dso and call it */ 119570af302Sopenharmony_civoid __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr); 120570af302Sopenharmony_civoid __cfi_slowpath_diag(uint64_t call_site_type_id, void *func_ptr, void *diag_data); 121570af302Sopenharmony_ci 122570af302Sopenharmony_cistatic inline uintptr_t addr_to_offset(uintptr_t addr, int bits) 123570af302Sopenharmony_ci{ 124570af302Sopenharmony_ci /* Convert addr to CFI shadow offset. 125570af302Sopenharmony_ci * Shift left 1 bit because the shadow value is uint16_t. */ 126570af302Sopenharmony_ci return (addr >> bits) << 1; 127570af302Sopenharmony_ci} 128570af302Sopenharmony_ci 129570af302Sopenharmony_cistatic struct symdef find_cfi_check_sym(struct dso *p) 130570af302Sopenharmony_ci{ 131570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 132570af302Sopenharmony_ci 133570af302Sopenharmony_ci struct verinfo verinfo = { .s = "__cfi_check", .v = "", .use_vna_hash = false }; 134570af302Sopenharmony_ci struct sym_info_pair s_info_p = gnu_hash(verinfo.s); 135570af302Sopenharmony_ci return find_sym_impl(p, &verinfo, s_info_p, 0, p->namespace); 136570af302Sopenharmony_ci} 137570af302Sopenharmony_ci 138570af302Sopenharmony_cistatic int is_addr_in_ldso(size_t a) 139570af302Sopenharmony_ci{ 140570af302Sopenharmony_ci size_t i = 0; 141570af302Sopenharmony_ci if (DL_FDPIC) { 142570af302Sopenharmony_ci i = count_syms(pldso); 143570af302Sopenharmony_ci if (a - (size_t)pldso->funcdescs < i * sizeof(*pldso->funcdescs)) 144570af302Sopenharmony_ci return 1; 145570af302Sopenharmony_ci } 146570af302Sopenharmony_ci if (DL_FDPIC && pldso->loadmap) { 147570af302Sopenharmony_ci for (i = 0; i < pldso->loadmap->nsegs; i++) { 148570af302Sopenharmony_ci if (a-pldso->loadmap->segs[i].p_vaddr 149570af302Sopenharmony_ci < pldso->loadmap->segs[i].p_memsz) 150570af302Sopenharmony_ci return 1; 151570af302Sopenharmony_ci } 152570af302Sopenharmony_ci } else { 153570af302Sopenharmony_ci Phdr *ph = pldso->phdr; 154570af302Sopenharmony_ci size_t phcnt = pldso->phnum; 155570af302Sopenharmony_ci size_t entsz = pldso->phentsize; 156570af302Sopenharmony_ci size_t base = (size_t)pldso->base; 157570af302Sopenharmony_ci for (; phcnt--; ph = (void *)((char *)ph + entsz)) { 158570af302Sopenharmony_ci if (ph->p_type != PT_LOAD) continue; 159570af302Sopenharmony_ci if (a - base - ph->p_vaddr < ph->p_memsz) 160570af302Sopenharmony_ci return 1; 161570af302Sopenharmony_ci } 162570af302Sopenharmony_ci if (a - (size_t)pldso->map < pldso->map_len) 163570af302Sopenharmony_ci return 0; 164570af302Sopenharmony_ci } 165570af302Sopenharmony_ci return 0; 166570af302Sopenharmony_ci} 167570af302Sopenharmony_ci 168570af302Sopenharmony_cistatic uintptr_t get_cfi_check_addr(uint16_t value, void* func_ptr) 169570af302Sopenharmony_ci{ 170570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 171570af302Sopenharmony_ci 172570af302Sopenharmony_ci uintptr_t addr = (uintptr_t)func_ptr; 173570af302Sopenharmony_ci uintptr_t aligned_addr = ALIGN_DOWN(addr, shadow_alignment) + shadow_alignment; 174570af302Sopenharmony_ci uintptr_t cfi_check_func_addr = aligned_addr - ((uintptr_t)(value - sv_valid_min) << cfi_check_granularity); 175570af302Sopenharmony_ci#ifdef __arm__ 176570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] __arm__ defined!\n", __FUNCTION__); 177570af302Sopenharmony_ci cfi_check_func_addr++; 178570af302Sopenharmony_ci#endif 179570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] cfi_check_func_addr[%{public}p] in dso[%{public}s]\n", 180570af302Sopenharmony_ci __FUNCTION__, cfi_check_func_addr, ((struct dso *)addr2dso((size_t)cfi_check_func_addr))->name); 181570af302Sopenharmony_ci 182570af302Sopenharmony_ci return cfi_check_func_addr; 183570af302Sopenharmony_ci} 184570af302Sopenharmony_ci 185570af302Sopenharmony_cistatic inline void cfi_slowpath_common(uint64_t call_site_type_id, void *func_ptr, void *diag_data) 186570af302Sopenharmony_ci{ 187570af302Sopenharmony_ci uint16_t value = sv_invalid; 188570af302Sopenharmony_ci 189570af302Sopenharmony_ci if (func_ptr == NULL) { 190570af302Sopenharmony_ci return; 191570af302Sopenharmony_ci } 192570af302Sopenharmony_ci 193570af302Sopenharmony_ci#if defined(__aarch64__) 194570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] __aarch64__ defined!\n", __FUNCTION__); 195570af302Sopenharmony_ci uintptr_t addr = (uintptr_t)func_ptr & ((1ULL << 56) - 1); 196570af302Sopenharmony_ci#else 197570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] __aarch64__ not defined!\n", __FUNCTION__); 198570af302Sopenharmony_ci uintptr_t addr = func_ptr; 199570af302Sopenharmony_ci#endif 200570af302Sopenharmony_ci 201570af302Sopenharmony_ci /* Get shadow value */ 202570af302Sopenharmony_ci uintptr_t offset = addr_to_offset(addr, shadow_granularity); 203570af302Sopenharmony_ci 204570af302Sopenharmony_ci if (cfi_shadow_start == NULL) { 205570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] the cfi_shadow_start is null!\n", __FUNCTION__); 206570af302Sopenharmony_ci __builtin_trap(); 207570af302Sopenharmony_ci } 208570af302Sopenharmony_ci 209570af302Sopenharmony_ci if (offset > shadow_size) { 210570af302Sopenharmony_ci value = sv_invalid; 211570af302Sopenharmony_ci } else { 212570af302Sopenharmony_ci value = *((uint16_t*)(cfi_shadow_start + offset)); 213570af302Sopenharmony_ci } 214570af302Sopenharmony_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", 215570af302Sopenharmony_ci __FUNCTION__, 216570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)__builtin_return_address(0)))->name, 217570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)func_ptr))->name, 218570af302Sopenharmony_ci func_ptr, value, diag_data, call_site_type_id); 219570af302Sopenharmony_ci 220570af302Sopenharmony_ci struct dso *dso = NULL; 221570af302Sopenharmony_ci switch (value) 222570af302Sopenharmony_ci { 223570af302Sopenharmony_ci case sv_invalid: 224570af302Sopenharmony_ci /* The ldso is an exception because it is loaded by kernel and is not mapped to the CFI shadow. 225570af302Sopenharmony_ci * Do not check it. */ 226570af302Sopenharmony_ci if (is_addr_in_ldso((size_t)func_ptr)) { 227570af302Sopenharmony_ci LD_LOGI("[CFI] [%{public}s] uncheck for ldso\n", __FUNCTION__); 228570af302Sopenharmony_ci return; 229570af302Sopenharmony_ci } 230570af302Sopenharmony_ci 231570af302Sopenharmony_ci dso = (struct dso *)addr2dso((size_t)__builtin_return_address(0)); 232570af302Sopenharmony_ci if (dso == NULL) { 233570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] can not find the dso from address:%{public}p func_ptr:0x%{public}p shadow value:%{public}d call_site_type_id[%{public}p!\n", 234570af302Sopenharmony_ci __FUNCTION__, 235570af302Sopenharmony_ci (size_t)__builtin_return_address(0), 236570af302Sopenharmony_ci func_ptr, value, call_site_type_id); 237570af302Sopenharmony_ci __builtin_trap(); 238570af302Sopenharmony_ci } 239570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] dso name[%{public}s]!\n", __FUNCTION__, dso->name); 240570af302Sopenharmony_ci 241570af302Sopenharmony_ci struct symdef cfi_check_sym = find_cfi_check_sym(dso); 242570af302Sopenharmony_ci if (!cfi_check_sym.sym) { 243570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] can not find the __cfi_check in the dso: %{public}s func_ptr:0x%{public}p shadow value:%{public}d call_site_type_id[%{public}p!\n", 244570af302Sopenharmony_ci __FUNCTION__, 245570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)__builtin_return_address(0)))->name, 246570af302Sopenharmony_ci func_ptr, value, call_site_type_id); 247570af302Sopenharmony_ci __builtin_trap(); 248570af302Sopenharmony_ci } 249570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] cfi_check addr[%{public}p]!\n", __FUNCTION__, 250570af302Sopenharmony_ci LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value)); 251570af302Sopenharmony_ci ((cfi_check_t)LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value))(call_site_type_id, func_ptr, diag_data); 252570af302Sopenharmony_ci break; 253570af302Sopenharmony_ci case sv_uncheck: 254570af302Sopenharmony_ci break; 255570af302Sopenharmony_ci default: 256570af302Sopenharmony_ci ((cfi_check_t)get_cfi_check_addr(value, func_ptr))(call_site_type_id, func_ptr, diag_data); 257570af302Sopenharmony_ci break; 258570af302Sopenharmony_ci } 259570af302Sopenharmony_ci 260570af302Sopenharmony_ci return; 261570af302Sopenharmony_ci} 262570af302Sopenharmony_ci 263570af302Sopenharmony_ciint init_cfi_shadow(struct dso *dso_list, struct dso *ldso) 264570af302Sopenharmony_ci{ 265570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 266570af302Sopenharmony_ci 267570af302Sopenharmony_ci if (dso_list == NULL) { 268570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] has null param!\n", __FUNCTION__); 269570af302Sopenharmony_ci return CFI_SUCCESS; 270570af302Sopenharmony_ci } 271570af302Sopenharmony_ci 272570af302Sopenharmony_ci /* Save the head node of dso list */ 273570af302Sopenharmony_ci dso_list_head = dso_list; 274570af302Sopenharmony_ci pldso = ldso; 275570af302Sopenharmony_ci 276570af302Sopenharmony_ci return map_dso_to_cfi_shadow(dso_list); 277570af302Sopenharmony_ci} 278570af302Sopenharmony_ci 279570af302Sopenharmony_ciint map_dso_to_cfi_shadow(struct dso *dso) 280570af302Sopenharmony_ci{ 281570af302Sopenharmony_ci bool has_cfi_check = false; 282570af302Sopenharmony_ci 283570af302Sopenharmony_ci if (dso == NULL) { 284570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] has null param!\n", __FUNCTION__); 285570af302Sopenharmony_ci return CFI_SUCCESS; 286570af302Sopenharmony_ci } 287570af302Sopenharmony_ci 288570af302Sopenharmony_ci /* If the cfi shadow does not exist, create it and map all the dsos and its dependents to it. */ 289570af302Sopenharmony_ci if (cfi_shadow_start == NULL) { 290570af302Sopenharmony_ci /* Find __cfi_check symbol in dso list */ 291570af302Sopenharmony_ci for (struct dso *p = dso; p; p = p->next) { 292570af302Sopenharmony_ci if (find_cfi_check_sym(p).sym) { 293570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] find __cfi_check function in dso %{public}s!\n", __FUNCTION__, p->name); 294570af302Sopenharmony_ci has_cfi_check = true; 295570af302Sopenharmony_ci break; 296570af302Sopenharmony_ci } 297570af302Sopenharmony_ci } 298570af302Sopenharmony_ci 299570af302Sopenharmony_ci if (has_cfi_check) { 300570af302Sopenharmony_ci if (create_cfi_shadow() == CFI_FAILED) { 301570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] create cfi shadow failed!\n", __FUNCTION__); 302570af302Sopenharmony_ci return CFI_FAILED; 303570af302Sopenharmony_ci } 304570af302Sopenharmony_ci add_dso_to_cfi_shadow(dso_list_head); 305570af302Sopenharmony_ci prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); 306570af302Sopenharmony_ci } 307570af302Sopenharmony_ci /* If the cfi shadow exists, map the current dso and its dependents to it. */ 308570af302Sopenharmony_ci } else { 309570af302Sopenharmony_ci add_dso_to_cfi_shadow(dso); 310570af302Sopenharmony_ci prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); 311570af302Sopenharmony_ci } 312570af302Sopenharmony_ci 313570af302Sopenharmony_ci return CFI_SUCCESS; 314570af302Sopenharmony_ci} 315570af302Sopenharmony_ci 316570af302Sopenharmony_civoid unmap_dso_from_cfi_shadow(struct dso *dso) 317570af302Sopenharmony_ci{ 318570af302Sopenharmony_ci if (dso == NULL) { 319570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] has null param!\n", __FUNCTION__); 320570af302Sopenharmony_ci return; 321570af302Sopenharmony_ci } 322570af302Sopenharmony_ci 323570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] unmap dso %{public}s from shadow!\n", __FUNCTION__, dso->name); 324570af302Sopenharmony_ci 325570af302Sopenharmony_ci if (cfi_shadow_start == NULL) 326570af302Sopenharmony_ci return; 327570af302Sopenharmony_ci 328570af302Sopenharmony_ci if (dso->map == 0 || dso->map_len == 0) 329570af302Sopenharmony_ci return; 330570af302Sopenharmony_ci 331570af302Sopenharmony_ci if (dso->is_mapped_to_shadow == false) 332570af302Sopenharmony_ci return; 333570af302Sopenharmony_ci 334570af302Sopenharmony_ci /* Set the dso's shadow value as invalid. */ 335570af302Sopenharmony_ci fill_shadow_value_to_shadow(dso->map, dso->map + dso->map_len, 0, sv_invalid); 336570af302Sopenharmony_ci dso->is_mapped_to_shadow = false; 337570af302Sopenharmony_ci prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); 338570af302Sopenharmony_ci 339570af302Sopenharmony_ci return; 340570af302Sopenharmony_ci} 341570af302Sopenharmony_ci 342570af302Sopenharmony_cistatic int create_cfi_shadow(void) 343570af302Sopenharmony_ci{ 344570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start!\n", __FUNCTION__); 345570af302Sopenharmony_ci 346570af302Sopenharmony_ci /* Each process can load up to (max_target_addr >> shadow_granularity) dsos. Shift left 1 bit because the shadow 347570af302Sopenharmony_ci * value is uint16_t. The size passed to mmap() should be aligned with 4096, so shadow_size should be aligned. */ 348570af302Sopenharmony_ci shadow_size = ALIGN_UP(((max_target_addr >> shadow_granularity) << 1), PAGE_SIZE); 349570af302Sopenharmony_ci 350570af302Sopenharmony_ci uintptr_t *mmap_addr = mmap(NULL, shadow_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); 351570af302Sopenharmony_ci 352570af302Sopenharmony_ci if (mmap_addr == MAP_FAILED) { 353570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] mmap failed!\n", __FUNCTION__); 354570af302Sopenharmony_ci return CFI_FAILED; 355570af302Sopenharmony_ci } 356570af302Sopenharmony_ci 357570af302Sopenharmony_ci cfi_shadow_start = (char*)mmap_addr; 358570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] the cfi_shadow_start addr is %{public}p!\n", __FUNCTION__, cfi_shadow_start); 359570af302Sopenharmony_ci 360570af302Sopenharmony_ci return CFI_SUCCESS; 361570af302Sopenharmony_ci} 362570af302Sopenharmony_ci 363570af302Sopenharmony_cistatic int add_dso_to_cfi_shadow(struct dso *dso) 364570af302Sopenharmony_ci{ 365570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] start with %{public}s !\n", __FUNCTION__, dso->name); 366570af302Sopenharmony_ci for (struct dso *p = dso; p; p = p->next) { 367570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] adding %{public}s to cfi shadow!\n", __FUNCTION__, p->name); 368570af302Sopenharmony_ci if (p->map == 0 || p->map_len == 0) { 369570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] the dso has no data! map[%{public}p] map_len[0x%{public}x]\n", 370570af302Sopenharmony_ci __FUNCTION__, p->map, p->map_len); 371570af302Sopenharmony_ci continue; 372570af302Sopenharmony_ci } 373570af302Sopenharmony_ci 374570af302Sopenharmony_ci if (p->is_mapped_to_shadow == true) { 375570af302Sopenharmony_ci LD_LOGW("[CFI] [%{public}s] %{public}s is already in shadow!\n", __FUNCTION__, p->name); 376570af302Sopenharmony_ci continue; 377570af302Sopenharmony_ci } 378570af302Sopenharmony_ci 379570af302Sopenharmony_ci struct symdef cfi_check_sym = find_cfi_check_sym(p); 380570af302Sopenharmony_ci /* If the dso doesn't have __cfi_check(), set it's shadow value unchecked. */ 381570af302Sopenharmony_ci if (!cfi_check_sym.sym) { 382570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] %{public}s has no __cfi_check()!\n", __FUNCTION__, p->name); 383570af302Sopenharmony_ci if (fill_shadow_value_to_shadow(p->map, p->map + p->map_len, 0, sv_uncheck) == CFI_FAILED) { 384570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] add dso to cfi shadow failed!\n", __FUNCTION__); 385570af302Sopenharmony_ci return CFI_FAILED; 386570af302Sopenharmony_ci } 387570af302Sopenharmony_ci /* If the dso has __cfi_check(), set it's shadow value valid. */ 388570af302Sopenharmony_ci } else { 389570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] %{public}s has __cfi_check()!\n", __FUNCTION__, p->name); 390570af302Sopenharmony_ci uintptr_t end = p->map + p->map_len; 391570af302Sopenharmony_ci uintptr_t cfi_check = LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value); 392570af302Sopenharmony_ci 393570af302Sopenharmony_ci if (cfi_check == 0) { 394570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] %{public}s has null cfi_check func!\n", __FUNCTION__, p->name); 395570af302Sopenharmony_ci return CFI_FAILED; 396570af302Sopenharmony_ci } 397570af302Sopenharmony_ci if (fill_shadow_value_to_shadow(p->map, end, cfi_check, sv_valid_min) == CFI_FAILED) { 398570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] add %{public}s to cfi shadow failed!\n", __FUNCTION__, p->name); 399570af302Sopenharmony_ci return CFI_FAILED; 400570af302Sopenharmony_ci } 401570af302Sopenharmony_ci } 402570af302Sopenharmony_ci p->is_mapped_to_shadow = true; 403570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] add %{public}s to cfi shadow succeed.\n", __FUNCTION__, p->name); 404570af302Sopenharmony_ci } 405570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] %{public}s done.\n", __FUNCTION__, dso->name); 406570af302Sopenharmony_ci 407570af302Sopenharmony_ci return CFI_SUCCESS; 408570af302Sopenharmony_ci} 409570af302Sopenharmony_ci 410570af302Sopenharmony_cistatic int fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type) 411570af302Sopenharmony_ci{ 412570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] begin[%{public}x] end[%{public}x] cfi_check[%{public}x] type[%{public}x]!\n", 413570af302Sopenharmony_ci __FUNCTION__, begin, end, cfi_check, type); 414570af302Sopenharmony_ci 415570af302Sopenharmony_ci /* To ensure the atomicity of the CFI shadow operation, we create a temp_shadow, write the shadow value to 416570af302Sopenharmony_ci * the temp_shadow, and then write it back to the CFI shadow by mremap(). */ 417570af302Sopenharmony_ci begin = ALIGN_DOWN(MAX(begin, cfi_check), shadow_alignment); 418570af302Sopenharmony_ci char* shadow_begin = cfi_shadow_start + addr_to_offset(begin, LIBRARY_ALIGNMENT_BITS); 419570af302Sopenharmony_ci char* shadow_end = (char*)(((uint16_t*)(cfi_shadow_start + addr_to_offset(end - 1, LIBRARY_ALIGNMENT_BITS))) + 1); 420570af302Sopenharmony_ci char* aligned_shadow_begin = (char*)ALIGN_DOWN((uintptr_t)shadow_begin, PAGE_SIZE); 421570af302Sopenharmony_ci char* aligned_shadow_end = (char*)ALIGN_UP((uintptr_t)shadow_end, PAGE_SIZE); 422570af302Sopenharmony_ci 423570af302Sopenharmony_ci uint16_t tmp_shadow_size = aligned_shadow_end - aligned_shadow_begin; 424570af302Sopenharmony_ci uint16_t offset_begin = shadow_begin - aligned_shadow_begin; 425570af302Sopenharmony_ci uint16_t offset_end = shadow_end - aligned_shadow_begin; 426570af302Sopenharmony_ci 427570af302Sopenharmony_ci char* tmp_shadow_start = (char*)mmap(NULL, tmp_shadow_size, 428570af302Sopenharmony_ci PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 429570af302Sopenharmony_ci 430570af302Sopenharmony_ci if (tmp_shadow_start == MAP_FAILED) { 431570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] mmap failed!\n", __FUNCTION__); 432570af302Sopenharmony_ci return CFI_FAILED; 433570af302Sopenharmony_ci } 434570af302Sopenharmony_ci 435570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] tmp_shadow_start is %{public}p\t tmp_shadow_size is 0x%{public}x!\n", 436570af302Sopenharmony_ci __FUNCTION__, tmp_shadow_start, tmp_shadow_size); 437570af302Sopenharmony_ci memcpy(tmp_shadow_start, aligned_shadow_begin, offset_begin); 438570af302Sopenharmony_ci memcpy(tmp_shadow_start + offset_end, shadow_end, aligned_shadow_end - shadow_end); 439570af302Sopenharmony_ci 440570af302Sopenharmony_ci /* If the dso has __cfi_check(), calculate valid shadow value */ 441570af302Sopenharmony_ci if (type == sv_valid_min) { 442570af302Sopenharmony_ci#ifdef __arm__ 443570af302Sopenharmony_ci uint16_t shadow_value_begin = ((begin + shadow_alignment - (cfi_check - 1)) 444570af302Sopenharmony_ci >> cfi_check_granularity) + sv_valid_min; 445570af302Sopenharmony_ci#else 446570af302Sopenharmony_ci uint16_t shadow_value_begin = ((begin + shadow_alignment - cfi_check) 447570af302Sopenharmony_ci >> cfi_check_granularity) + sv_valid_min; 448570af302Sopenharmony_ci#endif 449570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] shadow_value_begin is 0x%{public}x!\n", __FUNCTION__, shadow_value_begin); 450570af302Sopenharmony_ci uint16_t shadow_value_step = 1 << (shadow_granularity - cfi_check_granularity); 451570af302Sopenharmony_ci uint16_t shadow_value = shadow_value_begin; 452570af302Sopenharmony_ci 453570af302Sopenharmony_ci /* Set shadow_value */ 454570af302Sopenharmony_ci for (uint16_t *shadow_addr = tmp_shadow_start + offset_begin; 455570af302Sopenharmony_ci shadow_addr != tmp_shadow_start + offset_end; shadow_addr++) { 456570af302Sopenharmony_ci /* If a dso is larger than 16G( = max_shadow_value * shadow_alignment / 1G), 457570af302Sopenharmony_ci * the excess is not checked. */ 458570af302Sopenharmony_ci if (shadow_value < shadow_value_begin) { 459570af302Sopenharmony_ci *shadow_addr = sv_uncheck; 460570af302Sopenharmony_ci continue; 461570af302Sopenharmony_ci } 462570af302Sopenharmony_ci *shadow_addr = (*shadow_addr == sv_invalid) ? shadow_value : sv_uncheck; 463570af302Sopenharmony_ci shadow_value += shadow_value_step; 464570af302Sopenharmony_ci } 465570af302Sopenharmony_ci /* in these cases, shadow_value will always be sv_uncheck or sv_invalid */ 466570af302Sopenharmony_ci } else if (type == sv_uncheck || type == sv_invalid) { 467570af302Sopenharmony_ci /* Set shadow_value */ 468570af302Sopenharmony_ci for (uint16_t *shadow_addr = tmp_shadow_start + offset_begin; 469570af302Sopenharmony_ci shadow_addr != tmp_shadow_start + offset_end; shadow_addr++) { 470570af302Sopenharmony_ci *shadow_addr = type; 471570af302Sopenharmony_ci } 472570af302Sopenharmony_ci } else { 473570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] has error param!\n", __FUNCTION__); 474570af302Sopenharmony_ci munmap(tmp_shadow_start, tmp_shadow_size); 475570af302Sopenharmony_ci return CFI_FAILED; 476570af302Sopenharmony_ci } 477570af302Sopenharmony_ci 478570af302Sopenharmony_ci mprotect(tmp_shadow_start, tmp_shadow_size, PROT_READ); 479570af302Sopenharmony_ci /* Remap temp_shadow to CFI shadow. */ 480570af302Sopenharmony_ci uint16_t* mremap_addr = mremap(tmp_shadow_start, tmp_shadow_size, tmp_shadow_size, 481570af302Sopenharmony_ci MREMAP_MAYMOVE | MREMAP_FIXED, aligned_shadow_begin); 482570af302Sopenharmony_ci 483570af302Sopenharmony_ci if (mremap_addr == MAP_FAILED) { 484570af302Sopenharmony_ci LD_LOGE("[CFI] [%{public}s] mremap failed!\n", __FUNCTION__); 485570af302Sopenharmony_ci munmap(tmp_shadow_start, tmp_shadow_size); 486570af302Sopenharmony_ci return CFI_FAILED; 487570af302Sopenharmony_ci } 488570af302Sopenharmony_ci 489570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] fill completed!\n", __FUNCTION__); 490570af302Sopenharmony_ci return CFI_SUCCESS; 491570af302Sopenharmony_ci} 492570af302Sopenharmony_ci 493570af302Sopenharmony_civoid __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr) 494570af302Sopenharmony_ci{ 495570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] called from dso[%{public}s] to dso[%{public}s] func_ptr[%{public}p]\n", 496570af302Sopenharmony_ci __FUNCTION__, 497570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)__builtin_return_address(0)))->name, 498570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)func_ptr))->name, 499570af302Sopenharmony_ci func_ptr); 500570af302Sopenharmony_ci 501570af302Sopenharmony_ci cfi_slowpath_common(call_site_type_id, func_ptr, NULL); 502570af302Sopenharmony_ci return; 503570af302Sopenharmony_ci} 504570af302Sopenharmony_ci 505570af302Sopenharmony_civoid __cfi_slowpath_diag(uint64_t call_site_type_id, void *func_ptr, void *diag_data) 506570af302Sopenharmony_ci{ 507570af302Sopenharmony_ci LD_LOGD("[CFI] [%{public}s] called from dso[%{public}s] to dso[%{public}s] func_ptr[%{public}p]\n", 508570af302Sopenharmony_ci __FUNCTION__, 509570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)__builtin_return_address(0)))->name, 510570af302Sopenharmony_ci ((struct dso *)addr2dso((size_t)func_ptr))->name, 511570af302Sopenharmony_ci func_ptr); 512570af302Sopenharmony_ci 513570af302Sopenharmony_ci cfi_slowpath_common(call_site_type_id, func_ptr, diag_data); 514570af302Sopenharmony_ci return; 515570af302Sopenharmony_ci} 516