]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdwfl: elf_from_remote_memory should use pagesize, not p_align.
authorMark Wielaard <mjw@redhat.com>
Mon, 3 Mar 2014 10:43:43 +0000 (11:43 +0100)
committerMark Wielaard <mjw@redhat.com>
Mon, 10 Mar 2014 20:28:48 +0000 (21:28 +0100)
elf_from_remote_memory would use the actual p_align of the PT_LOAD segments
to calculate the loadbase, end and start of a segment. But the dynamic
loader aligns the segments using the pagesize and only sanity checks the
p_align values. So we should do the same to get accurate segment addresses.
Also fixes a small memory leak in case the ELF image appears to be bad.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
libdwfl/ChangeLog
libdwfl/elf-from-memory.c
libdwfl/linux-proc-maps.c

index ae5866051ae651c94ca72c430d1fdcdbedbb70e5..e37139356399fdc2598e3b46dab533d308339dbc 100644 (file)
@@ -1,3 +1,12 @@
+2014-03-03  Mark Wielaard  <mjw@redhat.com>
+
+       * elf-from-memory.c (elf_from_remote_memory): Take pagesize as
+       argument. Free buffer when detecting bad elf. Check PT_LOAD
+       alignment requirements on first handle_segment pass. Calculate
+       loadbase, start and end of segment using pagesize, not p_align.
+       * linux-proc-maps.c (dwfl_linux_proc_find_elf): Provide pagesize
+       to elf_from_remote_memory.
+
 2014-02-26  Mark Wielaard  <mjw@redhat.com>
 
        * linux-proc-maps.c (proc_maps_report): Don't assert on bad input.
index 7d35df6ab9cd19b32e745c6fc36d32bc572618c5..602614b898fb3923c8dc04e5793d13d5135b766a 100644 (file)
@@ -1,5 +1,5 @@
 /* Reconstruct an ELF file by reading the segments out of remote memory.
-   Copyright (C) 2005-2011 Red Hat, Inc.
+   Copyright (C) 2005-2011, 2014 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
    MAXREAD bytes from the remote memory at target address ADDRESS into the
    local buffer at DATA; it should return -1 for errors (with code in
    `errno'), 0 if it failed to read at least MINREAD bytes due to EOF, or
-   the number of bytes read if >= MINREAD.  ARG is passed through.  */
+   the number of bytes read if >= MINREAD.  ARG is passed through.
+
+   PAGESIZE is the minimum page size and alignment used for the PT_LOAD
+   segments.  */
 
 Elf *
 elf_from_remote_memory (GElf_Addr ehdr_vma,
+                       GElf_Xword pagesize,
                        GElf_Addr *loadbasep,
                        ssize_t (*read_memory) (void *arg, void *data,
                                                GElf_Addr address,
@@ -83,6 +87,7 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
   if (memcmp (buffer, ELFMAG, SELFMAG) != 0)
     {
     bad_elf:
+      free (buffer);
       __libdwfl_seterrno (DWFL_E_BADELF);
       return NULL;
     }
@@ -195,21 +200,32 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
   bool found_base = false;
   switch (ehdr.e32.e_ident[EI_CLASS])
     {
-      inline void handle_segment (GElf_Addr vaddr, GElf_Off offset,
-                                 GElf_Xword filesz, GElf_Xword align)
+      /* Sanity checks segments and calculates segment_end,
+        segments_end, segments_end_mem and loadbase (if not
+        found_base yet).  Returns true if sanity checking failed,
+        false otherwise.  */
+      inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
+                                 GElf_Xword filesz, GElf_Xword palign)
        {
-         GElf_Off segment_end = ((offset + filesz + align - 1) & -align);
+         /* Sanity check the alignment requirements.  */
+         if ((palign & (pagesize - 1)) != 0
+             || ((vaddr - offset) & (palign - 1)) != 0)
+           return true;
+
+         GElf_Off segment_end = ((offset + filesz + pagesize - 1)
+                                 & -pagesize);
 
          if (segment_end > (GElf_Off) contents_size)
            contents_size = segment_end;
 
-         if (!found_base && (offset & -align) == 0)
+         if (!found_base && (offset & -pagesize) == 0)
            {
-             loadbase = ehdr_vma - (vaddr & -align);
+             loadbase = ehdr_vma - (vaddr & -pagesize);
              found_base = true;
            }
 
          segments_end = offset + filesz;
+         return false;
        }
 
     case ELFCLASS32:
@@ -218,8 +234,9 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
        goto libelf_error;
       for (uint_fast16_t i = 0; i < phnum; ++i)
        if (phdrs.p32[i].p_type == PT_LOAD)
-         handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
-                         phdrs.p32[i].p_filesz, phdrs.p32[i].p_align);
+         if (handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
+                             phdrs.p32[i].p_filesz, phdrs.p32[i].p_align))
+           goto bad_elf;
       break;
 
     case ELFCLASS64:
@@ -228,8 +245,9 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
        goto libelf_error;
       for (uint_fast16_t i = 0; i < phnum; ++i)
        if (phdrs.p64[i].p_type == PT_LOAD)
-         handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
-                         phdrs.p64[i].p_filesz, phdrs.p64[i].p_align);
+         if (handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
+                             phdrs.p64[i].p_filesz, phdrs.p64[i].p_align))
+           goto bad_elf;
       break;
 
     default:
@@ -259,15 +277,17 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
 
   switch (ehdr.e32.e_ident[EI_CLASS])
     {
+      /* Reads the given segment.  Returns true if reading fails,
+        false otherwise.  */
       inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
-                                 GElf_Xword filesz, GElf_Xword align)
+                                 GElf_Xword filesz)
        {
-         GElf_Off start = offset & -align;
-         GElf_Off end = (offset + filesz + align - 1) & -align;
+         GElf_Off start = offset & -pagesize;
+         GElf_Off end = (offset + filesz + pagesize - 1) & -pagesize;
          if (end > (GElf_Off) contents_size)
            end = contents_size;
          nread = (*read_memory) (arg, buffer + start,
-                                 (loadbase + vaddr) & -align,
+                                 (loadbase + vaddr) & -pagesize,
                                  end - start, end - start);
          return nread <= 0;
        }
@@ -276,7 +296,7 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
       for (uint_fast16_t i = 0; i < phnum; ++i)
        if (phdrs.p32[i].p_type == PT_LOAD)
          if (handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
-                             phdrs.p32[i].p_filesz, phdrs.p32[i].p_align))
+                             phdrs.p32[i].p_filesz))
            goto read_error;
 
       /* If the segments visible in memory didn't include the section
@@ -303,7 +323,7 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
       for (uint_fast16_t i = 0; i < phnum; ++i)
        if (phdrs.p64[i].p_type == PT_LOAD)
          if (handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
-                             phdrs.p64[i].p_filesz, phdrs.p64[i].p_align))
+                             phdrs.p64[i].p_filesz))
            goto read_error;
 
       /* If the segments visible in memory didn't include the section
index a52920cdab8867daa7b7fd0c87253ad2f252c548..3384403c88facc292189734c483a563d6fd0e2e9 100644 (file)
@@ -1,5 +1,5 @@
 /* Standard libdwfl callbacks for debugging a live Linux process.
-   Copyright (C) 2005-2010, 2013 Red Hat, Inc.
+   Copyright (C) 2005-2010, 2013, 2014 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -321,6 +321,7 @@ read_proc_memory (void *arg, void *data, GElf_Addr address,
 }
 
 extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
+                                   GElf_Xword pagesize,
                                    GElf_Addr *loadbasep,
                                    ssize_t (*read_memory) (void *arg,
                                                            void *data,
@@ -375,7 +376,8 @@ dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
       if (fd < 0)
        return -1;
 
-      *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
+      *elfp = elf_from_remote_memory (base, getpagesize (), NULL,
+                                     &read_proc_memory, &fd);
 
       close (fd);