diff options
author | Abseil Team <absl-team@google.com> | 2021-11-15 11:29:23 -0800 |
---|---|---|
committer | dinord <dino.radakovich@gmail.com> | 2021-11-15 14:43:32 -0500 |
commit | d587c966ed86dc3b94362444e2b20fc3c5c4224e (patch) | |
tree | 2a5d864e2389ec1425463c1ac40a73cb06216c78 /absl/debugging | |
parent | f2dbd918d8d08529800eb72f23bd2829f92104a4 (diff) |
Export of internal Abseil changes
--
355b8f7b0070b005d53d94ee4180e95559ba2c88 by Derek Mauro <dmauro@google.com>:
Documentation: don't define absl::Status::ok() in terms of itself
Fixes #1058
PiperOrigin-RevId: 410035651
--
31c512c834b3a8979297adc5006c3727a3c6554b by Evan Brown <ezb@google.com>:
Cleanup: move set_params/set_slot_policy into btree_set.h and move map_params into btree_map.h.
Also change some `sizeof(value_type)`s to `sizeof(slot_type)`s and update some comments/variable names referring to values to refer to slots as appropriate in btree.h.
Motivation: preliminary cleanup towards node_btree_*.
PiperOrigin-RevId: 409991342
--
3129ca320d61a82f1c9ee8c02a23d25024eea4ab by Abseil Team <absl-team@google.com>:
Use simpler implementation for AddressIsReadable.
In particular, current solution doesn't work on systems configured with large
pid_t space.
PiperOrigin-RevId: 409397716
--
f71067f7494b19ce4a2e1df730b934dc931c51b2 by Martijn Vels <mvels@google.com>:
Add Span dependency
PiperOrigin-RevId: 409198889
GitOrigin-RevId: 355b8f7b0070b005d53d94ee4180e95559ba2c88
Change-Id: I7f4df3ec7739fdfde61d8ba983f07a08f6f1c7d7
Diffstat (limited to 'absl/debugging')
-rw-r--r-- | absl/debugging/internal/address_is_readable.cc | 127 |
1 files changed, 47 insertions, 80 deletions
diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 329c285f..26df3289 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -33,12 +33,12 @@ ABSL_NAMESPACE_END #else #include <fcntl.h> +#include <sys/stat.h> #include <sys/syscall.h> +#include <sys/types.h> #include <unistd.h> -#include <atomic> #include <cerrno> -#include <cstdint> #include "absl/base/internal/errno_saver.h" #include "absl/base/internal/raw_logging.h" @@ -47,89 +47,56 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { -// Pack a pid and two file descriptors into a 64-bit word, -// using 16, 24, and 24 bits for each respectively. -static uint64_t Pack(uint64_t pid, uint64_t read_fd, uint64_t write_fd) { - ABSL_RAW_CHECK((read_fd >> 24) == 0 && (write_fd >> 24) == 0, - "fd out of range"); - return (pid << 48) | ((read_fd & 0xffffff) << 24) | (write_fd & 0xffffff); -} - -// Unpack x into a pid and two file descriptors, where x was created with -// Pack(). -static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) { - *pid = x >> 48; - *read_fd = (x >> 24) & 0xffffff; - *write_fd = x & 0xffffff; -} - -// Return whether the byte at *addr is readable, without faulting. -// Save and restores errno. Returns true on systems where -// unimplemented. -// This is a namespace-scoped variable for correct zero-initialization. -static std::atomic<uint64_t> pid_and_fds; // initially 0, an invalid pid. - bool AddressIsReadable(const void *addr) { + int fd = 0; absl::base_internal::ErrnoSaver errno_saver; - // We test whether a byte is readable by using write(). Normally, this would - // be done via a cached file descriptor to /dev/null, but linux fails to - // check whether the byte is readable when the destination is /dev/null, so - // we use a cached pipe. We store the pid of the process that created the - // pipe to handle the case where a process forks, and the child closes all - // the file descriptors and then calls this routine. This is not perfect: - // the child could use the routine, then close all file descriptors and then - // use this routine again. But the likely use of this routine is when - // crashing, to test the validity of pages when dumping the stack. Beware - // that we may leak file descriptors, but we're unlikely to leak many. - int bytes_written; - int current_pid = getpid() & 0xffff; // we use only the low order 16 bits - do { // until we do not get EBADF trying to use file descriptors - int pid; - int read_fd; - int write_fd; - uint64_t local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); - Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); - while (current_pid != pid) { - int p[2]; - // new pipe - if (pipe(p) != 0) { - ABSL_RAW_LOG(FATAL, "Failed to create pipe, errno=%d", errno); - } - fcntl(p[0], F_SETFD, FD_CLOEXEC); - fcntl(p[1], F_SETFD, FD_CLOEXEC); - uint64_t new_pid_and_fds = Pack(current_pid, p[0], p[1]); - if (pid_and_fds.compare_exchange_strong( - local_pid_and_fds, new_pid_and_fds, std::memory_order_release, - std::memory_order_relaxed)) { - local_pid_and_fds = new_pid_and_fds; // fds exposed to other threads - } else { // fds not exposed to other threads; we can close them. - close(p[0]); - close(p[1]); - local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); - } - Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); - } - errno = 0; - // Use syscall(SYS_write, ...) instead of write() to prevent ASAN - // and other checkers from complaining about accesses to arbitrary - // memory. + for (int j = 0; j < 2; j++) { + // Here we probe with some syscall which + // - accepts a one-byte region of user memory as input + // - tests for EFAULT before other validation + // - has no problematic side-effects + // + // connect(2) works for this. It copies the address into kernel + // memory before any validation beyond requiring an open fd. + // But a one byte address is never valid (sa_family is two bytes), + // so the call cannot succeed and change any state. + // + // This strategy depends on Linux implementation details, + // so we rely on the test to alert us if it stops working. + // + // Some discarded past approaches: + // - msync() doesn't reject PROT_NONE regions + // - write() on /dev/null doesn't return EFAULT + // - write() on a pipe requires creating it and draining the writes + // + // Use syscall(SYS_connect, ...) instead of connect() to prevent ASAN + // and other checkers from complaining about accesses to arbitrary memory. do { - bytes_written = syscall(SYS_write, write_fd, addr, 1); - } while (bytes_written == -1 && errno == EINTR); - if (bytes_written == 1) { // remove the byte from the pipe - char c; - while (read(read_fd, &c, 1) == -1 && errno == EINTR) { + ABSL_RAW_CHECK(syscall(SYS_connect, fd, addr, 1) == -1, + "should never succeed"); + } while (errno == EINTR); + if (errno == EFAULT) return false; + if (errno == EBADF) { + if (j != 0) { + // Unclear what happened. + ABSL_RAW_LOG(ERROR, "unexpected EBADF on fd %d", fd); + return false; } + // fd 0 must have been closed. Try opening it again. + // Note: we shouldn't leak too many file descriptors here, since we expect + // to get fd==0 reopened. + fd = open("/dev/null", O_RDONLY); + if (fd == -1) { + ABSL_RAW_LOG(ERROR, "can't open /dev/null"); + return false; + } + } else { + // probably EINVAL or ENOTSOCK; we got past EFAULT validation. + return true; } - if (errno == EBADF) { // Descriptors invalid. - // If pid_and_fds contains the problematic file descriptors we just used, - // this call will forget them, and the loop will try again. - pid_and_fds.compare_exchange_strong(local_pid_and_fds, 0, - std::memory_order_release, - std::memory_order_relaxed); - } - } while (errno == EBADF); - return bytes_written == 1; + } + ABSL_RAW_CHECK(false, "unreachable"); + return false; } } // namespace debugging_internal |