[libunwind] [libunwind] Replace process_vm_readv with pipe (PR #74791)

Jordan R AW via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 18 14:55:24 PST 2023


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 <ajordanr at google.com>
Date: Fri, 8 Dec 2023 00:09:59 +0000
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 <fcntl.h>
 #include <sys/syscall.h>
 #include <sys/uio.h>
 #include <unistd.h>
@@ -2700,19 +2701,18 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_arm64 &) {
   // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S
   const pint_t pc = static_cast<pint_t>(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 = {&instructions, sizeof instructions};
-  struct iovec remote_iov = {reinterpret_cast<void *>(pc), sizeof instructions};
-  long bytesRead =
-      syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0);
+  const auto bufferSize = sizeof instructions;
+  if (write(pipefd[1], reinterpret_cast<void *>(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] != 0xd4000001)
     return false;
 
@@ -2762,17 +2762,20 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_arm64 &) {
 template <typename A, typename R>
 bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_riscv &) {
   const pint_t pc = static_cast<pint_t>(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 = {&instructions, sizeof instructions};
-  struct iovec remote_iov = {reinterpret_cast<void *>(pc), sizeof instructions};
-  long bytesRead =
-      syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0);
+  const auto bufferSize = sizeof instructions;
+  if (write(pipefd[1], reinterpret_cast<void *>(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
   // 0x00000073 ecall
-  if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 ||
+  if (bytesRead != bufferSize || instructions[0] != 0x08b00893 ||
       instructions[1] != 0x00000073)
     return false;
 
@@ -2822,13 +2825,18 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
   // onto the stack.
   const pint_t pc = static_cast<pint_t>(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 = {&inst, sizeof inst};
-  struct iovec remote_iov = {reinterpret_cast<void *>(pc), sizeof inst};
-  long bytesRead = process_vm_readv(getpid(), &local_iov, 1, &remote_iov, 1, 0);
-  if (bytesRead == sizeof inst && (inst == 0x0a77 || inst == 0x0aad)) {
+  int pipefd[2];
+  if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1)
+    return false;
+  uint16_t inst;
+  const auto bufferSize = sizeof inst;
+  if (write(pipefd[1], reinterpret_cast<void *>(pc), bufferSize) != bufferSize)
+    return false;
+  const auto bytesRead = read(pipefd[0], &inst, bufferSize);
+  if (bytesRead == bufferSize && (inst == 0x0a77 || inst == 0x0aad)) {
     _info = {};
     _info.start_ip = pc;
     _info.end_ip = pc + 2;

>From c65626a68f2d47ac84ad8df4b3cfdfead43ffe63 Mon Sep 17 00:00:00 2001
From: Jordan R Abrahams-Whitehead <ajordanr at google.com>
Date: Fri, 8 Dec 2023 22:41:17 +0000
Subject: [PATCH 2/7] fixup! [libunwind] Replace process_vm_readv with pipe

---
 libunwind/src/UnwindCursor.hpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 5e4e376220daa0..c36be5a6c0ad1f 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -2827,7 +2827,6 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
   // The PC might contain an invalid address if the unwind info is bad, so
   // directly accessing it could cause a segfault. Use pipe/write/read to
   // read the memory safely instead.
-  uint16_t inst;
   int pipefd[2];
   if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1)
     return false;

>From 03cf6f3ae42e84ce889e57f76cf2893eef9da0f3 Mon Sep 17 00:00:00 2001
From: Jordan R Abrahams-Whitehead <ajordanr at google.com>
Date: Wed, 13 Dec 2023 21:35:55 +0000
Subject: [PATCH 3/7] fixup! [libunwind] Replace process_vm_readv with pipe

---
 libunwind/src/UnwindCursor.hpp | 66 ++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 30 deletions(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index c36be5a6c0ad1f..165434dde74a7d 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -558,6 +558,26 @@ class UnwindCursor : public AbstractUnwindCursor {
       _info.handler = 0;
     return UNW_STEP_SUCCESS;
   }
+  bool isReadableAddr(const void *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.
+
+    // We have to check that addr is a nullptr because sigprocmask allows that
+    // as an argument without failure.
+    if (!addr)
+      return false;
+    // Align to 8-bytes.
+    const auto uintptr_addr = reinterpret_cast<uintptr_t>(addr) & ~uintptr_t{7};
+    const auto sigsetaddr = reinterpret_cast<sigset_t *>(uintptr_addr);
+    [[maybe_unused]] int Result = sigprocmask(/*how=*/-1, sigsetaddr, nullptr);
+    // 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.
+    assert(Result == -1);
+    assert(errno == EFAULT || errno == EINVAL);
+    return errno != EFAULT;
+  }
 
   A                   &_addressSpace;
   unw_proc_info_t      _info;
@@ -2701,19 +2721,13 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_arm64 &) {
   // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S
   const pint_t pc = static_cast<pint_t>(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 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];
-  const auto bufferSize = sizeof instructions;
-  if (write(pipefd[1], reinterpret_cast<void *>(pc), bufferSize) != bufferSize)
+  // directly accessing it could cause a SIGSEGV.
+  if (!isReadableAddr(static_cast<void *>(pc)) ||
+      !isReadableAddr(static_cast<void *>(pc + 4)))
     return false;
-  const auto bytesRead = read(pipefd[0], instructions, bufferSize);
+  auto *instructions = static_cast<uint32_t *>(pc);
   // Look for instructions: mov x8, #0x8b; svc #0x0
-  if (bytesRead != bufferSize || instructions[0] != 0xd2801168 ||
-      instructions[1] != 0xd4000001)
+  if (instructions[0] != 0xd2801168 || instructions[1] != 0xd4000001)
     return false;
 
   _info = {};
@@ -2762,21 +2776,18 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_arm64 &) {
 template <typename A, typename R>
 bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_riscv &) {
   const pint_t pc = static_cast<pint_t>(getReg(UNW_REG_IP));
-  int pipefd[2];
-  if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1)
-    return false;
-  uint32_t instructions[2];
-  const auto bufferSize = sizeof instructions;
-  if (write(pipefd[1], reinterpret_cast<void *>(pc), bufferSize) != bufferSize)
+  // The PC might contain an invalid address if the unwind info is bad, so
+  // directly accessing it could cause a SIGSEGV.
+  if (!isReadableAddr(static_cast<void *>(pc)) ||
+      !isReadableAddr(static_cast<void *>(pc + 4)))
     return false;
-  const auto bytesRead = read(pipefd[0], instructions, bufferSize);
+  const auto *instructions = static_cast<uint32_t *>(pc);
   // Look for the two instructions used in the sigreturn trampoline
   // __vdso_rt_sigreturn:
   //
   // 0x08b00893 li a7,0x8b
   // 0x00000073 ecall
-  if (bytesRead != bufferSize || instructions[0] != 0x08b00893 ||
-      instructions[1] != 0x00000073)
+  if (instructions[0] != 0x08b00893 || instructions[1] != 0x00000073)
     return false;
 
   _info = {};
@@ -2825,17 +2836,12 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
   // onto the stack.
   const pint_t pc = static_cast<pint_t>(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 pipe/write/read to
-  // read the memory safely instead.
-  int pipefd[2];
-  if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1)
-    return false;
-  uint16_t inst;
-  const auto bufferSize = sizeof inst;
-  if (write(pipefd[1], reinterpret_cast<void *>(pc), bufferSize) != bufferSize)
+  // directly accessing it could cause a SIGSEGV.
+  if (!isReadableAddr(static_cast<void *>(pc)) ||
+      !isReadableAddr(static_cast<void *>(pc + 2)))
     return false;
-  const auto bytesRead = read(pipefd[0], &inst, bufferSize);
-  if (bytesRead == bufferSize && (inst == 0x0a77 || inst == 0x0aad)) {
+  const auto inst = *reinterpret_cast<uint16_t *>(pc);
+  if (inst == 0x0a77 || inst == 0x0aad) {
     _info = {};
     _info.start_ip = pc;
     _info.end_ip = pc + 2;

>From 9b22940209fb555120c574738c369eb3559e805f Mon Sep 17 00:00:00 2001
From: Jordan R Abrahams-Whitehead <ajordanr at google.com>
Date: Wed, 13 Dec 2023 21:41:51 +0000
Subject: [PATCH 4/7] fixup! [libunwind] Replace process_vm_readv with pipe

---
 libunwind/src/UnwindCursor.hpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 165434dde74a7d..f5050d327f6f04 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -12,6 +12,8 @@
 #define __UNWINDCURSOR_HPP__
 
 #include "cet_unwind.h"
+#include <errno.h>
+#include <signal.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,7 +35,6 @@
 #if defined(_LIBUNWIND_TARGET_LINUX) &&                                        \
     (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \
      defined(_LIBUNWIND_TARGET_S390X))
-#include <fcntl.h>
 #include <sys/syscall.h>
 #include <sys/uio.h>
 #include <unistd.h>
@@ -2725,7 +2726,7 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_arm64 &) {
   if (!isReadableAddr(static_cast<void *>(pc)) ||
       !isReadableAddr(static_cast<void *>(pc + 4)))
     return false;
-  auto *instructions = static_cast<uint32_t *>(pc);
+  auto *instructions = reinterpret_cast<uint32_t *>(pc);
   // Look for instructions: mov x8, #0x8b; svc #0x0
   if (instructions[0] != 0xd2801168 || instructions[1] != 0xd4000001)
     return false;
@@ -2781,7 +2782,7 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_riscv &) {
   if (!isReadableAddr(static_cast<void *>(pc)) ||
       !isReadableAddr(static_cast<void *>(pc + 4)))
     return false;
-  const auto *instructions = static_cast<uint32_t *>(pc);
+  const auto *instructions = reinterpret_cast<uint32_t *>(pc);
   // Look for the two instructions used in the sigreturn trampoline
   // __vdso_rt_sigreturn:
   //

>From 8165be8ef4635eee959ad3cf9deb1ef240231afd Mon Sep 17 00:00:00 2001
From: Jordan R Abrahams-Whitehead <ajordanr at google.com>
Date: Wed, 13 Dec 2023 21:51:28 +0000
Subject: [PATCH 5/7] fixup! [libunwind] Replace process_vm_readv with pipe

---
 libunwind/src/UnwindCursor.hpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index f5050d327f6f04..436b6fc3bc5a3c 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -2723,10 +2723,10 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_arm64 &) {
   const pint_t pc = static_cast<pint_t>(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(static_cast<void *>(pc)) ||
-      !isReadableAddr(static_cast<void *>(pc + 4)))
+  if (!isReadableAddr(static_cast<const void *>(pc)) ||
+      !isReadableAddr(static_cast<const void *>(pc + 4)))
     return false;
-  auto *instructions = reinterpret_cast<uint32_t *>(pc);
+  auto *instructions = reinterpret_cast<const uint32_t *>(pc);
   // Look for instructions: mov x8, #0x8b; svc #0x0
   if (instructions[0] != 0xd2801168 || instructions[1] != 0xd4000001)
     return false;
@@ -2779,10 +2779,10 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_riscv &) {
   const pint_t pc = static_cast<pint_t>(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(static_cast<void *>(pc)) ||
-      !isReadableAddr(static_cast<void *>(pc + 4)))
+  if (!isReadableAddr(static_cast<const void *>(pc)) ||
+      !isReadableAddr(static_cast<const void *>(pc + 4)))
     return false;
-  const auto *instructions = reinterpret_cast<uint32_t *>(pc);
+  const auto *instructions = reinterpret_cast<const uint32_t *>(pc);
   // Look for the two instructions used in the sigreturn trampoline
   // __vdso_rt_sigreturn:
   //
@@ -2838,10 +2838,10 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
   const pint_t pc = static_cast<pint_t>(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(static_cast<void *>(pc)) ||
-      !isReadableAddr(static_cast<void *>(pc + 2)))
+  if (!isReadableAddr(static_cast<const void *>(pc)) ||
+      !isReadableAddr(static_cast<const void *>(pc + 2)))
     return false;
-  const auto inst = *reinterpret_cast<uint16_t *>(pc);
+  const auto inst = *reinterpret_cast<const uint16_t *>(pc);
   if (inst == 0x0a77 || inst == 0x0aad) {
     _info = {};
     _info.start_ip = pc;

>From f3c2c22339a81efffa7dfc2355e282fd2ca1d8b4 Mon Sep 17 00:00:00 2001
From: Jordan R Abrahams-Whitehead <ajordanr at google.com>
Date: Wed, 13 Dec 2023 23:52:58 +0000
Subject: [PATCH 6/7] fixup! [libunwind] Replace process_vm_readv with pipe

---
 libunwind/src/UnwindCursor.hpp | 54 ++++++++++++++++++----------------
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 436b6fc3bc5a3c..67a6ab353a16ed 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -559,26 +559,6 @@ class UnwindCursor : public AbstractUnwindCursor {
       _info.handler = 0;
     return UNW_STEP_SUCCESS;
   }
-  bool isReadableAddr(const void *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.
-
-    // We have to check that addr is a nullptr because sigprocmask allows that
-    // as an argument without failure.
-    if (!addr)
-      return false;
-    // Align to 8-bytes.
-    const auto uintptr_addr = reinterpret_cast<uintptr_t>(addr) & ~uintptr_t{7};
-    const auto sigsetaddr = reinterpret_cast<sigset_t *>(uintptr_addr);
-    [[maybe_unused]] int Result = sigprocmask(/*how=*/-1, sigsetaddr, nullptr);
-    // 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.
-    assert(Result == -1);
-    assert(errno == EFAULT || errno == EINVAL);
-    return errno != EFAULT;
-  }
 
   A                   &_addressSpace;
   unw_proc_info_t      _info;
@@ -1012,6 +992,7 @@ class UnwindCursor : public AbstractUnwindCursor{
     R dummy;
     return stepThroughSigReturn(dummy);
   }
+  bool isReadableAddr(const pint_t addr) const;
 #if defined(_LIBUNWIND_TARGET_AARCH64)
   bool setInfoForSigReturn(Registers_arm64 &);
   int stepThroughSigReturn(Registers_arm64 &);
@@ -2723,8 +2704,7 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_arm64 &) {
   const pint_t pc = static_cast<pint_t>(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(static_cast<const void *>(pc)) ||
-      !isReadableAddr(static_cast<const void *>(pc + 4)))
+  if (!isReadableAddr(pc) || !isReadableAddr(pc + 4))
     return false;
   auto *instructions = reinterpret_cast<const uint32_t *>(pc);
   // Look for instructions: mov x8, #0x8b; svc #0x0
@@ -2779,8 +2759,7 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_riscv &) {
   const pint_t pc = static_cast<pint_t>(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(static_cast<const void *>(pc)) ||
-      !isReadableAddr(static_cast<const void *>(pc + 4)))
+  if (!isReadableAddr(pc) || !isReadableAddr(pc + 4))
     return false;
   const auto *instructions = reinterpret_cast<const uint32_t *>(pc);
   // Look for the two instructions used in the sigreturn trampoline
@@ -2838,8 +2817,7 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
   const pint_t pc = static_cast<pint_t>(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(static_cast<const void *>(pc)) ||
-      !isReadableAddr(static_cast<const void *>(pc + 2)))
+  if (!isReadableAddr(pc) || !isReadableAddr(pc + 2))
     return false;
   const auto inst = *reinterpret_cast<const uint16_t *>(pc);
   if (inst == 0x0a77 || inst == 0x0aad) {
@@ -2988,6 +2966,30 @@ bool UnwindCursor<A, R>::getFunctionName(char *buf, size_t bufLen,
                                          buf, bufLen, offset);
 }
 
+#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN)
+template <typename A, typename R>
+bool UnwindCursor<A, R>::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.
+
+  // We have to check that addr is nullptr (0) because sigprocmask allows that
+  // as an argument without failure.
+  if (addr == 0)
+    return false;
+  // Align to 8-bytes.
+  const auto alignedAddr = addr & ~pint_t{7};
+  const auto sigsetAddr = reinterpret_cast<sigset_t *>(alignedAddr);
+  [[maybe_unused]] int Result = sigprocmask(/*how=*/-1, sigsetAddr, nullptr);
+  // 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.
+  assert(Result == -1);
+  assert(errno == EFAULT || errno == EINVAL);
+  return errno != EFAULT;
+}
+#endif
+
 #if defined(_LIBUNWIND_USE_CET)
 extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) {
   AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;

>From f0d890294eaa1b2eb4414c89f293085cd80592e4 Mon Sep 17 00:00:00 2001
From: Jordan R Abrahams-Whitehead <ajordanr at google.com>
Date: Mon, 18 Dec 2023 22:54:31 +0000
Subject: [PATCH 7/7] fixup! [libunwind] Replace process_vm_readv with pipe

---
 libunwind/src/UnwindCursor.hpp          | 19 ++++++++++++++-----
 libunwind/test/bad_unwind_info.pass.cpp |  2 +-
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 67a6ab353a16ed..aa3c3626647910 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -2973,14 +2973,23 @@ bool UnwindCursor<A, R>::isReadableAddr(const pint_t addr) const {
   // which is Copyright Abseil Authors (2017), and provided under
   // the Apache License 2.0.
 
-  // We have to check that addr is nullptr (0) because sigprocmask allows that
-  // as an argument without failure.
-  if (addr == 0)
-    return false;
   // Align to 8-bytes.
   const auto alignedAddr = addr & ~pint_t{7};
   const auto sigsetAddr = reinterpret_cast<sigset_t *>(alignedAddr);
-  [[maybe_unused]] int Result = sigprocmask(/*how=*/-1, sigsetAddr, nullptr);
+  // 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]] 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.
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index b3284e8daed71f..2e7cf15f0dcab8 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -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"
           "stp     x30, x8, [sp, #-16]!\n"
           ".cfi_def_cfa_offset 16\n"
           "// purposely use incorrect offset for x30\n"



More information about the cfe-commits mailing list