This is the mail archive of the
elfutils-devel@sourceware.org
mailing list for the elfutils project.
[PATCH 1/2] libdwfl: elf_from_remote_memory should use pagesize, not p_align.
- From: Mark Wielaard <mjw at redhat dot com>
- To: elfutils-devel at lists dot fedorahosted dot org
- Date: Mon, 03 Mar 2014 15:31:27 +0100
- Subject: [PATCH 1/2] libdwfl: elf_from_remote_memory should use pagesize, not p_align.
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 | 9 ++++++++
libdwfl/elf-from-memory.c | 48 +++++++++++++++++++++++++++++---------------
libdwfl/linux-proc-maps.c | 6 +++-
3 files changed, 44 insertions(+), 19 deletions(-)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index ae58660..e371393 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -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.
diff --git a/libdwfl/elf-from-memory.c b/libdwfl/elf-from-memory.c
index 7d35df6..c18da27 100644
--- a/libdwfl/elf-from-memory.c
+++ b/libdwfl/elf-from-memory.c
@@ -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
@@ -48,10 +48,14 @@
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,28 @@ 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)
+ 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 +230,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 +241,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:
@@ -260,14 +274,14 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
switch (ehdr.e32.e_ident[EI_CLASS])
{
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 +290,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 +317,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
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
index a52920c..3384403 100644
--- a/libdwfl/linux-proc-maps.c
+++ b/libdwfl/linux-proc-maps.c
@@ -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);
--
1.7.1