|
12 | 12 | #define __UNWINDCURSOR_HPP__
|
13 | 13 |
|
14 | 14 | #include "cet_unwind.h"
|
| 15 | +#include <errno.h> |
| 16 | +#include <signal.h> |
15 | 17 | #include <stdint.h>
|
16 | 18 | #include <stdio.h>
|
17 | 19 | #include <stdlib.h>
|
@@ -990,6 +992,7 @@ class UnwindCursor : public AbstractUnwindCursor{
|
990 | 992 | R dummy;
|
991 | 993 | return stepThroughSigReturn(dummy);
|
992 | 994 | }
|
| 995 | + bool isReadableAddr(const pint_t addr) const; |
993 | 996 | #if defined(_LIBUNWIND_TARGET_AARCH64)
|
994 | 997 | bool setInfoForSigReturn(Registers_arm64 &);
|
995 | 998 | int stepThroughSigReturn(Registers_arm64 &);
|
@@ -2700,20 +2703,12 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_arm64 &) {
|
2700 | 2703 | // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S
|
2701 | 2704 | const pint_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP));
|
2702 | 2705 | // The PC might contain an invalid address if the unwind info is bad, so
|
2703 |
| - // directly accessing it could cause a segfault. Use process_vm_readv to read |
2704 |
| - // the memory safely instead. process_vm_readv was added in Linux 3.2, and |
2705 |
| - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to |
2706 |
| - // be present. Unfortunately, there are Linux AArch64 environments where the |
2707 |
| - // libc wrapper for the syscall might not be present (e.g. Android 5), so call |
2708 |
| - // the syscall directly instead. |
2709 |
| - uint32_t instructions[2]; |
2710 |
| - struct iovec local_iov = {&instructions, sizeof instructions}; |
2711 |
| - struct iovec remote_iov = {reinterpret_cast<void *>(pc), sizeof instructions}; |
2712 |
| - long bytesRead = |
2713 |
| - syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0); |
| 2706 | + // directly accessing it could cause a SIGSEGV. |
| 2707 | + if (!isReadableAddr(pc)) |
| 2708 | + return false; |
| 2709 | + auto *instructions = reinterpret_cast<const uint32_t *>(pc); |
2714 | 2710 | // Look for instructions: mov x8, #0x8b; svc #0x0
|
2715 |
| - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || |
2716 |
| - instructions[1] != 0xd4000001) |
| 2711 | + if (instructions[0] != 0xd2801168 || instructions[1] != 0xd4000001) |
2717 | 2712 | return false;
|
2718 | 2713 |
|
2719 | 2714 | _info = {};
|
@@ -2762,18 +2757,17 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_arm64 &) {
|
2762 | 2757 | template <typename A, typename R>
|
2763 | 2758 | bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_riscv &) {
|
2764 | 2759 | const pint_t pc = static_cast<pint_t>(getReg(UNW_REG_IP));
|
2765 |
| - uint32_t instructions[2]; |
2766 |
| - struct iovec local_iov = {&instructions, sizeof instructions}; |
2767 |
| - struct iovec remote_iov = {reinterpret_cast<void *>(pc), sizeof instructions}; |
2768 |
| - long bytesRead = |
2769 |
| - syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0); |
| 2760 | + // The PC might contain an invalid address if the unwind info is bad, so |
| 2761 | + // directly accessing it could cause a SIGSEGV. |
| 2762 | + if (!isReadableAddr(pc)) |
| 2763 | + return false; |
| 2764 | + const auto *instructions = reinterpret_cast<const uint32_t *>(pc); |
2770 | 2765 | // Look for the two instructions used in the sigreturn trampoline
|
2771 | 2766 | // __vdso_rt_sigreturn:
|
2772 | 2767 | //
|
2773 | 2768 | // 0x08b00893 li a7,0x8b
|
2774 | 2769 | // 0x00000073 ecall
|
2775 |
| - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || |
2776 |
| - instructions[1] != 0x00000073) |
| 2770 | + if (instructions[0] != 0x08b00893 || instructions[1] != 0x00000073) |
2777 | 2771 | return false;
|
2778 | 2772 |
|
2779 | 2773 | _info = {};
|
@@ -2822,13 +2816,11 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
|
2822 | 2816 | // onto the stack.
|
2823 | 2817 | const pint_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP));
|
2824 | 2818 | // The PC might contain an invalid address if the unwind info is bad, so
|
2825 |
| - // directly accessing it could cause a segfault. Use process_vm_readv to |
2826 |
| - // read the memory safely instead. |
2827 |
| - uint16_t inst; |
2828 |
| - struct iovec local_iov = {&inst, sizeof inst}; |
2829 |
| - struct iovec remote_iov = {reinterpret_cast<void *>(pc), sizeof inst}; |
2830 |
| - long bytesRead = process_vm_readv(getpid(), &local_iov, 1, &remote_iov, 1, 0); |
2831 |
| - if (bytesRead == sizeof inst && (inst == 0x0a77 || inst == 0x0aad)) { |
| 2819 | + // directly accessing it could cause a SIGSEGV. |
| 2820 | + if (!isReadableAddr(pc)) |
| 2821 | + return false; |
| 2822 | + const auto inst = *reinterpret_cast<const uint16_t *>(pc); |
| 2823 | + if (inst == 0x0a77 || inst == 0x0aad) { |
2832 | 2824 | _info = {};
|
2833 | 2825 | _info.start_ip = pc;
|
2834 | 2826 | _info.end_ip = pc + 2;
|
@@ -2974,6 +2966,37 @@ bool UnwindCursor<A, R>::getFunctionName(char *buf, size_t bufLen,
|
2974 | 2966 | buf, bufLen, offset);
|
2975 | 2967 | }
|
2976 | 2968 |
|
| 2969 | +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) |
| 2970 | +template <typename A, typename R> |
| 2971 | +bool UnwindCursor<A, R>::isReadableAddr(const pint_t addr) const { |
| 2972 | + // We use SYS_rt_sigprocmask, inspired by Abseil's AddressIsReadable. |
| 2973 | + |
| 2974 | + const auto sigsetAddr = reinterpret_cast<sigset_t *>(addr); |
| 2975 | + // We have to check that addr is nullptr because sigprocmask allows that |
| 2976 | + // as an argument without failure. |
| 2977 | + if (!sigsetAddr) |
| 2978 | + return false; |
| 2979 | + const auto saveErrno = errno; |
| 2980 | + // We MUST use a raw syscall here, as wrappers may try to access |
| 2981 | + // sigsetAddr which may cause a SIGSEGV. A raw syscall however is |
| 2982 | + // safe. Additionally, we need to pass the kernel_sigset_size, which is |
| 2983 | + // different from libc sizeof(sigset_t). For the majority of architectures, |
| 2984 | + // it's 64 bits (_NSIG), and libc NSIG is _NSIG + 1. |
| 2985 | + const auto kernelSigsetSize = NSIG / 8; |
| 2986 | + [[maybe_unused]] const int Result = syscall( |
| 2987 | + SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, nullptr, kernelSigsetSize); |
| 2988 | + // Because our "how" is invalid, this syscall should always fail, and our |
| 2989 | + // errno should always be EINVAL or an EFAULT. This relies on the Linux |
| 2990 | + // kernel to check copy_from_user before checking if the "how" argument is |
| 2991 | + // invalid. |
| 2992 | + assert(Result == -1); |
| 2993 | + assert(errno == EFAULT || errno == EINVAL); |
| 2994 | + const auto readable = errno != EFAULT; |
| 2995 | + errno = saveErrno; |
| 2996 | + return readable; |
| 2997 | +} |
| 2998 | +#endif |
| 2999 | + |
2977 | 3000 | #if defined(_LIBUNWIND_USE_CET)
|
2978 | 3001 | extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) {
|
2979 | 3002 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
|
0 commit comments