From: Mark Wielaard Date: Mon, 3 Mar 2014 10:43:43 +0000 (+0100) Subject: libdwfl: elf_from_remote_memory should use pagesize, not p_align. X-Git-Tag: elfutils-0.159~31 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f15bcda4137446d4773bb54cb99cc18497e9822c;p=thirdparty%2Felfutils.git 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 --- diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index ae5866051..e37139356 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,12 @@ +2014-03-03 Mark Wielaard + + * 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 * 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 7d35df6ab..602614b89 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,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 diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c index a52920cda..3384403c8 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);