I'm putting this up as discussed per D62503.
Today when process_vm_readv fails to read the entire range we fallback to ptrace and try to read the same range.
However, process_vm_readv does partial reads so there's no need to read again what has already been read by process_vm_readv.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
- Build Status
Buildable 32716 Build 32715: arc lint + arc unit
Event Timeline
Thinking about tests, do you know of any way to create memory which is readable by ptrace, but it is not accessible via process_vm_readv ? I know that latest android phones have memory like that, but I believe this depends on selinux or some other mechanism which cannot be reproduced in a test.
If we can't do anything like that, we should at least have an lldb-server test that performs a read ($m packet) crossing page boundaries, where one of the pages is unmapped. Probably the easiest way to guarantee that a particular page will be unmapped is to first mmap 2 pages worth of memory, and then munmap one of them.
| lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp | ||
|---|---|---|
| 1457 | llvm's policy is to *not* "almost always use auto". And it's nice to know whether this returns size_t, ssize_t, or something else without looking up the documentation. | |
| 1467 | addr += vm_bytes_read ? | |
| lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp | ||
|---|---|---|
| 1457 | Makes sense. I'll make sure to take a more careful read at https://llvm.org/docs/CodingStandards.html. | |
| 1467 | For some reason I thought that would increase addr by vm_bytes_read*4 or 8 but it's really just a uint64_t not a pointer. I'll also update ReadCStringFromMemory function on my other diff then, I believe I make the same logic there. | |
Actually, it looks like getting process_vm_readv to fail is as simple as running mprotect(..., PROT_NONE) on the piece of memory. Here's my test program, in case you find it useful for anything:
#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
long *mem;
void child() {
assert(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0);
raise(SIGSTOP);
_exit(0);
}
void parent(pid_t child) {
int status;
assert(waitpid(child, &status, __WALL) == child);
assert(WIFSTOPPED(status));
assert(WSTOPSIG(status) == SIGSTOP);
unsigned long myx;
struct iovec local;
local.iov_base = &myx;
local.iov_len = sizeof myx;
struct iovec remote;
remote.iov_base = mem;
remote.iov_len = sizeof myx;
ssize_t s = process_vm_readv(child, &local, 1, &remote, 1, 0);
fprintf(stderr, "readv: %zd (%d - %s)\n", s, errno, strerror(errno));
myx = ptrace(PTRACE_PEEKDATA, child, mem, 0);
fprintf(stderr, "peek: %lx (%d - %s)\n", myx, errno, strerror(errno));
_exit(0);
}
int main() {
mem = reinterpret_cast<long *>(mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0));
assert(mem != MAP_FAILED);
*mem = 0x424344454647;
assert(mprotect(mem, 0x1000, PROT_NONE) == 0);
pid_t pid = fork();
assert(pid != -1);
if (pid)
parent(pid);
else
child();
}
$ g++ a.cc
$ ./a.out
readv: -1 (14 - Bad address)
peek: 424344454647 (0 - Success)
llvm's policy is to *not* "almost always use auto". And it's nice to know whether this returns size_t, ssize_t, or something else without looking up the documentation.