[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