[libunwind] [libunwind] Move errno.h and signal.h includes under the block where they're needed (PR #78054)
ajordanr-google wrote: Done! Thanks for the heads up on the email. I'm not too familiar with the GitHub workflow still. Also thanks for this fix PR. https://github.com/llvm/llvm-project/pull/78054 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[lldb] [llvm] [libcxx] [flang] [libc] [clang-tools-extra] [clang] [compiler-rt] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 9d4665cf1ddda98129af2f6ff575989cec49af6d Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 01/13] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
ajordanr-google wrote: All CI runs are failing on unrelated tests... can we re-run the CI again in a week or so? https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
ajordanr-google wrote: Rebased to include 47413bb2760e63a3302871ea770d6c0f5a742036 (a merge commit seemed silly?). No other changes have been made. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 9d4665cf1ddda98129af2f6ff575989cec49af6d Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 01/13] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[llvm] [clang] [libunwind] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
ajordanr-google wrote: Hmm. It looks like `[[maybe_unused]]` is still disallowed... https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [clang-tools-extra] [clang] [llvm] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
ajordanr-google wrote: I don't have Commit Access (I so rarely actually commit to upstream), and I don't think I have yet the "track record of submitting high quality patches" :) Can someone else merge this once the checks are green? https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [clang-tools-extra] [clang] [llvm] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,37 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017). + + const auto sigsetAddr = reinterpret_cast(addr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + // We MUST use a raw syscall here, as wrappers may try to access + // sigsetAddr which may cause a SIGSEGV. A raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). For the majority of architectures, + // it's 64 bits (_NSIG), and libc NSIG is _NSIG + 1. + const auto kernelSigsetSize = NSIG / 8; + const int Result = syscall(SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, + nullptr, kernelSigsetSize); + (void)Result; + // Because our "how" is invalid, this syscall should always fail, and our + // errno should always be EINVAL or an EFAULT. EFAULT is not guaranteed + // by the POSIX standard. Additionally, this relies on the Linux kernel + // to check copy_from_user before checking if the "how" argument is + // invalid. + assert(Result == -1); + assert(errno == EFAULT || errno == EINVAL); + return errno != EFAULT; ajordanr-google wrote: We should probably save and restore, as we do this above in `getInfoFromTBTable`. Good point. Done. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libunwind] [llvm] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,37 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017). + + const auto sigsetAddr = reinterpret_cast(addr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + // We MUST use a raw syscall here, as wrappers may try to access + // sigsetAddr which may cause a SIGSEGV. A raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). For the majority of architectures, + // it's 64 bits (_NSIG), and libc NSIG is _NSIG + 1. + const auto kernelSigsetSize = NSIG / 8; + const int Result = syscall(SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, + nullptr, kernelSigsetSize); + (void)Result; + // Because our "how" is invalid, this syscall should always fail, and our + // errno should always be EINVAL or an EFAULT. EFAULT is not guaranteed + // by the POSIX standard. Additionally, this relies on the Linux kernel ajordanr-google wrote: Done. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[llvm] [libunwind] [clang] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,37 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017). ajordanr-google wrote: Done https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [clang-tools-extra] [clang] [llvm] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 01/13] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[llvm] [clang-tools-extra] [clang] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google edited https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[llvm] [clang-tools-extra] [clang] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google edited https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [clang] [llvm] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google edited https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [clang] [llvm] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017), and provided under + // the Apache License 2.0. + + // Align to 8-bytes. + const auto alignedAddr = addr & ~pint_t{7}; + const auto sigsetAddr = reinterpret_cast(alignedAddr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + + // We MUST use the raw sigprocmask syscall here, as wrappers may try to + // access sigsetAddr which may cause a SIGSEGV. The raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). Some archs have sigset_t + // defined as unsigned long, so let's use that. + const auto approxKernelSigsetSize = sizeof(unsigned long); ajordanr-google wrote: Gotcha. I ended up using `NSIG/8` since it rounds down anyways, and I checked the glibc code and it has 128 instead of 127, and I think we would actually want 128/8 instead if I'm reading the mips case right. It's not that important if we don't care about mips. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [clang] [llvm] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2817,7 +2817,7 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so // directly accessing it could cause a SIGSEGV. - if (!isReadableAddr(pc) || !isReadableAddr(pc + 2)) + if (!isReadableAddr(pc)) ajordanr-google wrote: I think that's probably fine, as long as we aren't hitting a SIGSEGV. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [clang] [llvm] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -26,7 +26,7 @@ __attribute__((naked)) void bad_unwind_info() { #if defined(__aarch64__) __asm__("// not using 0 because unwinder was already resilient to that\n" - "mov x8, #4\n" + "mov x8, #12\n" ajordanr-google wrote: Undid this with the removal of the alignment code. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [llvm] [clang-tools-extra] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 01/12] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[clang-tools-extra] [clang] [llvm] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 01/11] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[clang] [llvm] [clang-tools-extra] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017), and provided under + // the Apache License 2.0. + + // Align to 8-bytes. + const auto alignedAddr = addr & ~pint_t{7}; + const auto sigsetAddr = reinterpret_cast(alignedAddr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + + // We MUST use the raw sigprocmask syscall here, as wrappers may try to + // access sigsetAddr which may cause a SIGSEGV. The raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). Some archs have sigset_t + // defined as unsigned long, so let's use that. + const auto approxKernelSigsetSize = sizeof(unsigned long); ajordanr-google wrote: How about `(NSIG-1)/8`? https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html. Just let libc handle it. It should work cleanly for everything that's not MIPS. I don't have a MIPS system to test if this works for it. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[llvm] [clang] [clang-tools-extra] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017), and provided under + // the Apache License 2.0. + + // Align to 8-bytes. + const auto alignedAddr = addr & ~pint_t{7}; + const auto sigsetAddr = reinterpret_cast(alignedAddr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + + // We MUST use the raw sigprocmask syscall here, as wrappers may try to + // access sigsetAddr which may cause a SIGSEGV. The raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). Some archs have sigset_t + // defined as unsigned long, so let's use that. + const auto approxKernelSigsetSize = sizeof(unsigned long); ajordanr-google wrote: Looking at linux-6.6.7 source code, we have some minor variety on `sigset_t` size... TL;DR: They are almost all `_NSIG` bits long, which is 64 bits or, in the case of MIPS, maybe 128? There are two exceptions to this, where they are not a struct of size `_NSIG/_NSIG_BPW * sizeof(unsigned long)` but rather just `unsigned long`. I assume `_NSIG_BPW == sizeof(long) == sizeof(unsigned long)`, which seems reasonable given the comments (and not `_NSIG_BPW == sizeof(int)`). --- `sigset_t` is almost always defined as: ``` typedef struct { unsigned long sig[_NSIG_WORDS]; } sigset_t; ``` Where: ``` arch/parisc/include/asm/signal.h-7-#define _NSIG64 arch/parisc/include/asm/signal.h-8-/* bits-per-word, where word apparently means 'long' not 'int' */ arch/parisc/include/asm/signal.h-9-#define _NSIG_BPWBITS_PER_LONG arch/parisc/include/asm/signal.h:10:#define _NSIG_WORDS (_NSIG / _NSIG_BPW) // or arch/xtensa/include/uapi/asm/signal.h-18-#define _NSIG 64 arch/xtensa/include/uapi/asm/signal.h-19-#define _NSIG_BPW 32 arch/xtensa/include/uapi/asm/signal.h:20:#define _NSIG_WORDS(_NSIG / _NSIG_BPW) // or arch/ia64/include/asm/signal.h-15-#define _NSIG 64 arch/ia64/include/asm/signal.h-16-#define _NSIG_BPW 64 arch/ia64/include/asm/signal.h:17:#define _NSIG_WORDS (_NSIG / _NSIG_BPW) // or arch/alpha/include/asm/signal.h-10-#define _NSIG64 arch/alpha/include/asm/signal.h-11-#define _NSIG_BPW64 arch/alpha/include/asm/signal.h:12:#define _NSIG_WORDS (_NSIG / _NSIG_BPW) // or arch/arm/include/asm/signal.h-10-#define _NSIG 64 arch/arm/include/asm/signal.h-11-#define _NSIG_BPW 32 arch/arm/include/asm/signal.h:12:#define _NSIG_WORDS(_NSIG / _NSIG_BPW) // or arch/s390/include/asm/signal.h-12-/* Most things should be clean enough to redefine this at will, if care arch/s390/include/asm/signal.h-13- is taken to make libc match. */ arch/s390/include/asm/signal.h-14-#include arch/s390/include/asm/signal.h-15-#define _NSIG _SIGCONTEXT_NSIG arch/s390/include/asm/signal.h-16-#define _NSIG_BPW _SIGCONTEXT_NSIG_BPW arch/s390/include/asm/signal.h:17:#define _NSIG_WORDS _SIGCONTEXT_NSIG_WORDS arch/s390/include/uapi/asm/sigcontext.h:30:/* Has to be at least _NSIG_WORDS from asm/signal.h */ arch/s390/include/uapi/asm/sigcontext.h-31-#define _SIGCONTEXT_NSIG 64 arch/s390/include/uapi/asm/sigcontext.h-32-#define _SIGCONTEXT_NSIG_BPW 64 // or arch/x86/include/asm/signal.h-11-#define _NSIG 64 arch/x86/include/asm/signal.h-12- arch/x86/include/asm/signal.h-13-#ifdef __i386__ arch/x86/include/asm/signal.h-14-# define _NSIG_BPW 32 arch/x86/include/asm/signal.h-15-#else arch/x86/include/asm/signal.h-16-# define _NSIG_BPW 64 arch/x86/include/asm/signal.h-17-#endif arch/x86/include/asm/signal.h-18- arch/x86/include/asm/signal.h:19:#define _NSIG_WORDS(_NSIG / _NSIG_BPW) // or arch/m68k/include/asm/signal.h-10-#define _NSIG 64 arch/m68k/include/asm/signal.h-11-#define _NSIG_BPW 32 arch/m68k/include/asm/signal.h:12:#define _NSIG_WORDS (_NSIG / _NSIG_BPW) // or (quite weirdly) arch/mips/include/uapi/asm/signal.h-15-#define _NSIG128 arch/mips/include/uapi/asm/signal.h-16-#define _NSIG_BPW (sizeof(unsigned long) * 8) arch/mips/include/uapi/asm/signal.h:17:#define _NSIG_WORDS (_NSIG / _NSIG_BPW) // or arch/sparc/include/uapi/asm/signal.h-81-#define __NEW_NSIG 64 arch/sparc/include/uapi/asm/signal.h-82-#ifdef __arch64__ arch/sparc/include/uapi/asm/signal.h-83-#define _NSIG_BPW 64 arch/sparc/include/uapi/asm/signal.h-84-#else arch/sparc/include/uapi/asm/signal.h-85-#define _NSIG_BPW 32 arch/sparc/include/uapi/asm/signal.h-86-#endif
[libunwind] [clang-tools-extra] [llvm] [clang] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017), and provided under + // the Apache License 2.0. + + // Align to 8-bytes. + const auto alignedAddr = addr & ~pint_t{7}; + const auto sigsetAddr = reinterpret_cast(alignedAddr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + + // We MUST use the raw sigprocmask syscall here, as wrappers may try to + // access sigsetAddr which may cause a SIGSEGV. The raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). Some archs have sigset_t + // defined as unsigned long, so let's use that. + const auto approxKernelSigsetSize = sizeof(unsigned long); + [[maybe_unused]] const int Result = + syscall(SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, sigsetAddr, + approxKernelSigsetSize); + // Because our "how" is invalid, this syscall should always fail, and our ajordanr-google wrote: Thanks, done. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [llvm] [libunwind] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017), and provided under + // the Apache License 2.0. + + // Align to 8-bytes. + const auto alignedAddr = addr & ~pint_t{7}; + const auto sigsetAddr = reinterpret_cast(alignedAddr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + + // We MUST use the raw sigprocmask syscall here, as wrappers may try to + // access sigsetAddr which may cause a SIGSEGV. The raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). Some archs have sigset_t + // defined as unsigned long, so let's use that. + const auto approxKernelSigsetSize = sizeof(unsigned long); + [[maybe_unused]] const int Result = + syscall(SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, sigsetAddr, ajordanr-google wrote: Done https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [llvm] [libunwind] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2762,18 +2757,17 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); - uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a SIGSEGV. + if (!isReadableAddr(pc) || !isReadableAddr(pc + 4)) ajordanr-google wrote: Given Paul's offline comments, I've removed the alignment as it seems to be a bug in QEMU not Linux. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[llvm] [clang] [clang-tools-extra] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017), and provided under + // the Apache License 2.0. + + // Align to 8-bytes. + const auto alignedAddr = addr & ~pint_t{7}; + const auto sigsetAddr = reinterpret_cast(alignedAddr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + + // We MUST use the raw sigprocmask syscall here, as wrappers may try to + // access sigsetAddr which may cause a SIGSEGV. The raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). Some archs have sigset_t + // defined as unsigned long, so let's use that. + const auto approxKernelSigsetSize = sizeof(unsigned long); + [[maybe_unused]] const int Result = + syscall(SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, sigsetAddr, + approxKernelSigsetSize); + // Because our "how" is invalid, this syscall should always fail, and our + // errno should always be EINVAL or an EFAULT. EFAULT is not guaranteed + // by the POSIX standard, so this is (for now) Linux specific. ajordanr-google wrote: Removed, thanks. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[llvm] [clang] [libunwind] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2822,13 +2816,11 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to - // read the memory safely instead. - uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead == sizeof inst && (inst == 0x0a77 || inst == 0x0aad)) { + // directly accessing it could cause a SIGSEGV. + if (!isReadableAddr(pc) || !isReadableAddr(pc + 2)) ajordanr-google wrote: Done https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libunwind] [llvm] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', ajordanr-google wrote: Done. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [llvm] [libunwind] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 01/10] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[llvm] [clang] [libunwind] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -2974,6 +2966,39 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, buf, bufLen, offset); } +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) +template +bool UnwindCursor::isReadableAddr(const pint_t addr) const { + // This code is heavily based on Abseil's 'address_is_readable.cc', + // which is Copyright Abseil Authors (2017), and provided under + // the Apache License 2.0. + + // Align to 8-bytes. + const auto alignedAddr = addr & ~pint_t{7}; + const auto sigsetAddr = reinterpret_cast(alignedAddr); + // We have to check that addr is nullptr because sigprocmask allows that + // as an argument without failure. + if (!sigsetAddr) +return false; + + // We MUST use the raw sigprocmask syscall here, as wrappers may try to + // access sigsetAddr which may cause a SIGSEGV. The raw syscall however is + // safe. Additionally, we need to pass the kernel_sigset_size, which is + // different from libc sizeof(sigset_t). Some archs have sigset_t + // defined as unsigned long, so let's use that. + const auto approxKernelSigsetSize = sizeof(unsigned long); ajordanr-google wrote: Thanks! If you have a better idea on how to make this more robust, I'd love to know. For now I've hardcoded it to 8 then. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [llvm] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google edited https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [llvm] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
@@ -26,7 +26,7 @@ __attribute__((naked)) void bad_unwind_info() { #if defined(__aarch64__) __asm__("// not using 0 because unwinder was already resilient to that\n" - "mov x8, #4\n" + "mov x8, #12\n" ajordanr-google wrote: Note: changed this for an 8 byte alignment check, because `0x4 & 0x7` is `0x0`, whereas `0xC & 0x7` is `0x8`. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[llvm] [clang] [libunwind] [clang-tools-extra] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 1/8] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[clang] [llvm] [clang-tools-extra] [libunwind] [libunwind] Replace process_vm_readv with SYS_rt_sigprocmask (PR #74791)
https://github.com/ajordanr-google edited https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [llvm] [clang-tools-extra] [libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google edited https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [libunwind] [clang] [llvm] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 1/7] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 1/7] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 1/5] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 1/4] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 1/3] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d9..5e4e376220daa0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead ==
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
@@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); ajordanr-google wrote: Gotcha, that all makes sense. Thanks! --- Playing around with `SYS_rt_procmask` on Linux x86_64, There's apparently a point in which we can still read a given address but `SYS_rt_procmask` sets `errno = 14` (Bad Address) Looping over the raw syscall with: ``` syscall(SYS_rt_sigprocmask, /* how= */ -1, Addr, Addr, 8); until it breaks gives ``` Addr: 0x56322ab46ff8; Offset: 59704; errno: 22; Value: 0x0 Addr: 0x56322ab46ff9; Offset: 59705; errno: 14; Value: 0x0 Addr: 0x56322ab46ffa; Offset: 59706; errno: 14; Value: 0x0 Addr: 0x56322ab46ffb; Offset: 59707; errno: 14; Value: 0x0 Addr: 0x56322ab46ffc; Offset: 59708; errno: 14; Value: 0x0 Addr: 0x56322ab46ffd; Offset: 59709; errno: 14; Value: 0x0 Addr: 0x56322ab46ffe; Offset: 59710; errno: 14; Value: 0x0 Addr: 0x56322ab46fff; Offset: 59711; errno: 14; Value: 0x0 fish: Job 1, './main' terminated by signal SIGSEGV (Address boundary error) ``` Seems there's about a 7 to 8 byte boundary where it fails but we can still read, at least on x86_64. So the 8 byte alignment on AARCH64 as mentioned in address_is_readable.cc seems to make sense, though we'd probably want to still check both the beginning and end of the `instructions` range. I think that should be sufficient. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
@@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; ajordanr-google wrote: Fixed. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google updated https://github.com/llvm/llvm-project/pull/74791 >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH 1/2] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d..5e4e376220daa 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073) return false; @@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; - struct iovec local_iov = {, sizeof inst}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; - long bytesRead = process_vm_readv(getpid(), _iov, 1, _iov, 1, 0); - if (bytesRead == sizeof
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google edited https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
@@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); ajordanr-google wrote: This is interesting, thanks. I definitely could, though I'm mildly hesitant for two reasons. The first is because of the [comment from address_is_readable.cc](https://chromium.googlesource.com/external/github.com/abseil/abseil-cpp/+/HEAD/absl/debugging/internal/address_is_readable.cc#74) ```c++ // This strategy depends on Linux implementation details, // so we rely on the test to alert us if it stops working. ``` The other is whether the check will span the full set of instructions we want to read. The Abseil implimentation checks 8 bytes, which I think should just about fit everything we need if it weren't for that 8 byte alignment: ```c++ // Align address on 8-byte boundary. On aarch64, checking last // byte before inaccessible page returned unexpected EFAULT. const uintptr_t u_addr = reinterpret_cast(addr) & ~uintptr_t{7}; ``` I'm not familiar with the alignment requirements for a (potentially broken) PC. You'd have more context than I do as to whether we'd need to check for both instructions with `rt_sigprocmask`? I notice in the comments that pipe was a discarded method, but it doesn't really explain why. Performance reasons perhaps? https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
@@ -2822,13 +2825,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { // onto the stack. const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to + // directly accessing it could cause a segfault. Use pipe/write/read to // read the memory safely instead. uint16_t inst; ajordanr-google wrote: Should be removed, since we redefined this lower down on line 2834. https://github.com/llvm/llvm-project/pull/74791 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)
https://github.com/ajordanr-google created https://github.com/llvm/llvm-project/pull/74791 process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` >From 1f4df1b82970c95684eed93c8f6bcaa6d6507b88 Mon Sep 17 00:00:00 2001 From: Jordan R Abrahams-Whitehead Date: Fri, 8 Dec 2023 00:09:59 + Subject: [PATCH] [libunwind] Replace process_vm_readv with pipe process_vm_readv is generally considered dangerous from a syscall perspective, and is frequently blanket banned in seccomp filters such as those in Chromium and ChromiumOS. We can get the same behaviour during the invalid PC address case with pipes and write/read. Testing to ensure that process_vm_readv does not appear, I ran the output of check-unwind on an ARM64 device under strace. Previously, bad_unwind_info in particular would use process_vm_readv, but with this commit, it now uses pipe2: ``` strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep process_vm_readv strace test/Output/bad_unwind_info.pass.cpp.dir/t.tmp.exe \ |& grep pipe2 ``` --- libunwind/src/UnwindCursor.hpp | 50 -- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 647a5a9c9d92d..5e4e376220daa 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -33,6 +33,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ defined(_LIBUNWIND_TARGET_S390X)) +#include #include #include #include @@ -2700,19 +2701,18 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); // The PC might contain an invalid address if the unwind info is bad, so - // directly accessing it could cause a segfault. Use process_vm_readv to read - // the memory safely instead. process_vm_readv was added in Linux 3.2, and - // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to - // be present. Unfortunately, there are Linux AArch64 environments where the - // libc wrapper for the syscall might not be present (e.g. Android 5), so call - // the syscall directly instead. + // directly accessing it could cause a segfault. Use pipe/write/read to read + // the memory safely instead. + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + if (bytesRead != bufferSize || instructions[0] != 0xd2801168 || instructions[1] != 0xd401) return false; @@ -2762,17 +2762,20 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { template bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { const pint_t pc = static_cast(getReg(UNW_REG_IP)); + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) +return false; uint32_t instructions[2]; - struct iovec local_iov = {, sizeof instructions}; - struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; - long bytesRead = - syscall(SYS_process_vm_readv, getpid(), _iov, 1, _iov, 1, 0); + const auto bufferSize = sizeof instructions; + if (write(pipefd[1], reinterpret_cast(pc), bufferSize) != bufferSize) +return false; + const auto bytesRead = read(pipefd[0], instructions, bufferSize); // Look for the two instructions used in the sigreturn trampoline // __vdso_rt_sigreturn: // // 0x08b00893 li a7,0x8b // 0x0073 ecall - if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + if (bytesRead != bufferSize || instructions[0] != 0x08b00893 || instructions[1] != 0x0073)