[PATCH] elf: fix pldd on targets that map objects above 2^63 [BZ #34641]
Stian Halseth
stian@itx.no
Thu Sep 17 08:02:26 GMT 2026
Hi,
Ignore my email regarding the rationale on process_vm_readv. I hadn't
seen this response before I wrote it.
On Wed, 2026-09-16 at 16:01 -0300, Adhemerval Zanella Netto wrote:
>
>
> On 16/09/26 15:02, Stian Halseth wrote:
> > pldd reads target's memory with pread on /proc/PID/mem. The offset
> > argument of pread is signed, and ksys_pread64 rejects a negative
> > position. On sparc64 the dynamic linker and the shared objects are
> > mapped at 0xfff8000100000000 and above, so every such read fails
> > with
> > EINVAL and pldd exits with "cannot read r_debug". elf/tst-pldd
> > fails
> > as a result. This is not visible on x86_64, where objects are
> > mapped
> > low.
> >
> > Route the reads through a helper that tries pread first and, when
> > it
> > fails with EINVAL, falls back to process_vm_readv, which takes the
> > address as a pointer. pread stays the primary path so that a 32-
> > bit
> > pldd can still read a 64-bit target, which process_vm_readv cannot
> > do
> > once the address no longer fits a pointer; the fallback is taken
> > only
> > when it does fit. pldd already attaches with ptrace, so the
> > permissions
> > are in place, and both calls report a partial transfer as a short
> > count,
> > which the object-name loop in find_maps relies on.
> >
> > Tested on sparc64: pldd fails on every process before this change
> > and
> > lists them correctly after it. Under strace the reads of the main
> > executable, which is mapped low, go through pread, and the reads
> > that
> > hit EINVAL, those into ld.so and libc above 2^63, are retried with
> > process_vm_readv.
> >
> > Signed-off-by: Stian Halseth <stian@itx.no>
> > ---
> > elf/pldd-xx.c | 27 +++++++++++++++++++++------
> > elf/pldd.c | 3 +++
> > 2 files changed, 24 insertions(+), 6 deletions(-)
> >
> > diff --git a/elf/pldd-xx.c b/elf/pldd-xx.c
> > index 29bbb7307..073b145f9 100644
> > --- a/elf/pldd-xx.c
> > +++ b/elf/pldd-xx.c
> > @@ -73,6 +73,21 @@ _Static_assert (offsetof (struct r_debug, r_map)
> > #endif
> >
> >
> > +/* Read LEN bytes at ADDR in process PID into BUF. */
> > +static ssize_t
> > +E(read_mem) (int memfd, pid_t pid, void *buf, size_t len, EW(Addr)
> > addr)
> > +{
> > + ssize_t n = pread (memfd, buf, len, addr);
> > + if (n != -1)
> > + return n;
> > + if (errno != EINVAL || (EW(Addr)) (uintptr_t) addr != addr)
> > + return -1;
> > + struct iovec local = { .iov_base = buf, .iov_len = len };
> > + struct iovec remote = { .iov_base = (void *) (uintptr_t) addr,
> > + .iov_len = len };
> > + return process_vm_readv (pid, &local, 1, &remote, 1, 0);
> > +}
> > +
>
> The process_vm_readv was not added at same time on all ABIs. Although
> for mostly
> kABI it was done on 3.2, for arm32 compat (aarch64 kernel) it was
> done on 3.7,
> on hppa at 3.9.
>
> Maybe we can just lseek/read:
>
> static ssize_t
> pread_mem (int fd, void *buf, size_t nbytes, uint64_t addr)
> {
> if (lseek (fd, (off_t) addr, SEEK_SET) == (off_t) -1)
> return -1;
>
> return read (fd, buf, nbytes);
> }
>
Right, I missed that, and you're right. I have tested your suggestion.
It works and is much cleaner. Sending patch v2 now.
> > static int
> >
> > E(find_maps) (const char *exe, int memfd, pid_t pid, void *auxv,
> > @@ -103,7 +118,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> > pid, void *auxv,
> > error (EXIT_FAILURE, 0, gettext ("cannot find program header
> > of process"));
> >
> > EW(Phdr) *p = xmalloc (phnum * phent);
> > - if (pread (memfd, p, phnum * phent, phdr) != phnum * phent)
> > + if (E(read_mem) (memfd, pid, p, phnum * phent, phdr) != phnum *
> > phent)
> > error (EXIT_FAILURE, 0, gettext ("cannot read program
> > header"));
> >
> > /* Determine the load offset. We need this for interpreting the
> > @@ -124,7 +139,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> > pid, void *auxv,
> > if (p[i].p_type == PT_DYNAMIC)
> > {
> > EW(Dyn) *dyn = xmalloc (p[i].p_filesz);
> > - if (pread (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
> > + if (E(read_mem) (memfd, pid, dyn, p[i].p_filesz, offset +
> > p[i].p_vaddr)
> > != p[i].p_filesz)
> > error (EXIT_FAILURE, 0, gettext ("cannot read dynamic
> > section"));
> >
> > @@ -136,7 +151,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> > pid, void *auxv,
> > if (off != 0)
> > {
> > struct E(r_debug) r;
> > - if (pread (memfd, &r, sizeof (r), off)
> > + if (E(read_mem) (memfd, pid, &r, sizeof (r), off)
> > != sizeof (r))
> > error (EXIT_FAILURE, 0, gettext ("cannot read r_debug"));
> >
> > @@ -154,7 +169,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> > pid, void *auxv,
> > else if (p[i].p_type == PT_INTERP)
> > {
> > interp = xmalloc (p[i].p_filesz);
> > - if (pread (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
> > + if (E(read_mem) (memfd, pid, interp, p[i].p_filesz, offset +
> > p[i].p_vaddr)
> > != p[i].p_filesz)
> > error (EXIT_FAILURE, 0, gettext ("cannot read program
> > interpreter"));
> > }
> > @@ -184,13 +199,13 @@ E(find_maps) (const char *exe, int memfd,
> > pid_t pid, void *auxv,
> > do
> > {
> > struct E(link_map) m;
> > - if (pread (memfd, &m, sizeof (m), list) != sizeof (m))
> > + if (E(read_mem) (memfd, pid, &m, sizeof (m), list) != sizeof
> > (m))
> > error (EXIT_FAILURE, 0, gettext ("cannot read link map"));
> >
> > EW(Addr) name_offset = m.l_name;
> > while (1)
> > {
> > - ssize_t n = pread (memfd, tmpbuf.data, tmpbuf.length,
> > name_offset);
> > + ssize_t n = E(read_mem) (memfd, pid, tmpbuf.data,
> > tmpbuf.length, name_offset);
> > if (n == -1)
> > error (EXIT_FAILURE, 0, gettext ("cannot read object name"));
> >
> > diff --git a/elf/pldd.c b/elf/pldd.c
> > index 62e660c3f..762770026 100644
> > --- a/elf/pldd.c
> > +++ b/elf/pldd.c
> > @@ -27,6 +27,9 @@
> > #include <stdlib.h>
> > #include <unistd.h>
> > #include <sys/ptrace.h>
> > +#include <sys/uio.h>
> > +#include <stdint.h>
> > +#include <errno.h>
> > #include <sys/wait.h>
> > #include <scratch_buffer.h>
> >
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: This is a digitally signed message part
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20260917/a03eadf5/attachment.sig>
More information about the Libc-alpha
mailing list