And a couple of sanity checks and make sure that memory is released in
all error paths.
* elf-from-memory.c (elf_from_remote_memory): Check shentsize
is sane (or zero). Make sure shdrs_end calculation uses 64bit
unsigned values and check for overflow. Make sure nread >=
sizeof (Elf64_Ehdr) for ELFCLASS64. free buffer and phdrsp on
libelf_error. Check segment_end for overflow. Make sure
PT_LOAD end didn't wrap around. Directly go to libelf_error
when elf_memory fails.
Signed-off-by: Mark Wielaard <[email protected]>
---
libdwfl/elf-from-memory.c | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/libdwfl/elf-from-memory.c b/libdwfl/elf-from-memory.c
index 4015efd23427..c6137f73f14d 100644
--- a/libdwfl/elf-from-memory.c
+++ b/libdwfl/elf-from-memory.c
@@ -77,6 +77,7 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
return NULL;
}
+ /* Note minread is just enough for an Elf32_Ehdr. */
ssize_t nread = (*read_memory) (arg, buffer, ehdr_vma,
sizeof (Elf32_Ehdr), initial_bufsize);
if (nread <= 0)
@@ -130,6 +131,8 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
{
libelf_error:
+ free (buffer);
+ free (phdrsp);
__libdwfl_seterrno (DWFL_E_LIBELF);
return NULL;
}
@@ -143,10 +146,17 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
zero sh_size field. We ignore this here because getting shdrs
is just a nice bonus (see below where we trim the last phdrs
PT_LOAD segment). */
- shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * ehdr.e32.e_shentsize;
+ GElf_Xword shnum32 = ehdr.e32.e_shnum;
+ GElf_Xword shentsize32 = ehdr.e32.e_shentsize;
+ if (shentsize32 != sizeof (Elf32_Shdr) && shentsize32 != 0)
+ goto bad_elf;
+ shdrs_end = ehdr.e32.e_shoff + shnum32 * shentsize32;
break;
case ELFCLASS64:
+ /* Unlikely, but minread was for just an Elf32_Ehdr. */
+ if (unlikely ((size_t) nread < sizeof (Elf64_Ehdr)))
+ goto bad_elf;
xlatefrom.d_size = sizeof (Elf64_Ehdr);
if (elf64_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
goto libelf_error;
@@ -156,7 +166,11 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
if (phentsize != sizeof (Elf64_Phdr) || phnum == 0)
goto bad_elf;
/* See the NOTE above for shdrs_end and ehdr.e32.e_shnum. */
- shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * ehdr.e64.e_shentsize;
+ GElf_Xword shnum64 = ehdr.e64.e_shnum;
+ GElf_Xword shentsize64 = ehdr.e64.e_shentsize;
+ if (shentsize64 != sizeof (Elf64_Shdr) && shentsize64 != 0)
+ goto bad_elf;
+ shdrs_end = ehdr.e64.e_shoff + shnum64 * shentsize64;
break;
default:
@@ -254,6 +268,11 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
GElf_Off segment_end = ((offset + filesz + pagesize - 1)
& -pagesize);
+ /* Check for overflow or too big segment size. */
+ if (unlikely (segment_end < (offset & -pagesize)
+ || segment_end > SSIZE_MAX))
+ goto bad_elf;
+
if (segment_end > (GElf_Off) contents_size)
contents_size = segment_end;
@@ -310,6 +329,11 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
GElf_Off start = offset & -pagesize;
GElf_Off end = (offset + filesz + pagesize - 1) & -pagesize;
+
+ /* Make sure end didn't wrap around. */
+ if (unlikely (end < start))
+ goto bad_elf;
+
/* The final contents_size is the trimmed last segment's end, which
may be smaller than an earlier segment's start (segments_end above
tracks the last PT_LOAD, not the maximum). Skip any segment that
@@ -373,10 +397,7 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
Elf *elf = elf_memory ((char *) buffer, contents_size);
if (elf == NULL)
- {
- free (buffer);
- goto libelf_error;
- }
+ goto libelf_error;
elf->flags |= ELF_F_MALLOCED;
if (loadbasep != NULL)
--
2.55.0