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