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

Jordan R AW via cfe-commits cfe-commits at lists.llvm.org
Fri Dec 8 10:18:14 PST 2023


================
@@ -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);
----------------
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<uintptr_t>(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


More information about the cfe-commits mailing list