diff --git a/backport-Port-memory-address-checks-from-x86_64-to-Aarch64.patch b/backport-Port-memory-address-checks-from-x86_64-to-Aarch64.patch new file mode 100644 index 0000000000000000000000000000000000000000..91001048c8a59274c93da70ced6e62fe858beef8 --- /dev/null +++ b/backport-Port-memory-address-checks-from-x86_64-to-Aarch64.patch @@ -0,0 +1,466 @@ +From f8327f50be0413357816c2a41348455af476b9e5 Mon Sep 17 00:00:00 2001 +From: Mikhail Durnev +Date: Fri, 5 Mar 2021 21:41:31 +1000 +Subject: [PATCH] Port memory address checks from x86/x86_64 to Aarch64 + +Signed-off-by: Mikhail Durnev +--- + include/tdep-aarch64/libunwind_i.h | 14 +- + src/aarch64/Gglobal.c | 2 + + src/aarch64/Ginit.c | 248 ++++++++++++++++++++++++++++- + src/aarch64/Ginit_local.c | 4 +- + src/aarch64/Ginit_remote.c | 11 +- + src/aarch64/Gresume.c | 2 +- + src/aarch64/Gstep.c | 20 ++- + 7 files changed, 291 insertions(+), 10 deletions(-) + +diff --git a/include/tdep-aarch64/libunwind_i.h b/include/tdep-aarch64/libunwind_i.h +index fc4416069..d96833a21 100644 +--- a/include/tdep-aarch64/libunwind_i.h ++++ b/include/tdep-aarch64/libunwind_i.h +@@ -105,8 +105,16 @@ struct cursor + unw_word_t sigcontext_sp; + unw_word_t sigcontext_pc; + int validate; ++ ucontext_t *uc; + }; + ++static inline ucontext_t * ++dwarf_get_uc(const struct dwarf_cursor *cursor) ++{ ++ const struct cursor *c = (struct cursor *) cursor->as_arg; ++ return c->uc; ++} ++ + #define DWARF_GET_LOC(l) ((l).val) + + #ifdef UNW_LOCAL_ONLY +@@ -115,10 +123,10 @@ struct cursor + # define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) }) + # define DWARF_IS_REG_LOC(l) 0 + # define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) \ +- tdep_uc_addr((c)->as_arg, (r)), 0)) ++ tdep_uc_addr(dwarf_get_uc(c), (r)), 0)) + # define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) + # define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \ +- tdep_uc_addr((c)->as_arg, (r)), 0)) ++ tdep_uc_addr(dwarf_get_uc(c), (r)), 0)) + + static inline int + dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) +@@ -262,6 +270,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) + + #define tdep_getcontext_trace UNW_ARCH_OBJ(getcontext_trace) + #define tdep_init_done UNW_OBJ(init_done) ++#define tdep_init_mem_validate UNW_OBJ(init_mem_validate) + #define tdep_init UNW_OBJ(init) + /* Platforms that support UNW_INFO_FORMAT_TABLE need to define + tdep_search_unwind_table. */ +@@ -300,6 +309,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) + extern int tdep_init_done; + + extern void tdep_init (void); ++extern void tdep_init_mem_validate (void); + extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, + unw_dyn_info_t *di, unw_proc_info_t *pi, + int need_unwind_info, void *arg); +diff --git a/src/aarch64/Gglobal.c b/src/aarch64/Gglobal.c +index 854b54914..2987f2aff 100644 +--- a/src/aarch64/Gglobal.c ++++ b/src/aarch64/Gglobal.c +@@ -47,6 +47,8 @@ tdep_init (void) + + dwarf_init (); + ++ tdep_init_mem_validate (); ++ + #ifndef UNW_REMOTE_ONLY + aarch64_local_addr_space_init (); + #endif +diff --git a/src/aarch64/Ginit.c b/src/aarch64/Ginit.c +index a3b933b6d..2b08feb36 100644 +--- a/src/aarch64/Ginit.c ++++ b/src/aarch64/Ginit.c +@@ -24,8 +24,11 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + ++#include + #include + #include ++#include ++#include + + #include "unwind_i.h" + +@@ -81,17 +84,256 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, + return 0; + } + ++#define PAGE_SIZE 4096 ++#define PAGE_START(a) ((a) & ~(PAGE_SIZE-1)) ++ ++static int mem_validate_pipe[2] = {-1, -1}; ++ ++#ifdef HAVE_PIPE2 ++static inline void ++do_pipe2 (int pipefd[2]) ++{ ++ pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); ++} ++#else ++static inline void ++set_pipe_flags (int fd) ++{ ++ int fd_flags = fcntl (fd, F_GETFD, 0); ++ int status_flags = fcntl (fd, F_GETFL, 0); ++ ++ fd_flags |= FD_CLOEXEC; ++ fcntl (fd, F_SETFD, fd_flags); ++ ++ status_flags |= O_NONBLOCK; ++ fcntl (fd, F_SETFL, status_flags); ++} ++ ++static inline void ++do_pipe2 (int pipefd[2]) ++{ ++ pipe (pipefd); ++ set_pipe_flags(pipefd[0]); ++ set_pipe_flags(pipefd[1]); ++} ++#endif ++ ++static inline void ++open_pipe (void) ++{ ++ if (mem_validate_pipe[0] != -1) ++ close (mem_validate_pipe[0]); ++ if (mem_validate_pipe[1] != -1) ++ close (mem_validate_pipe[1]); ++ ++ do_pipe2 (mem_validate_pipe); ++} ++ ++ALWAYS_INLINE ++static int ++write_validate (void *addr) ++{ ++ int ret = -1; ++ ssize_t bytes = 0; ++ ++ do ++ { ++ char buf; ++ bytes = read (mem_validate_pipe[0], &buf, 1); ++ } ++ while ( errno == EINTR ); ++ ++ int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK); ++ if (!valid_read) ++ { ++ // re-open closed pipe ++ open_pipe (); ++ } ++ ++ do ++ { ++ ret = write (mem_validate_pipe[1], addr, 1); ++ } ++ while ( errno == EINTR ); ++ ++ return ret; ++} ++ ++static int (*mem_validate_func) (void *addr, size_t len); ++static int msync_validate (void *addr, size_t len) ++{ ++ if (msync (addr, len, MS_ASYNC) != 0) ++ { ++ return -1; ++ } ++ ++ return write_validate (addr); ++} ++ ++#ifdef HAVE_MINCORE ++static int mincore_validate (void *addr, size_t len) ++{ ++ unsigned char mvec[2]; /* Unaligned access may cross page boundary */ ++ ++ /* mincore could fail with EAGAIN but we conservatively return -1 ++ instead of looping. */ ++ if (mincore (addr, len, (unsigned char *)mvec) != 0) ++ { ++ return -1; ++ } ++ ++ return write_validate (addr); ++} ++#endif ++ ++/* Initialise memory validation method. On linux kernels <2.6.21, ++ mincore() returns incorrect value for MAP_PRIVATE mappings, ++ such as stacks. If mincore() was available at compile time, ++ check if we can actually use it. If not, use msync() instead. */ ++HIDDEN void ++tdep_init_mem_validate (void) ++{ ++ open_pipe (); ++ ++#ifdef HAVE_MINCORE ++ unsigned char present = 1; ++ unw_word_t addr = PAGE_START((unw_word_t)&present); ++ unsigned char mvec[1]; ++ int ret; ++ while ((ret = mincore ((void*)addr, PAGE_SIZE, (unsigned char *)mvec)) == -1 && ++ errno == EAGAIN) {} ++ if (ret == 0) ++ { ++ Debug(1, "using mincore to validate memory\n"); ++ mem_validate_func = mincore_validate; ++ } ++ else ++#endif ++ { ++ Debug(1, "using msync to validate memory\n"); ++ mem_validate_func = msync_validate; ++ } ++} ++ ++/* Cache of already validated addresses */ ++#define NLGA 4 ++#if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD ++// thread-local variant ++static _Thread_local unw_word_t last_good_addr[NLGA]; ++static _Thread_local int lga_victim; ++ ++static int ++is_cached_valid_mem(unw_word_t addr) ++{ ++ int i; ++ for (i = 0; i < NLGA; i++) ++ { ++ if (addr == last_good_addr[i]) ++ return 1; ++ } ++ return 0; ++} ++ ++static void ++cache_valid_mem(unw_word_t addr) ++{ ++ int i, victim; ++ victim = lga_victim; ++ for (i = 0; i < NLGA; i++) { ++ if (last_good_addr[victim] == 0) { ++ last_good_addr[victim] = addr; ++ return; ++ } ++ victim = (victim + 1) % NLGA; ++ } ++ ++ /* All slots full. Evict the victim. */ ++ last_good_addr[victim] = addr; ++ victim = (victim + 1) % NLGA; ++ lga_victim = victim; ++} ++ ++#else ++// global, thread safe variant ++static _Atomic unw_word_t last_good_addr[NLGA]; ++static _Atomic int lga_victim; ++ ++static int ++is_cached_valid_mem(unw_word_t addr) ++{ ++ int i; ++ for (i = 0; i < NLGA; i++) ++ { ++ if (addr == atomic_load(&last_good_addr[i])) ++ return 1; ++ } ++ return 0; ++} ++ ++static void ++cache_valid_mem(unw_word_t addr) ++{ ++ int i, victim; ++ victim = atomic_load(&lga_victim); ++ unw_word_t zero = 0; ++ for (i = 0; i < NLGA; i++) { ++ if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) { ++ return; ++ } ++ victim = (victim + 1) % NLGA; ++ } ++ ++ /* All slots full. Evict the victim. */ ++ atomic_store(&last_good_addr[victim], addr); ++ victim = (victim + 1) % NLGA; ++ atomic_store(&lga_victim, victim); ++} ++#endif ++ ++static int ++validate_mem (unw_word_t addr) ++{ ++ size_t len; ++ ++ if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr)) ++ len = PAGE_SIZE; ++ else ++ len = PAGE_SIZE * 2; ++ ++ addr = PAGE_START(addr); ++ ++ if (addr == 0) ++ return -1; ++ ++ if (is_cached_valid_mem(addr)) ++ return 0; ++ ++ if (mem_validate_func ((void *) addr, len) == -1) ++ return -1; ++ ++ cache_valid_mem(addr); ++ ++ return 0; ++} ++ + static int + access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, + void *arg) + { +- if (write) ++ if (unlikely (write)) + { + Debug (16, "mem[%lx] <- %lx\n", addr, *val); + *(unw_word_t *) addr = *val; + } + else + { ++ /* validate address */ ++ const struct cursor *c = (const struct cursor *)arg; ++ if (likely (c != NULL) && unlikely (c->validate) ++ && unlikely (validate_mem (addr))) { ++ Debug (16, "mem[%016lx] -> invalid\n", addr); ++ return -1; ++ } + *val = *(unw_word_t *) addr; + Debug (16, "mem[%lx] -> %lx\n", addr, *val); + } +@@ -103,7 +345,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, + void *arg) + { + unw_word_t *addr; +- unw_tdep_context_t *uc = arg; ++ unw_tdep_context_t *uc = ((struct cursor *)arg)->uc; + + if (unw_is_fpreg (reg)) + goto badreg; +@@ -132,7 +374,7 @@ static int + access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, + int write, void *arg) + { +- unw_tdep_context_t *uc = arg; ++ unw_tdep_context_t *uc = ((struct cursor *)arg)->uc; + unw_fpreg_t *addr; + + if (!unw_is_fpreg (reg)) +diff --git a/src/aarch64/Ginit_local.c b/src/aarch64/Ginit_local.c +index 3f0080ade..4a055fb09 100644 +--- a/src/aarch64/Ginit_local.c ++++ b/src/aarch64/Ginit_local.c +@@ -47,7 +47,9 @@ unw_init_local_common (unw_cursor_t *cursor, unw_context_t *uc, unsigned use_pre + Debug (1, "(cursor=%p)\n", c); + + c->dwarf.as = unw_local_addr_space; +- c->dwarf.as_arg = uc; ++ c->dwarf.as_arg = c; ++ c->uc = uc; ++ c->validate = 0; + + return common_init (c, use_prev_instr); + } +diff --git a/src/aarch64/Ginit_remote.c b/src/aarch64/Ginit_remote.c +index 26d11ba94..e300173ca 100644 +--- a/src/aarch64/Ginit_remote.c ++++ b/src/aarch64/Ginit_remote.c +@@ -39,7 +39,16 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) + Debug (1, "(cursor=%p)\n", c); + + c->dwarf.as = as; +- c->dwarf.as_arg = as_arg; ++ if (as == unw_local_addr_space) ++ { ++ c->dwarf.as_arg = c; ++ c->uc = as_arg; ++ } ++ else ++ { ++ c->dwarf.as_arg = as_arg; ++ c->uc = 0; ++ } + return common_init (c, 0); + #endif /* !UNW_LOCAL_ONLY */ + } +diff --git a/src/aarch64/Gresume.c b/src/aarch64/Gresume.c +index 2cc161360..445bac70f 100644 +--- a/src/aarch64/Gresume.c ++++ b/src/aarch64/Gresume.c +@@ -34,7 +34,7 @@ aarch64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) + { + #ifdef __linux__ + struct cursor *c = (struct cursor *) cursor; +- unw_tdep_context_t *uc = c->dwarf.as_arg; ++ unw_tdep_context_t *uc = c->uc; + + if (c->sigcontext_format == AARCH64_SCF_NONE) + { +diff --git a/src/aarch64/Gstep.c b/src/aarch64/Gstep.c +index fdf64a73f..92e2a6663 100644 +--- a/src/aarch64/Gstep.c ++++ b/src/aarch64/Gstep.c +@@ -70,7 +70,7 @@ aarch64_handle_signal_frame (unw_cursor_t *cursor) + c->sigcontext_sp = c->dwarf.cfa; + c->sigcontext_pc = c->dwarf.ip; + +- if (ret) ++ if (ret > 0) + { + c->sigcontext_format = AARCH64_SCF_LINUX_RT_SIGFRAME; + sc_addr = sp_addr + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF; +@@ -134,14 +134,30 @@ int + unw_step (unw_cursor_t *cursor) + { + struct cursor *c = (struct cursor *) cursor; ++ int validate = c->validate; + int ret; + + Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx))\n", + c, c->dwarf.ip, c->dwarf.cfa); + ++ /* Validate all addresses before dereferencing. */ ++ c->validate = 1; ++ + /* Check if this is a signal frame. */ +- if (unw_is_signal_frame (cursor) > 0) ++ ret = unw_is_signal_frame (cursor); ++ if (ret > 0) + return aarch64_handle_signal_frame (cursor); ++ else if (unlikely (ret < 0)) ++ { ++ /* IP points to non-mapped memory. */ ++ /* This is probably SIGBUS. */ ++ /* Try to load LR in IP to recover. */ ++ Debug(1, "Invalid address found in the call stack: 0x%lx\n", c->dwarf.ip); ++ dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X30], &c->dwarf.ip); ++ } ++ ++ /* Restore default memory validation state */ ++ c->validate = validate; + + ret = dwarf_step (&c->dwarf); + Debug(1, "dwarf_step()=%d\n", ret); diff --git a/backport-aarch64-unw_step-validates-address-before-calling-dwarf_get.patch b/backport-aarch64-unw_step-validates-address-before-calling-dwarf_get.patch new file mode 100644 index 0000000000000000000000000000000000000000..7be6a50be562faee61324d6b791f7c26ed0e9381 --- /dev/null +++ b/backport-aarch64-unw_step-validates-address-before-calling-dwarf_get.patch @@ -0,0 +1,48 @@ +From 8a0e2fa6579085dc8f51b2bdd90d8b00a2e7d6ab Mon Sep 17 00:00:00 2001 +From: he7850 +Date: Wed, 3 May 2023 15:18:27 +0800 +Subject: [PATCH] aarch64: unw_step() validates address before calling + dwarf_get + +Signed-off-by: he7850 +--- + src/aarch64/Gstep.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/src/aarch64/Gstep.c b/src/aarch64/Gstep.c +index f4ef369d3..a2ed9bc35 100644 +--- a/src/aarch64/Gstep.c ++++ b/src/aarch64/Gstep.c +@@ -156,18 +156,28 @@ unw_step (unw_cursor_t *cursor) + dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X30], &c->dwarf.ip); + } + +- /* Restore default memory validation state */ +- c->validate = validate; +- + ret = dwarf_step (&c->dwarf); + Debug(1, "dwarf_step()=%d\n", ret); + ++ /* Restore default memory validation state */ ++ c->validate = validate; ++ + if (unlikely (ret == -UNW_ESTOPUNWIND)) + return ret; + + if (unlikely (ret < 0)) + { + /* DWARF failed. */ ++ ++ /* ++ * We could get here because of missing/bad unwind information. ++ * Validate all addresses before dereferencing. ++ */ ++ if (c->dwarf.as == unw_local_addr_space) ++ { ++ c->validate = 1; ++ } ++ + if (is_plt_entry (&c->dwarf)) + { + Debug (2, "found plt entry\n"); + diff --git a/libunwind.spec b/libunwind.spec index c8d2abc4e7823bfe9ec5f23b18ca696556c32d50..8f414f7068868cdcec450b1e4f671909671a4c3e 100644 --- a/libunwind.spec +++ b/libunwind.spec @@ -1,7 +1,7 @@ Name: libunwind Epoch: 2 Version: 1.5.0 -Release: 6 +Release: 7 Summary: Libunwind provides a C ABI to determine the call-chain of a program License: BSD URL: http://savannah.nongnu.org/projects/libunwind @@ -11,6 +11,8 @@ Patch2: backport-check-namespace.sh-adjust-aarch64-symbols.patch Patch3: backport-Ltest-mem-validate-Disable-inlining-for-consume_and_.patch Patch4: backport-tests-run-coredump-unwind-Skip-test-if-no-coredump-h.patch Patch5: libunwind-Add-loongarch64-support.patch +Patch6: backport-Port-memory-address-checks-from-x86_64-to-Aarch64.patch +Patch7: backport-aarch64-unw_step-validates-address-before-calling-dwarf_get.patch ExclusiveArch: aarch64 %{ix86} x86_64 sw_64 loongarch64 @@ -90,6 +92,9 @@ make check || true %endif %changelog +* Thu Jun 01 2023 wangjiang - 2:1.5.0-7 +- fix failed testcase + * Mon Mar 27 2023 Youling Tang - 2:1.5.0-6 - Add loongarch64 support