summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2017-04-20 16:04:54 +0200
committerMark Wielaard <mark@klomp.org>2017-04-27 16:38:48 +0200
commiteb956e080ff54dd219231f70f727b6b10eecfe42 (patch)
treeb44fc953aac29eb701e744bfe62289d231584586
parent28149ca8173a49c6c4fcc3a3eb919bb2420517be (diff)
Protect against integer overflow on shnum
If shnum is 0, the many "shnum - 1" would result in an overflow. Check it for 0, and only subtract once, rather than on every usage. Signed-off-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--libdwfl/ChangeLog5
-rw-r--r--libdwfl/dwfl_module_getdwarf.c18
-rw-r--r--src/ChangeLog4
-rw-r--r--src/unstrip.c25
4 files changed, 33 insertions, 19 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 5c814181..92043209 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,5 +1,10 @@
2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+ * dwfl_module_getdwarf.c: Check shnum for 0 before subtracting from
+ it.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
* dwfl_frame.c: Drop unused sys/ptrace.h include.
* frame_unwind.c: Likewise.
* linux-pid-attach.c: Include sys/ptrace.h and sys/syscall.h only on
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index 46caece5..c8b839dc 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -349,12 +349,14 @@ find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
/* Since prelink does not store the zeroth section header in the undo
section, it cannot support SHN_XINDEX encoding. */
- if (unlikely (shnum >= SHN_LORESERVE)
+ if (unlikely (shnum >= SHN_LORESERVE) || unlikely(shnum == 0)
|| unlikely (undodata->d_size != (src.d_size
+ phnum * phentsize
+ (shnum - 1) * shentsize)))
return DWFL_E_BAD_PRELINK;
+ --shnum;
+
/* We look at the allocated SHT_PROGBITS (or SHT_NOBITS) sections. (Most
every file will have some SHT_PROGBITS sections, but it's possible to
have one with nothing but .bss, i.e. SHT_NOBITS.) The special sections
@@ -431,12 +433,12 @@ find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
src.d_buf += src.d_size;
src.d_type = ELF_T_SHDR;
- src.d_size = gelf_fsize (mod->main.elf, ELF_T_SHDR, shnum - 1, EV_CURRENT);
+ src.d_size = gelf_fsize (mod->main.elf, ELF_T_SHDR, shnum, EV_CURRENT);
size_t shdr_size = class32 ? sizeof (Elf32_Shdr) : sizeof (Elf64_Shdr);
- if (unlikely (shnum - 1 > SIZE_MAX / shdr_size))
+ if (unlikely (shnum > SIZE_MAX / shdr_size))
return DWFL_E_NOMEM;
- const size_t shdrs_bytes = (shnum - 1) * shdr_size;
+ const size_t shdrs_bytes = shnum * shdr_size;
void *shdrs = malloc (shdrs_bytes);
if (unlikely (shdrs == NULL))
return DWFL_E_NOMEM;
@@ -488,16 +490,16 @@ find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
highest = 0;
if (class32)
{
- Elf32_Shdr (*s32)[shnum - 1] = shdrs;
- for (size_t i = 0; i < shnum - 1; ++i)
+ Elf32_Shdr (*s32)[shnum] = shdrs;
+ for (size_t i = 0; i < shnum; ++i)
consider_shdr (undo_interp, (*s32)[i].sh_type,
(*s32)[i].sh_flags, (*s32)[i].sh_addr,
(*s32)[i].sh_size, &highest);
}
else
{
- Elf64_Shdr (*s64)[shnum - 1] = shdrs;
- for (size_t i = 0; i < shnum - 1; ++i)
+ Elf64_Shdr (*s64)[shnum] = shdrs;
+ for (size_t i = 0; i < shnum; ++i)
consider_shdr (undo_interp, (*s64)[i].sh_type,
(*s64)[i].sh_flags, (*s64)[i].sh_addr,
(*s64)[i].sh_size, &highest);
diff --git a/src/ChangeLog b/src/ChangeLog
index e0a591e2..1521d808 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,7 @@
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * unstrip.c: Check shnum for 0 before subtracting from it.
+
2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
* readelf.c: Replace YESSTR and NOSTR with gettext ("yes") and
diff --git a/src/unstrip.c b/src/unstrip.c
index 6e57a6b5..50749093 100644
--- a/src/unstrip.c
+++ b/src/unstrip.c
@@ -1027,21 +1027,24 @@ find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab,
shnum = ehdr.e64.e_shnum;
}
+ bool class32 = ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32;
+ size_t shsize = class32 ? sizeof (Elf32_Shdr) : sizeof (Elf64_Shdr);
+ if (unlikely (shnum == 0 || shnum > SIZE_MAX / shsize + 1))
+ error (EXIT_FAILURE, 0, _("overflow with shnum = %zu in '%s' section"),
+ (size_t) shnum, ".gnu.prelink_undo");
+
+ --shnum;
+
size_t phsize = gelf_fsize (main, ELF_T_PHDR, phnum, EV_CURRENT);
src.d_buf += src.d_size + phsize;
- src.d_size = gelf_fsize (main, ELF_T_SHDR, shnum - 1, EV_CURRENT);
+ src.d_size = gelf_fsize (main, ELF_T_SHDR, shnum, EV_CURRENT);
src.d_type = ELF_T_SHDR;
if ((size_t) (src.d_buf - undodata->d_buf) > undodata->d_size
|| undodata->d_size - (src.d_buf - undodata->d_buf) != src.d_size)
error (EXIT_FAILURE, 0, _("invalid contents in '%s' section"),
".gnu.prelink_undo");
- bool class32 = ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32;
- size_t shsize = class32 ? sizeof (Elf32_Shdr) : sizeof (Elf64_Shdr);
- if (unlikely ((shnum - 1) > SIZE_MAX / shsize))
- error (EXIT_FAILURE, 0, _("overflow with shnum = %zu in '%s' section"),
- (size_t) shnum, ".gnu.prelink_undo");
- const size_t shdr_bytes = (shnum - 1) * shsize;
+ const size_t shdr_bytes = shnum * shsize;
void *shdr = xmalloc (shdr_bytes);
dst.d_buf = shdr;
dst.d_size = shdr_bytes;
@@ -1049,12 +1052,12 @@ find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab,
main_ehdr->e_ident[EI_DATA]) != NULL,
_("cannot read '.gnu.prelink_undo' section: %s"));
- undo_sections = xmalloc ((shnum - 1) * sizeof undo_sections[0]);
- for (size_t i = 0; i < shnum - 1; ++i)
+ undo_sections = xmalloc (shnum * sizeof undo_sections[0]);
+ for (size_t i = 0; i < shnum; ++i)
{
struct section *sec = &undo_sections[undo_nalloc];
- Elf32_Shdr (*s32)[shnum - 1] = shdr;
- Elf64_Shdr (*s64)[shnum - 1] = shdr;
+ Elf32_Shdr (*s32)[shnum] = shdr;
+ Elf64_Shdr (*s64)[shnum] = shdr;
if (class32)
{
#define COPY(field) sec->shdr.field = (*s32)[i].field