diff --git a/libc-test/src/functionalext/ldso_cfi/BUILD.gn b/libc-test/src/functionalext/ldso_cfi/BUILD.gn index 1386220e9bf08b98106a9f1f8ceca3ccce24ad60..17ed34de798789e05b0fd3fe9c6bca24a7911a80 100644 --- a/libc-test/src/functionalext/ldso_cfi/BUILD.gn +++ b/libc-test/src/functionalext/ldso_cfi/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Huawei Device Co., Ltd. +# Copyright (c) 2023 Huawei Device Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -22,8 +22,6 @@ group("ldso_cfi_test") { } ohos_executable("ldso_cfi_check") { - # cflags_c = ["--coverage"] - # ldflags = ["--coverage"] subsystem_name = "musl" part_name = "libc-test" include_dirs = [ @@ -41,8 +39,6 @@ ohos_shared_library("ldso_cfi_test_lib") { include_dirs = [ "." ] sources = [ "ldso_cfi_test_lib.c" ] - # cflags_c = ["--coverage"] - # ldflags = ["--coverage"] output_name = "ldso_cfi_test_lib" output_extension = "so" subsystem_name = "musl" diff --git a/libc-test/src/functionalext/ldso_cfi/ldso_cfi_check.c b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_check.c index c561a81276c02ab210305499dff2a48ac8bd3a92..ac5884b2b945fb2bf07ba6407e55c7cfd5432230 100644 --- a/libc-test/src/functionalext/ldso_cfi/ldso_cfi_check.c +++ b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_check.c @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -25,10 +25,12 @@ struct dso { char *mock; + unsigned char *map; + size_t map_len; }; -extern bool init_cfi_shadow(struct dso *dso_list); -extern bool map_dso_to_cfi_shadow(struct dso *dso); +extern int init_cfi_shadow(struct dso *dso_list); +extern int map_dso_to_cfi_shadow(struct dso *dso); extern void unmap_dso_from_cfi_shadow(struct dso *dso); extern void __cfi_slowpath(uint64_t CallSiteTypeId, void *Ptr); extern void __cfi_slowpath_diag(uint64_t CallSiteTypeId, void *Ptr, void *DiagData); @@ -60,7 +62,7 @@ static void test_func() {} void cfi_init_test_0001(void) { printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); - EXPECT_TRUE("cfi_init_test_0001", init_cfi_shadow(NULL)); + EXPECT_EQ("cfi_init_test_0001", init_cfi_shadow(NULL), 0); } /** @@ -71,7 +73,7 @@ void cfi_init_test_0001(void) void cfi_init_test_0002(void) { printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); - EXPECT_TRUE("cfi_init_test_0002", map_dso_to_cfi_shadow(NULL)); + EXPECT_EQ("cfi_init_test_0002", map_dso_to_cfi_shadow(NULL), 0); } /** @@ -320,6 +322,37 @@ void cfi_slowpath_diag_function_test_0001(void) printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); } +/** + * @tc.name : cfi_unmap_dso_from_cfi_shadow_001 + * @tc.desc : If dso map is NULL while unmapping from the CFI shadow, do nothing. + * @tc.level : Level 2 + */ +void cfi_unmap_dso_from_cfi_shadow_001(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + struct dso test_dso = {}; + test_dso.map = 0; + test_dso.map_len = 1; + unmap_dso_from_cfi_shadow(&test_dso); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + +/** + * @tc.name : cfi_unmap_dso_from_cfi_shadow_002 + * @tc.desc : If dso map_len is NULL while unmapping from the CFI shadow, do nothing. + * @tc.level : Level 2 + */ +void cfi_unmap_dso_from_cfi_shadow_002(void) +{ + printf("["__FILE__"][Line: %d][%s]: entry\n", __LINE__, __func__); + struct dso test_dso = {}; + int a = 9; + test_dso.map = (unsigned char *)a; + test_dso.map_len = 0; + unmap_dso_from_cfi_shadow(&test_dso); + printf("["__FILE__"][Line: %d][%s]: end\n", __LINE__, __func__); +} + TEST_FUN G_Fun_Array[] = { cfi_init_test_0001, cfi_init_test_0002, @@ -332,6 +365,8 @@ TEST_FUN G_Fun_Array[] = { cfi_slowpath_function_test_0007, cfi_slowpath_function_test_0008, cfi_slowpath_diag_function_test_0001, + cfi_unmap_dso_from_cfi_shadow_001, + cfi_unmap_dso_from_cfi_shadow_002, }; int main(void) diff --git a/libc-test/src/functionalext/ldso_cfi/ldso_cfi_test_lib.c b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_test_lib.c index b1655f54d21e8fc8090251678bcd34fd614dcb4c..9f5ae2a1ded0bb4d7238a0cb48d4481400d86771 100644 --- a/libc-test/src/functionalext/ldso_cfi/ldso_cfi_test_lib.c +++ b/libc-test/src/functionalext/ldso_cfi/ldso_cfi_test_lib.c @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include #include #include diff --git a/porting/linux/user/ldso/cfi.c b/porting/linux/user/ldso/cfi.c index b685dc427f3e7276252893a64896eaaedd85c1db..64814ee95e20a42d6704299532af91c4479842cd 100755 --- a/porting/linux/user/ldso/cfi.c +++ b/porting/linux/user/ldso/cfi.c @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #define _GNU_SOURCE #include #include @@ -7,13 +22,48 @@ /* This module provides support for LLVM CFI Cross-DSO by implementing the __cfi_slowpath() and __cfi_slowpath_diag() * functions. These two functions will be called before visiting other dso's resources. The responsibility is to - * calculate the __cfi_check() of the target dso, and call it. - * The relationship between __cfi_check and dso addr is: - * __cfi_check = AlignDown(addr, shadow_alignment) + shadow_alignment - (shadow_value - 2) * 4096; - * The shadow_alignment presents the size of memory mapped by one shadow value. - * The shadow_value presents the distance between the __cfi_check() and the the end address of each shadow alignment - * in the dso. It can be presented as a multiple of 4096. - * The CFI shadow is used to store shadow value(s) of each dso. + * calculate the __cfi_check() of the target dso, and call it. So use CFI shadow and shadow value to store the + * relationship between dso and its __cfi_check addr while loading a dso. CFI shadow is an array which stores shadow + * values. Shadow value is used to store the relationship. A shadow value can map 1 LIBRARY_ALIGNMENT memory range. So + * each dso will be mapped to one or more shadow values in the CFI shadow, this depends on the address range of the + * dso. + * There are 3 types for shadow value: + * - invalid(0) : the target addr does not belongs to any loaded dso. + * - uncheck(1) : this LIBRARY_ALIGNMENT memory range belongs to a dso but it is no need to do the CFI check. + * - valid(2 - 0xFFFF) : this LIBRARY_ALIGNMENT memory range belongs to a dso and need to do the CFI check. + * The valid shadow value records the distance from the end of a LIBRARY_ALIGNMENT memory range to the __cfi_check addr + * of the dso (The unit is 4096, because the __cfi_check is aligned with 4096). + * The valid shadow value is calculated as below: + * sv = (AlignUp(__cfi_check, LIBRARY_ALIGNMENT) - cfi_check_addr + N * LIBRARY_ALIGNMENT) / 4096 + 2; + * + * N : starts at 0, is the index of LIBRARY_ALIGNMENT memory range that belongs to a dso. + * + 2 : to avoid conflict with invalid and uncheck shadow value. + * + * Below is a example for calculating shadow values of a dso. + * liba.so + * /\ + * /'''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''\ + * 0x40000 __cfi_check addr = 0x42000 0x80000 0xA0000 0xC0000 + * +---------^----------------------------------------^-------------------------^-------------------------+ + * Memory | | | | | + * +------------------------------------------------------------------------------------------------------+ + * \........... LIBRARY_ALIGNMENT ..................../\........... LIBRARY_ALIGNMENT ..................../ / + * \ / / + * \ / / + * \ / / + * \ / / + * \ / / + * +-----------------------------------------------------------------------------------------------------+ + * CFI shadow | invalid | sv1 | sv2 | invalid | + * +-----------------------------------------------------------------------------------------------------+ + * sv1 = (0x80000 - 0x42000 + 0 * LIBRARY_ALIGNMENT) / 4096 + 2 = 64 + * sv2 = (0x80000 - 0x42000 + 1 * LIBRARY_ALIGNMENT) / 4096 + 2 = 126 + * + * Calculating the __cfi_check address is a reverse process: + * - First align up the target addr with LIBRARY_ALIGNMENT to locate the corresponding shadow value. + * - Then calculate the __cfi_check addr. + * + * In order for the algorithm to work well, the start addr of each dso should be aligned with LIBRARY_ALIGNMENT. */ #define MAX(a,b) (((a) > (b)) ? (a) : (b)) @@ -39,8 +89,20 @@ static char *cfi_shadow_start = NULL; static struct dso *dso_list_head = NULL; /* Shadow value */ +/* The related shadow value(s) will be set to `sv_invalid` when: + * - init CFI shadow. + * - removing a dso. + */ static const uint16_t sv_invalid = 0; +/* The related shadow value(s) will be set to `sv_uncheck` if: + * - the DSO does not enable CFI Cross-Dso. + * - the DSO enabled CFI Cross-Dso, but this DSO is larger than 16G, for the part of the dso that exceeds 16G, + * its shadow value will be set to `sv_uncheck`. + */ static const uint16_t sv_uncheck = 1; +/* If a DSO enabled CFI Cross-Dso, the DSO's shadow value should be valid. Because of the defination of `sv_invalid` + * and `sv_unchecked`, the valid shadow value should be at least 2. + */ static const uint16_t sv_valid_min = 2; #if defined(__LP64__) @@ -50,11 +112,11 @@ static const uintptr_t max_target_addr = 0xffffffff; #endif /* Create a cfi shadow */ -static bool create_cfi_shadow(void); +static int create_cfi_shadow(void); /* Map dsos to CFI shadow */ -static bool add_dso_to_cfi_shadow(struct dso *dso); -static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type); +static int add_dso_to_cfi_shadow(struct dso *dso); +static int fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type); /* Find the __cfi_check() of target dso and call it */ void __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr); @@ -68,37 +130,24 @@ static inline uintptr_t addr_to_offset(uintptr_t addr, int bits) return (addr >> bits) << 1; } -static void set_cfi_shadow_name() -{ - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); - - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); - - return; -} - static struct symdef find_cfi_check_sym(struct dso *p) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); - if (p == NULL) { - LD_LOGE("[%{public}s] has null param!\n", __FUNCTION__); - struct symdef emptysym = {0}; - return emptysym; - } - ns_t *ns = p->namespace; + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); + struct verinfo verinfo = { .s = "__cfi_check", .v = "", .use_vna_hash = false }; - return find_sym2(p, &verinfo, 0, 1, ns); + struct sym_info_pair s_info_p = gnu_hash(verinfo.s); + return find_sym_impl(p, &verinfo, s_info_p, 0, p->namespace); } static uintptr_t get_cfi_check_addr(uint16_t value, void* func_ptr) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); uintptr_t addr = (uintptr_t)func_ptr; uintptr_t aligned_addr = ALIGN_DOWN(addr, shadow_alignment) + shadow_alignment; uintptr_t cfi_check_func_addr = aligned_addr - ((uintptr_t)(value - sv_valid_min) << cfi_check_granularity); #ifdef __arm__ - LD_LOGI("[%{public}s] __arm__ defined!\n", __FUNCTION__); + LD_LOGD("[%{public}s] __arm__ defined!\n", __FUNCTION__); cfi_check_func_addr++; #endif LD_LOGI("[%{public}s] the cfi_check_func_addr is %{public}p!\n", __FUNCTION__, cfi_check_func_addr); @@ -108,21 +157,27 @@ static uintptr_t get_cfi_check_addr(uint16_t value, void* func_ptr) static void cfi_slowpath_common(uint64_t call_site_type_id, void *func_ptr, void *diag_data) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); LD_LOGI("[%{public}s] func_ptr[%{public}p] !\n", __FUNCTION__, func_ptr); uint16_t value = sv_invalid; #if defined(__aarch64__) - LD_LOGI("[%{public}s] __aarch64__ defined!\n", __FUNCTION__); + LD_LOGD("[%{public}s] __aarch64__ defined!\n", __FUNCTION__); uintptr_t addr = (uintptr_t)func_ptr & ((1ULL << 56) - 1); #else - LD_LOGI("[%{public}s] __aarch64__ not defined!\n", __FUNCTION__); + LD_LOGD("[%{public}s] __aarch64__ not defined!\n", __FUNCTION__); uintptr_t addr = func_ptr; #endif /* Get shadow value */ uintptr_t offset = addr_to_offset(addr, shadow_granularity); + + if (cfi_shadow_start == NULL) { + LD_LOGE("[%{public}s] the cfi_shadow_start is null!\n", __FUNCTION__); + __builtin_trap(); + } + if (offset > shadow_size) { value = sv_invalid; } else { @@ -146,7 +201,7 @@ static void cfi_slowpath_common(uint64_t call_site_type_id, void *func_ptr, void LD_LOGE("[%{public}s] can not find the __cfi_check in the dso!\n", __FUNCTION__); __builtin_trap(); } - LD_LOGI("[%{public}s] cfi_check addr[%{public}p]!\n", + LD_LOGD("[%{public}s] cfi_check addr[%{public}p]!\n", __FUNCTION__, LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value)); ((cfi_check_t)LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value))(call_site_type_id, func_ptr, diag_data); break; @@ -160,13 +215,13 @@ static void cfi_slowpath_common(uint64_t call_site_type_id, void *func_ptr, void return; } -bool init_cfi_shadow(struct dso *dso_list) +int init_cfi_shadow(struct dso *dso_list) { LD_LOGI("[%{public}s] start!\n", __FUNCTION__); if (dso_list == NULL) { LD_LOGW("[%{public}s] has null param!\n", __FUNCTION__); - return true; + return CFI_SUCCESS; } /* Save the head node of dso list */ @@ -175,15 +230,15 @@ bool init_cfi_shadow(struct dso *dso_list) return map_dso_to_cfi_shadow(dso_list); } -bool map_dso_to_cfi_shadow(struct dso *dso) +int map_dso_to_cfi_shadow(struct dso *dso) { LD_LOGI("[%{public}s] start!\n", __FUNCTION__); bool has_cfi_check = false; if (dso == NULL) { - LD_LOGE("[%{public}s] has null param!\n", __FUNCTION__); - return true; + LD_LOGW("[%{public}s] has null param!\n", __FUNCTION__); + return CFI_SUCCESS; } /* Find __cfi_check symbol in dso list */ @@ -198,27 +253,27 @@ bool map_dso_to_cfi_shadow(struct dso *dso) /* If the cfi shadow does not exist, create it and map all the dsos and its dependents to it. */ if (cfi_shadow_start == NULL) { if (has_cfi_check) { - if (!create_cfi_shadow()) { + if (create_cfi_shadow() == CFI_FAILED) { LD_LOGE("[%{public}s] create cfi shadow failed!\n", __FUNCTION__); - return false; + return CFI_FAILED; } - LD_LOGI("[%{public}s] add_dso_to_cfi_shadow with dso_list_head!\n", __FUNCTION__); + LD_LOGD("[%{public}s] add_dso_to_cfi_shadow with dso_list_head!\n", __FUNCTION__); add_dso_to_cfi_shadow(dso_list_head); - set_cfi_shadow_name(); + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); } /* If the cfi shadow exists, map the current dso and its dependents to it. */ } else { - LD_LOGI("[%{public}s] add_dso_to_cfi_shadow with dso!\n", __FUNCTION__); + LD_LOGD("[%{public}s] add_dso_to_cfi_shadow with dso!\n", __FUNCTION__); add_dso_to_cfi_shadow(dso); - set_cfi_shadow_name(); + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); } - return true; + return CFI_SUCCESS; } void unmap_dso_from_cfi_shadow(struct dso *dso) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); if (dso == NULL) { LD_LOGE("[%{public}s] has null param!\n", __FUNCTION__); @@ -233,16 +288,20 @@ void unmap_dso_from_cfi_shadow(struct dso *dso) if (dso->map == 0 || dso->map_len == 0) return; + if (dso->is_mapped_to_shadow == false) + return; + /* Set the dso's shadow value as invalid. */ fill_shadow_value_to_shadow(dso->map, dso->map + dso->map_len, 0, sv_invalid); - set_cfi_shadow_name(); + dso->is_mapped_to_shadow = false; + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, cfi_shadow_start, shadow_size, "cfi_shadow:musl"); return; } -static bool create_cfi_shadow(void) +static int create_cfi_shadow(void) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); /* Each process can load up to (max_target_addr >> shadow_granularity) dsos. Shift left 1 bit because the shadow * value is uint16_t. The size passed to mmap() should be aligned with 4096, so shadow_size should be aligned. @@ -253,66 +312,67 @@ static bool create_cfi_shadow(void) if (mmap_addr == MAP_FAILED) { LD_LOGE("[%{public}s] mmap failed!\n", __FUNCTION__); - return false; + return CFI_FAILED; } cfi_shadow_start = (char*)mmap_addr; LD_LOGI("[%{public}s] the cfi_shadow_start addr is %{public}p!\n", __FUNCTION__, cfi_shadow_start); - return true; + return CFI_SUCCESS; } -static bool add_dso_to_cfi_shadow(struct dso *dso) +static int add_dso_to_cfi_shadow(struct dso *dso) { LD_LOGI("[%{public}s] start!\n", __FUNCTION__); for (struct dso *p = dso; p; p = p->next) { LD_LOGI("[%{public}s] start to deal with dso %{public}s!\n", __FUNCTION__, p->name); if (p->map == 0 || p->map_len == 0) { - LD_LOGI("[%{public}s] the dso has no data!\n", __FUNCTION__); + LD_LOGW("[%{public}s] the dso has no data!\n", __FUNCTION__); + continue; + } + + if (p->is_mapped_to_shadow == true) { + LD_LOGW("[%{public}s] the dso is already in shadow!\n", __FUNCTION__); continue; } struct symdef cfi_check_sym = find_cfi_check_sym(p); /* If the dso doesn't have __cfi_check(), set it's shadow value unchecked. */ if (!cfi_check_sym.sym) { - LD_LOGI("[%{public}s] the dso has no __cfi_check func, call fill_shadow_value_to_shadow!\n", __FUNCTION__); - if (!fill_shadow_value_to_shadow(p->map, p->map + p->map_len, 0, sv_uncheck)) { + LD_LOGI("[%{public}s] the dso has no __cfi_check()!\n", __FUNCTION__); + if (fill_shadow_value_to_shadow(p->map, p->map + p->map_len, 0, sv_uncheck) == CFI_FAILED) { LD_LOGE("[%{public}s] add dso to cfi shadow failed!\n", __FUNCTION__); - return false; + return CFI_FAILED; } /* If the dso has __cfi_check(), set it's shadow value valid. */ } else { - LD_LOGI("[%{public}s] the dso has __cfi_check func,call fill_shadow_value_to_shadow!\n", __FUNCTION__); + LD_LOGI("[%{public}s] the dso has __cfi_check()!\n", __FUNCTION__); uintptr_t end = p->map + p->map_len; uintptr_t cfi_check = LADDR(cfi_check_sym.dso, cfi_check_sym.sym->st_value); if (cfi_check == 0) { LD_LOGE("[%{public}s] the dso has null cfi_check func!\n", __FUNCTION__); - return false; + return CFI_FAILED; } - if (!fill_shadow_value_to_shadow(p->map, end, cfi_check, sv_valid_min)) { + if (fill_shadow_value_to_shadow(p->map, end, cfi_check, sv_valid_min) == CFI_FAILED) { LD_LOGE("[%{public}s] add dso to cfi shadow failed!\n", __FUNCTION__); - return false; + return CFI_FAILED; } } + p->is_mapped_to_shadow = true; LD_LOGI("[%{public}s] finish to deal with dso %{public}s!\n", __FUNCTION__, p->name); } - return true; + return CFI_SUCCESS; } -static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type) +static int fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_t cfi_check, uint16_t type) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); LD_LOGI("[%{public}s] begin[%{public}x] end[%{public}x] cfi_check[%{public}x] type[%{public}x]!\n", __FUNCTION__, begin, end, cfi_check, type); - if (begin == 0 || end == 0) { - LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); - return false; - } - /* To ensure the atomicity of the CFI shadow operation, we create a temp_shadow, write the shadow value to * the temp_shadow, and then write it back to the CFI shadow by mremap().*/ begin = ALIGN_DOWN(MAX(begin, cfi_check), shadow_alignment); @@ -330,10 +390,10 @@ static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_ if (tmp_shadow_start == MAP_FAILED) { LD_LOGE("[%{public}s] mmap failed!\n", __FUNCTION__); - return false; + return CFI_FAILED; } - LD_LOGI("[%{public}s] tmp_shadow_start is %{public}p\t tmp_shadow_size is 0x%{public}x!\n", + LD_LOGD("[%{public}s] tmp_shadow_start is %{public}p\t tmp_shadow_size is 0x%{public}x!\n", __FUNCTION__, tmp_shadow_start, tmp_shadow_size); memcpy(tmp_shadow_start, aligned_shadow_begin, offset_begin); memcpy(tmp_shadow_start + offset_end, shadow_end, aligned_shadow_end - shadow_end); @@ -354,11 +414,14 @@ static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_ /* Set shadow_value */ for (uint16_t *shadow_addr = tmp_shadow_start + offset_begin; shadow_addr != tmp_shadow_start + offset_end; shadow_addr++) { + /* If a dso is larger than 16G( = max_shadow_value * shadow_alignment / 1G), + * the excess is not checked. + */ if (shadow_value < shadow_value_begin) { *shadow_addr = sv_uncheck; continue; } - *shadow_addr = shadow_value; + *shadow_addr = (*shadow_addr == sv_invalid) ? shadow_value : sv_uncheck; shadow_value += shadow_value_step; } /* in these cases, shadow_value will always be sv_uncheck or sv_invalid */ @@ -370,7 +433,8 @@ static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_ } } else { LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); - return false; + munmap(tmp_shadow_start, tmp_shadow_size); + return CFI_FAILED; } mprotect(tmp_shadow_start, tmp_shadow_size, PROT_READ); @@ -378,30 +442,25 @@ static bool fill_shadow_value_to_shadow(uintptr_t begin, uintptr_t end, uintptr_ uint16_t* mremap_addr = mremap(tmp_shadow_start, tmp_shadow_size, tmp_shadow_size, MREMAP_MAYMOVE | MREMAP_FIXED, aligned_shadow_begin); - if (mremap_addr == MAP_FAILED) - { + if (mremap_addr == MAP_FAILED) { LD_LOGE("[%{public}s] mremap failed!\n", __FUNCTION__); - return false; + munmap(tmp_shadow_start, tmp_shadow_size); + return CFI_FAILED; } - LD_LOGI("[%{public}s] fill completed!\n", __FUNCTION__); - return true; + LD_LOGD("[%{public}s] fill completed!\n", __FUNCTION__); + return CFI_SUCCESS; } void __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); if (func_ptr == NULL) { LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); return; } - if (cfi_shadow_start == NULL) { - LD_LOGE("[%{public}s] the cfi_shadow_start is null!\n", __FUNCTION__); - __builtin_trap(); - } - cfi_slowpath_common(call_site_type_id, func_ptr, NULL); return; @@ -409,18 +468,13 @@ void __cfi_slowpath(uint64_t call_site_type_id, void *func_ptr) void __cfi_slowpath_diag(uint64_t call_site_type_id, void *func_ptr, void *diag_data) { - LD_LOGI("[%{public}s] start!\n", __FUNCTION__); + LD_LOGD("[%{public}s] start!\n", __FUNCTION__); if (func_ptr == NULL) { LD_LOGE("[%{public}s] has error param!\n", __FUNCTION__); return; } - if (cfi_shadow_start == NULL) { - LD_LOGE("[%{public}s] the cfi_shadow_start is null!\n", __FUNCTION__); - __builtin_trap(); - } - cfi_slowpath_common(call_site_type_id, func_ptr, diag_data); return; diff --git a/porting/linux/user/ldso/cfi.h b/porting/linux/user/ldso/cfi.h index 4ed990a4eeaa162649466e21d982bc075a27590a..8ae8e1b31e2b13365697a92be8522b8557b151c2 100755 --- a/porting/linux/user/ldso/cfi.h +++ b/porting/linux/user/ldso/cfi.h @@ -1,13 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "dynlink.h" -/* alignment bits in memory space for dso */ +#define CFI_SUCCESS 0 +#define CFI_FAILED -1 +/* Define LIBRARY_ALIGNMENT to balance the size of CFI shadow and the number of DSOs that each process can load. + * The LIBRARY_ALIGNMENT is use to calculate the start address of a DSO. Each DSO should be in the different + * LIBRARY_ALIGNMENT memory range so that each DSO can be well mapped to the CFI shadow. + */ #define LIBRARY_ALIGNMENT_BITS 18 -/* minimum unit in memory space for dso */ #define LIBRARY_ALIGNMENT (1UL << LIBRARY_ALIGNMENT_BITS) /* map all the dso and the dependents to cfi shadow */ -bool init_cfi_shadow(struct dso *dso_list); +int init_cfi_shadow(struct dso *dso_list); /* map a dso and the dependents to cfi shadow */ -bool map_dso_to_cfi_shadow(struct dso *dso); +int map_dso_to_cfi_shadow(struct dso *dso); /* unmap a dso from cfi shadow */ void unmap_dso_from_cfi_shadow(struct dso *dso); \ No newline at end of file diff --git a/porting/linux/user/ldso/dynlink.c b/porting/linux/user/ldso/dynlink.c index b0388e2f800749eaa842562b61d558169332dc76..b5e0c5c17a408ef81c56accfdce94483c2758463 100644 --- a/porting/linux/user/ldso/dynlink.c +++ b/porting/linux/user/ldso/dynlink.c @@ -28,10 +28,10 @@ #include #include +#include "cfi.h" #include "dlfcn_ext.h" #include "dynlink_rand.h" #include "ld_log.h" -#include "cfi.h" #include "libc.h" #include "malloc_impl.h" #include "namespace.h" @@ -90,11 +90,6 @@ struct reserved_address_params { #endif }; -struct sym_info_pair { - uint_fast32_t sym_h; - uint32_t sym_l; -}; - typedef void (*stage3_func)(size_t *, size_t *); static struct builtin_tls { @@ -491,7 +486,7 @@ static struct sym_info_pair sysv_hash(const char *s0) return s_info_p; } -static struct sym_info_pair gnu_hash(const char *s0) +struct sym_info_pair gnu_hash(const char *s0) { struct sym_info_pair s_info_p; const unsigned char *s = (void *)s0; @@ -706,7 +701,61 @@ static void add_can_search_so_list_in_dso(struct dso *dso_relocating, struct dso #if defined(__GNUC__) __attribute__((always_inline)) #endif -struct symdef find_sym2(struct dso *dso, struct verinfo *verinfo, int need_def, int use_deps, ns_t *ns) + +struct symdef find_sym_impl( + struct dso *dso, struct verinfo *verinfo, struct sym_info_pair s_info_p, int need_def, ns_t *ns) +{ + Sym *sym; + uint32_t *ght; + uint32_t h = 0; + uint32_t gh = s_info_p.sym_h; + uint32_t gho = gh / (8 * sizeof(size_t)); + size_t ghm = 1ul << gh % (8 * sizeof(size_t)); + struct symdef def = {0}; + if (ns && !check_sym_accessible(dso, ns)) + return def; + + if ((ght = dso->ghashtab)) { + const size_t *bloomwords = (const void *)(ght + 4); + size_t f = bloomwords[gho & (ght[2] - 1)]; + if (!(f & ghm)) + return def; + + f >>= (gh >> ght[3]) % (8 * sizeof f); + if (!(f & 1)) + return def; + + sym = gnu_lookup(s_info_p, ght, dso, verinfo); + } else { + if (!h) + s_info_p = sysv_hash(verinfo->s); + + sym = sysv_lookup(verinfo, s_info_p, dso); + } + + if (!sym) + return def; + + if (!sym->st_shndx) + if (need_def || (sym->st_info & 0xf) == STT_TLS || ARCH_SYM_REJECT_UND(sym)) + return def; + + if (!sym->st_value) + if ((sym->st_info & 0xf) != STT_TLS) + return def; + + if (!(1 << (sym->st_info & 0xf) & OK_TYPES)) + return def; + + if (!(1 << (sym->st_info >> 4) & OK_BINDS)) + return def; + + def.sym = sym; + def.dso = dso; + return def; +} + +static inline struct symdef find_sym2(struct dso *dso, struct verinfo *verinfo, int need_def, int use_deps, ns_t *ns) { struct sym_info_pair s_info_p = gnu_hash(verinfo->s); uint32_t h = 0, gh = s_info_p.sym_h, gho = gh / (8*sizeof(size_t)), *ght; @@ -2835,11 +2884,8 @@ void __dls3(size_t *sp, size_t *auxv) libc.tls_size = tmp_tls_size; } - if (!init_cfi_shadow(head)) { + if (init_cfi_shadow(head) == CFI_FAILED) { error("[%s] init_cfi_shadow failed: %m", __FUNCTION__); - if (runtime) { - longjmp(*rtld_fail, 1); - } } if (ldso_fail) _exit(127); @@ -3142,11 +3188,9 @@ static void *dlopen_impl( * relocations resolved to symbol definitions that get removed. */ redo_lazy_relocs(); - if (!map_dso_to_cfi_shadow(p)) { + if (map_dso_to_cfi_shadow(p) == CFI_FAILED) { error("[%s] map_dso_to_cfi_shadow failed: %m", __FUNCTION__); - if (runtime) { - longjmp(*rtld_fail, 1); - } + longjmp(*rtld_fail, 1); } if (mode & RTLD_NODELETE) { @@ -4138,7 +4182,6 @@ static bool task_map_library(struct loadtask *task, struct reserved_address_para ? mmap((void *)start_addr, map_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) : mmap((void *)start_addr, map_len, prot, map_flags, task->fd, off_start + task->file_offset); if (map == MAP_FAILED) { - LD_LOGE("Error mapping library %{public}s: failed to map fd", task->name); goto error; } if (reserved_params && map_len < reserved_params->reserved_size) { @@ -4150,7 +4193,6 @@ static bool task_map_library(struct loadtask *task, struct reserved_address_para /* use tmp_map_len to mmap enough space for the dso with anonymous mapping */ unsigned char *temp_map = mmap((void *)NULL, tmp_map_len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (temp_map == MAP_FAILED) { - LD_LOGE("Error mapping library 1 %{public}s: failed to map fd", task->name); goto error; } @@ -4165,7 +4207,6 @@ static bool task_map_library(struct loadtask *task, struct reserved_address_para /* use map_len to mmap correct space for the dso with file mapping */ : mmap(real_map, map_len, prot, map_flags, task->fd, off_start + task->file_offset); if (map == MAP_FAILED) { - LD_LOGE("Error mapping library 3 %{public}s: failed to map fd", task->name); goto error; } } diff --git a/porting/linux/user/src/internal/dynlink.h b/porting/linux/user/src/internal/dynlink.h index ccfe2447d910008adaa9359dbb29ff7e81e61830..680412ec1bd5e7f1645cdccc429f584ca9ca6649 100644 --- a/porting/linux/user/src/internal/dynlink.h +++ b/porting/linux/user/src/internal/dynlink.h @@ -58,6 +58,23 @@ enum { REL_FUNCDESC_VAL, }; +struct td_index { + size_t args[2]; + struct td_index *next; +}; + +struct verinfo { + const char *s; + const char *v; + bool use_vna_hash; + uint32_t vna_hash; +}; + +struct sym_info_pair { + uint_fast32_t sym_h; + uint32_t sym_l; +}; + struct dso { #if DL_FDPIC struct fdpic_loadmap *loadmap; @@ -132,6 +149,7 @@ struct dso { struct dso **reloc_can_search_dso_list; size_t reloc_can_search_dso_count; size_t reloc_can_search_dso_capacity; + bool is_mapped_to_shadow; char buf[]; }; @@ -140,18 +158,6 @@ struct symdef { struct dso *dso; }; -struct verinfo { - const char *s; - const char *v; - bool use_vna_hash; - uint32_t vna_hash; -}; - -struct td_index { - size_t args[2]; - struct td_index *next; -}; - struct fdpic_loadseg { uintptr_t addr, p_vaddr, p_memsz; }; @@ -222,7 +228,9 @@ void *laddr(const struct dso *p, size_t v); #endif void *addr2dso(size_t a); -struct symdef find_sym2(struct dso *dso, struct verinfo *verinfo, int need_def, int use_deps, ns_t *ns); +struct sym_info_pair gnu_hash(const char *s0); +struct symdef find_sym_impl( + struct dso *dso, struct verinfo *verinfo, struct sym_info_pair s_info_p, int need_def, ns_t *ns); hidden void *__dlsym(void *restrict, const char *restrict, void *restrict); hidden void *__dlvsym(void *restrict, const char *restrict, const char *restrict, void *restrict);