xref: /third_party/musl/ldso/linux/cfi.c (revision 570af302)
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