[PATCH] Fix section corruption bug
by Thilo Schulz
Hi,
When adding data to existing sections in ELF files, libelf may corrupt
those sections, i.e. overwrite the existing data if certain conditions are
met.
If an Elf_Scn structure has seen a call to elf_rawdata(scn) before but no
call to elf_getdata(scn), scn->read_data flag is set, but not
scn->data_list_rear.
Thus, elf_newdata(scn) incorrectly detects a "new user added section" when
really it is a section with live, valid data that will be overwritten by
elf_update(), corrupting the section.
This patch fixes this incorrect behaviour.
Signed-off-by: Thilo Schulz <thilo(a)tjps.eu>
---
libelf/elf_newdata.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libelf/elf_newdata.c b/libelf/elf_newdata.c
index 90d1813..f90eb0a 100644
--- a/libelf/elf_newdata.c
+++ b/libelf/elf_newdata.c
@@ -64,7 +64,7 @@ elf_newdata (Elf_Scn *scn)
rwlock_wrlock (scn->elf->lock);
- if (scn->data_read && scn->data_list_rear == NULL)
+ if (scn->data_read && scn->data_list_rear == NULL && !scn->rawdata.s)
{
/* This means the section was created by the user and this is the
first data. */
--
1.7.10.4
8 years, 6 months
[PATCH] libdw: Search for the last matching address with dwarf_getsrc_die.
by Mark Wielaard
In commit 7d9b5a dwfl_module_getsrc was changed so that it returns the last
line record <= addr, rather than returning immediately on a match. This
changes dwarf_getsrc_die to do the same. And it adds a new test that checks
this by comparing against the same results from eu-addr2line (which uses
dwfl_module_getsrc) using dwarf_addrdie and dwarf_getsrc_die instead.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdw/ChangeLog | 6 ++++
libdw/dwarf_getsrc_die.c | 33 +++++++++-------------
tests/Makefile.am | 10 +++++--
tests/getsrc_die.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++
tests/run-getsrc-die.sh | 51 ++++++++++++++++++++++++++++++++++
5 files changed, 149 insertions(+), 23 deletions(-)
create mode 100644 tests/getsrc_die.c
create mode 100755 tests/run-getsrc-die.sh
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index abc2d71..bf64c2e 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,9 @@
+2014-12-24 Mark Wielaard <mjw(a)redhat.com>
+
+ * dwarf_getsrc_die.c (dwarf_getsrc_die): Return the last line record
+ smaller than or equal to addr, rather than returning immediately on
+ a match.
+
2014-12-18 Ulrich Drepper <drepper(a)gmail.com>
* Makefile.am: Suppress output of textrel_check command.
diff --git a/libdw/dwarf_getsrc_die.c b/libdw/dwarf_getsrc_die.c
index 1914cdf..fe06ca4 100644
--- a/libdw/dwarf_getsrc_die.c
+++ b/libdw/dwarf_getsrc_die.c
@@ -1,5 +1,5 @@
/* Find line information for address.
- Copyright (C) 2004, 2005 Red Hat, Inc.
+ Copyright (C) 2004, 2005, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2004.
@@ -45,33 +45,26 @@ dwarf_getsrc_die (Dwarf_Die *cudie, Dwarf_Addr addr)
return NULL;
/* The lines are sorted by address, so we can use binary search. */
- size_t l = 0, u = nlines;
+ size_t l = 0, u = nlines - 1;
while (l < u)
{
- size_t idx = (l + u) / 2;
- if (addr < lines->info[idx].addr)
- u = idx;
- else if (addr > lines->info[idx].addr || lines->info[idx].end_sequence)
- l = idx + 1;
+ size_t idx = u - (u -l) / 2;
+ Dwarf_Line *line = &lines->info[idx];
+ if (addr < line->addr)
+ u = idx - 1;
else
- return &lines->info[idx];
+ l = idx;
}
+ /* This is guaranteed for us by libdw read_srclines. */
if (nlines > 0)
assert (lines->info[nlines - 1].end_sequence);
- /* If none were equal, the closest one below is what we want. We
- never want the last one, because it's the end-sequence marker
- with an address at the high bound of the CU's code. If the debug
- information is faulty and no end-sequence marker is present, we
- still ignore it. */
- if (u > 0 && u < nlines && addr > lines->info[u - 1].addr)
- {
- while (lines->info[u - 1].end_sequence && u > 0)
- --u;
- if (u > 0)
- return &lines->info[u - 1];
- }
+ /* The last line which is less than or equal to addr is what we want,
+ except with an end_sequence which can only be strictly equal. */
+ Dwarf_Line *line = &lines->info[l];
+ if (line->addr == addr || (! line->end_sequence && line->addr < addr))
+ return &lines->info[l];
__libdw_seterrno (DWARF_E_ADDR_OUTOFRANGE);
return NULL;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 303f783..c364cc9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -50,7 +50,8 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
test-elf_cntl_gelf_getshdr dwflsyms dwfllines \
dwfl-report-elf-align varlocs backtrace backtrace-child \
backtrace-data backtrace-dwarf debuglink debugaltlink \
- buildid deleted deleted-lib.so aggregate_size vdsosyms
+ buildid deleted deleted-lib.so aggregate_size vdsosyms \
+ getsrc_die
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -111,7 +112,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-backtrace-core-aarch64.sh \
run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \
run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \
- run-linkmap-cut.sh run-aggregate-size.sh vdsosyms run-readelf-A.sh
+ run-linkmap-cut.sh run-aggregate-size.sh vdsosyms run-readelf-A.sh \
+ run-getsrc-die.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -280,7 +282,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
linkmap-cut.bz2 linkmap-cut.core.bz2 \
run-aggregate-size.sh testfile-sizes1.o.bz2 testfile-sizes2.o.bz2 \
testfile-sizes3.o.bz2 \
- run-readelf-A.sh testfileppc32attrs.o.bz2
+ run-readelf-A.sh testfileppc32attrs.o.bz2 \
+ run-getsrc-die.sh
if USE_VALGRIND
valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
@@ -420,6 +423,7 @@ deleted_lib_so_LDFLAGS = -shared -rdynamic
deleted_lib_so_CFLAGS = -fPIC -fasynchronous-unwind-tables
aggregate_size_LDADD = $(libdw) $(libelf)
vdsosyms_LDADD = $(libdw) $(libelf)
+getsrc_die_LDADD = $(libdw) $(libelf)
if GCOV
check: check-am coverage
diff --git a/tests/getsrc_die.c b/tests/getsrc_die.c
new file mode 100644
index 0000000..055aede
--- /dev/null
+++ b/tests/getsrc_die.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include ELFUTILS_HEADER(dw)
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+int
+main (int argc, char *argv[])
+{
+ /* file addr+ */
+ int fd = open (argv[1], O_RDONLY);
+ Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ);
+ if (dbg == NULL)
+ error (-1, 0, "dwarf_begin (%s): %s\n", argv[1], dwarf_errmsg (-1));
+
+ for (int i = 2; i < argc; i++)
+ {
+ Dwarf_Addr addr;
+ char *endptr;
+ Dwarf_Die cudie;
+ Dwarf_Line *line;
+
+ errno = 0;
+ addr = strtoull (argv[i], &endptr, 16);
+ if (errno != 0)
+ error (-1, errno, "Cannot parrse '%s'", argv[1]);
+
+ if (dwarf_addrdie (dbg, addr, &cudie) == NULL)
+ error (-1, 0, "dwarf_addrdie (%s): %s", argv[i], dwarf_errmsg (-1));
+
+ line = dwarf_getsrc_die (&cudie, addr);
+ if (line == NULL)
+ error (-1, 0, "dwarf_getsrc_die (%s): %s", argv[i], dwarf_errmsg (-1));
+
+ const char *f = dwarf_linesrc (line, NULL, NULL);
+ int l;
+ if (dwarf_lineno (line, &l) != 0)
+ l = 0;
+
+ printf ("%s:%d\n", f ?: "???", l);
+ }
+
+ dwarf_end (dbg);
+ close (fd);
+
+ return 0;
+}
diff --git a/tests/run-getsrc-die.sh b/tests/run-getsrc-die.sh
new file mode 100755
index 0000000..4da16e7
--- /dev/null
+++ b/tests/run-getsrc-die.sh
@@ -0,0 +1,51 @@
+#! /bin/sh
+# Copyright (C) 2014 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# See run-addr2line-test.sh run-addr2line-i-test.sh run-addr2line-i-lex-test.sh
+# Output/files/lines matched should equal what is done through addr2line
+# which uses dwfl_module_getsrc. This test uses dwarf_addrdie and
+# dwarf_getsrc_die
+testfiles testfile testfile-inlines testfile-lex-inlines
+
+testrun_compare ${abs_top_builddir}/tests/getsrc_die testfile 0x08048468 0x0804845c <<\EOF
+/home/drepper/gnu/new-bu/build/ttt/f.c:3
+/home/drepper/gnu/new-bu/build/ttt/b.c:4
+EOF
+
+testrun_compare ${abs_top_builddir}/tests/getsrc_die testfile-inlines 0x00000000000005a0 0x00000000000005a1 0x00000000000005b0 0x00000000000005b1 0x00000000000005c0 0x00000000000005d0 0x00000000000005e0 0x00000000000005e1 0x00000000000005f1 0x00000000000005f2 <<\EOF
+/tmp/x.cpp:5
+/tmp/x.cpp:6
+/tmp/x.cpp:10
+/tmp/x.cpp:11
+/tmp/x.cpp:5
+/tmp/x.cpp:10
+/tmp/x.cpp:5
+/tmp/x.cpp:10
+/tmp/x.cpp:10
+/tmp/x.cpp:5
+EOF
+
+testrun_compare ${abs_top_builddir}/tests/getsrc_die testfile-lex-inlines 0x0000000000000680 0x0000000000000681 0x0000000000000690 0x0000000000000691 <<\EOF
+/tmp/x.cpp:5
+/tmp/x.cpp:5
+/tmp/x.cpp:5
+/tmp/x.cpp:5
+EOF
+
+exit 0
--
2.1.0
8 years, 8 months
[PATCH] libelf: gelf_getphdr should check phdr index is valid.
by Mark Wielaard
elf_getphdrnum does checks the phdrnum makes sense. But gelf_getphdr
checked the given index against the "raw" e_phnum or internal
__elf_getphdrnum_rdlock result without checking. Extract the checking
code into a new internal __elf_getphdrnum_chk_rdlock function and
use that.
Found by afl-fuzz.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libelf/ChangeLog | 8 ++++++++
libelf/elf_getphdrnum.c | 46 ++++++++++++++++++++++++++--------------------
libelf/gelf_getphdr.c | 12 ++++--------
libelf/libelfP.h | 2 ++
4 files changed, 40 insertions(+), 28 deletions(-)
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index f2b3f21..2ca9509 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,11 @@
+2014-12-30 Mark Wielaard <mjw(a)redhat.com>
+
+ * elf_getphdrnum.c (__elf_getphdrnum_chk_rdlock): New function.
+ (elf_getphdrnum): Call __elf_getphdrnum_chk_rdlock.
+ * gelf_getphdr (gelf_getphdr): Call __elf_getphdrnum_chk_rdlock
+ and always check ndx against phnum.
+ * libelfP.h (__elf_getphdrnum_chk_rdlock): New internal function.
+
2014-12-25 Mark Wielaard <mjw(a)redhat.com>
* elf_begin.c (__libelf_next_arhdr_wrlock): ar_size cannot be
diff --git a/libelf/elf_getphdrnum.c b/libelf/elf_getphdrnum.c
index 63c27fb..f2fad87 100644
--- a/libelf/elf_getphdrnum.c
+++ b/libelf/elf_getphdrnum.c
@@ -80,23 +80,11 @@ __elf_getphdrnum_rdlock (elf, dst)
}
int
-elf_getphdrnum (elf, dst)
+__elf_getphdrnum_chk_rdlock (elf, dst)
Elf *elf;
size_t *dst;
{
- int result;
-
- if (elf == NULL)
- return -1;
-
- if (unlikely (elf->kind != ELF_K_ELF))
- {
- __libelf_seterrno (ELF_E_INVALID_HANDLE);
- return -1;
- }
-
- rwlock_rdlock (elf->lock);
- result = __elf_getphdrnum_rdlock (elf, dst);
+ int result = __elf_getphdrnum_rdlock (elf, dst);
/* Do some sanity checking to make sure phnum and phoff are consistent. */
Elf64_Off off = (elf->class == ELFCLASS32
@@ -105,14 +93,13 @@ elf_getphdrnum (elf, dst)
if (unlikely (off == 0))
{
*dst = 0;
- goto out;
+ return result;
}
if (unlikely (off >= elf->maximum_size))
{
__libelf_seterrno (ELF_E_INVALID_DATA);
- result = -1;
- goto out;
+ return -1;
}
/* Check for too many sections. */
@@ -121,15 +108,34 @@ elf_getphdrnum (elf, dst)
if (unlikely (*dst > SIZE_MAX / phdr_size))
{
__libelf_seterrno (ELF_E_INVALID_DATA);
- result = -1;
- goto out;
+ return -1;
}
/* Truncated file? Don't return more than can be indexed. */
if (unlikely (elf->maximum_size - off < *dst * phdr_size))
*dst = (elf->maximum_size - off) / phdr_size;
-out:
+ return result;
+}
+
+int
+elf_getphdrnum (elf, dst)
+ Elf *elf;
+ size_t *dst;
+{
+ int result;
+
+ if (elf == NULL)
+ return -1;
+
+ if (unlikely (elf->kind != ELF_K_ELF))
+ {
+ __libelf_seterrno (ELF_E_INVALID_HANDLE);
+ return -1;
+ }
+
+ rwlock_rdlock (elf->lock);
+ result = __elf_getphdrnum_chk_rdlock (elf, dst);
rwlock_unlock (elf->lock);
return result;
diff --git a/libelf/gelf_getphdr.c b/libelf/gelf_getphdr.c
index 3bf7123..1a6ee62 100644
--- a/libelf/gelf_getphdr.c
+++ b/libelf/gelf_getphdr.c
@@ -80,10 +80,8 @@ gelf_getphdr (elf, ndx, dst)
/* Test whether the index is ok. */
size_t phnum;
- if (ndx >= elf->state.elf32.ehdr->e_phnum
- && (elf->state.elf32.ehdr->e_phnum != PN_XNUM
- || __elf_getphdrnum_rdlock (elf, &phnum) != 0
- || (size_t) ndx >= phnum))
+ if (__elf_getphdrnum_chk_rdlock (elf, &phnum) != 0
+ || (size_t) ndx >= phnum)
{
__libelf_seterrno (ELF_E_INVALID_INDEX);
goto out;
@@ -122,10 +120,8 @@ gelf_getphdr (elf, ndx, dst)
/* Test whether the index is ok. */
size_t phnum;
- if (ndx >= elf->state.elf64.ehdr->e_phnum
- && (elf->state.elf64.ehdr->e_phnum != PN_XNUM
- || __elf_getphdrnum_rdlock (elf, &phnum) != 0
- || (size_t) ndx >= phnum))
+ if (__elf_getphdrnum_chk_rdlock (elf, &phnum) != 0
+ || (size_t) ndx >= phnum)
{
__libelf_seterrno (ELF_E_INVALID_INDEX);
goto out;
diff --git a/libelf/libelfP.h b/libelf/libelfP.h
index 52cf745..3b24e75 100644
--- a/libelf/libelfP.h
+++ b/libelf/libelfP.h
@@ -511,6 +511,8 @@ extern Elf_Scn *__elf64_offscn_internal (Elf *__elf, Elf64_Off __offset)
attribute_hidden;
extern int __elf_getphdrnum_rdlock (Elf *__elf, size_t *__dst)
internal_function;
+extern int __elf_getphdrnum_chk_rdlock (Elf *__elf, size_t *__dst)
+ internal_function;
extern int __elf_getshdrnum_rdlock (Elf *__elf, size_t *__dst)
internal_function;
extern int __elf_getshdrstrndx_internal (Elf *__elf, size_t *__dst)
--
2.1.0
8 years, 8 months
[PATCH] backends: Check sh_entsize is not zero in ppc_symbol.c (find_dyn_got).
by Mark Wielaard
Found with afl-fuzz.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
backends/ChangeLog | 4 ++++
backends/ppc_symbol.c | 3 ++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/backends/ChangeLog b/backends/ChangeLog
index e800d16..57e1b8e 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,7 @@
+2014-12-30 Mark Wielaard <mjw(a)redhat.com>
+
+ * ppc_symbol.c (find_dyn_got): Check sh_entsize is not zero.
+
2014-12-18 Ulrich Drepper <drepper(a)gmail.com>
* Makefile.am: Suppress output of textrel_check command.
diff --git a/backends/ppc_symbol.c b/backends/ppc_symbol.c
index c17ab37..733114b 100644
--- a/backends/ppc_symbol.c
+++ b/backends/ppc_symbol.c
@@ -98,7 +98,8 @@ find_dyn_got (Elf *elf, GElf_Addr *addr)
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Elf_Data *data = elf_getdata (scn, NULL);
- if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC && data != NULL)
+ if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC && data != NULL
+ && shdr->sh_entsize != 0)
for (unsigned int j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j)
{
GElf_Dyn dyn_mem;
--
2.1.0
8 years, 8 months
[PATCH] elflint: Add various low-level checks.
by Mark Wielaard
elflint could crash or hang because it didn't check some low level issues.
https://bugzilla.redhat.com/show_bug.cgi?id=1170810
Reported-by: Alexander Cherepanov <cherepan(a)mccme.ru>
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 23 ++++++
src/elflint.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 208 insertions(+), 48 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 66d6270..f8fd947 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,26 @@
+2014-12-30 Mark Wielaard <mjw(a)redhat.com>
+
+ * elflint.c (check_scn_group): Check d_buf and name are not NULL.
+ (is_rel_dyn): Check d is not NULL. Check shdr->sh_entsize is not
+ zero.
+ (check_dynamic): Check strshdr is not NULL. Check d_tag is positive.
+ (check_symtab_shndx): Check symshdr and data->d_buf are not NULL.
+ Check shdr and symshdr sh_entsize are not zero.
+ (check_gnu_hash): Make sure maskidx is smaller than bitmask_words.
+ Check symshdr->sh_entsize is not zero. Check data->d_buf is not
+ NULL.
+ (compare_hash_gnu_hash): Check sections d_buf are not NULL.
+ Check section data is large enough. Use gnu_symbias.
+ (check_group): Check section val is valid.
+ (has_copy_reloc): Check sh_entsize is not zero.
+ (check_versym): Likewise.
+ (unknown_dependency_p): Likewise.
+ (check_verneed): Break on invalid ref or offset. Don't assert.
+ (check_attributes): Make sure d_buf is not NULL.
+ (check_note): Likewise.
+ (check_note_section): Likewise.
+ (check_program_header): Make sure section name is not NULL.
+
2014-12-26 Mark Wielaard <mjw(a)redhat.com>
* strings.c (read_elf): Produce error when section data falls outside
diff --git a/src/elflint.c b/src/elflint.c
index 7e73253..6bb49a9 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -554,7 +554,8 @@ check_scn_group (Ebl *ebl, int idx)
continue;
Elf_Data *data = elf_getdata (scn, NULL);
- if (data == NULL || data->d_size < sizeof (Elf32_Word))
+ if (data == NULL || data->d_buf == NULL
+ || data->d_size < sizeof (Elf32_Word))
/* Cannot check the section. */
continue;
@@ -692,8 +693,8 @@ section [%2d] '%s': symbol %zu: invalid name value\n"),
else
{
name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name);
- assert (name != NULL
- || strshdr->sh_type != SHT_STRTAB);
+ if (name == NULL)
+ name = "";
}
if (sym->st_shndx == SHN_XINDEX)
@@ -1061,12 +1062,17 @@ is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr,
if (rcshdr == NULL)
break;
- if (rcshdr->sh_type == SHT_DYNAMIC && rcshdr->sh_entsize)
+ if (rcshdr->sh_type == SHT_DYNAMIC && rcshdr->sh_entsize != 0)
{
/* Found the dynamic section. Look through it. */
Elf_Data *d = elf_getdata (scn, NULL);
size_t cnt;
+ if (d == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get section data.\n"),
+ idx, section_name (ebl, idx));
+
for (cnt = 1; cnt < rcshdr->sh_size / rcshdr->sh_entsize; ++cnt)
{
GElf_Dyn dyn_mem;
@@ -1099,7 +1105,7 @@ section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
relative. */
Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf,
idx), NULL);
- if (reldata != NULL)
+ if (reldata != NULL && shdr->sh_entsize != 0)
for (size_t inner = 0;
inner < shdr->sh_size / shdr->sh_entsize;
++inner)
@@ -1141,7 +1147,8 @@ section [%2d] '%s': DT_RELACOUNT used for this REL section\n"),
/* Does the number specified number of relative
relocations exceed the total number of
relocations? */
- if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize)
+ if (shdr->sh_entsize != 0
+ && dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize)
ERROR (gettext ("\
section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
idx, section_name (ebl, idx),
@@ -1151,7 +1158,7 @@ section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
relative. */
Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf,
idx), NULL);
- if (reldata != NULL)
+ if (reldata != NULL && shdr->sh_entsize != 0)
for (size_t inner = 0;
inner < shdr->sh_size / shdr->sh_entsize;
++inner)
@@ -1284,7 +1291,7 @@ section [%2d] '%s': section entry size does not match ElfXX_Rel\n"),
GElf_Shdr *dynshdr = gelf_getshdr (dynscn, &dynshdr_mem);
Elf_Data *dyndata = elf_getdata (dynscn, NULL);
if (dynshdr != NULL && dynshdr->sh_type == SHT_DYNAMIC
- && dyndata != NULL)
+ && dyndata != NULL && dynshdr->sh_entsize != 0)
for (size_t j = 0; j < dynshdr->sh_size / dynshdr->sh_entsize; ++j)
{
GElf_Dyn dyn_mem;
@@ -1622,6 +1629,13 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"),
shdr->sh_link, section_name (ebl, shdr->sh_link),
idx, section_name (ebl, idx));
+ else if (strshdr == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d]: referenced as string table for section [%2d] '%s' but section link value is invalid\n"),
+ shdr->sh_link, idx, section_name (ebl, idx));
+ return;
+ }
size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT);
if (shdr->sh_entsize != sh_entsize)
@@ -1685,10 +1699,10 @@ section [%2d] '%s': entry %zu: level 2 tag %s used\n"),
has_dt[dyn->d_tag] = true;
}
- else if (dyn->d_tag <= DT_VALRNGHI
+ else if (dyn->d_tag >= 0 && dyn->d_tag <= DT_VALRNGHI
&& DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM)
has_val_dt[DT_VALTAGIDX (dyn->d_tag)] = true;
- else if (dyn->d_tag <= DT_ADDRRNGHI
+ else if (dyn->d_tag >= 0 && dyn->d_tag <= DT_ADDRRNGHI
&& DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM)
has_addr_dt[DT_ADDRTAGIDX (dyn->d_tag)] = true;
@@ -1896,6 +1910,10 @@ section [%2d] '%s': only relocatable files can have extended section index\n"),
ERROR (gettext ("\
section [%2d] '%s': extended section index section not for symbol table\n"),
idx, section_name (ebl, idx));
+ else if (symshdr == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': sh_link extended section index [%2d] is invalid\n"),
+ idx, section_name (ebl, idx), shdr->sh_link);
Elf_Data *symdata = elf_getdata (symscn, NULL);
if (symdata == NULL)
ERROR (gettext ("cannot get data for symbol section\n"));
@@ -1906,8 +1924,8 @@ section [%2d] '%s': entry size does not match Elf32_Word\n"),
idx, section_name (ebl, idx));
if (symshdr != NULL
- && shdr->sh_entsize
- && symshdr->sh_entsize
+ && shdr->sh_entsize != 0
+ && symshdr->sh_entsize != 0
&& (shdr->sh_size / shdr->sh_entsize
< symshdr->sh_size / symshdr->sh_entsize))
ERROR (gettext ("\
@@ -1934,7 +1952,7 @@ section [%2d] '%s': extended section index in section [%2zu] '%s' refers to same
}
Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
- if (data == NULL)
+ if (data == NULL || data->d_buf == NULL)
{
ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
idx, section_name (ebl, idx));
@@ -2033,7 +2051,7 @@ section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
size_t maxidx = nchain;
- if (symshdr != NULL)
+ if (symshdr != NULL && shdr->sh_entsize != 0)
{
size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
@@ -2104,7 +2122,7 @@ section [%2d] '%s': 2nd hash function shift too big: %u\n"),
size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (4 + bitmask_words
+ nbuckets);
- if (symshdr != NULL)
+ if (symshdr != NULL && symshdr->sh_entsize != 0)
maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize);
/* We need the symbol section data. */
@@ -2155,8 +2173,10 @@ section [%2d] '%s': symbol %u referenced in chain for bucket %zu is undefined\n"
idx, section_name (ebl, idx), symidx,
cnt - (4 + bitmask_words));
- const char *symname = elf_strptr (ebl->elf, symshdr->sh_link,
- sym->st_name);
+ const char *symname = (sym != NULL
+ ? elf_strptr (ebl->elf, symshdr->sh_link,
+ sym->st_name)
+ : NULL);
if (symname != NULL)
{
Elf32_Word hval = elf_gnu_hash (symname);
@@ -2168,6 +2188,14 @@ section [%2d] '%s': hash value for symbol %u in chain for bucket %zu wrong\n"),
/* Set the bits in the bitmask. */
size_t maskidx = (hval / classbits) & bitmask_idxmask;
+ if (maskidx >= bitmask_words)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': mask index for symbol %u in chain for bucket %zu wrong\n"),
+ idx, section_name (ebl, idx), symidx,
+ cnt - (4 + bitmask_words));
+ return;
+ }
if (classbits == 32)
{
collected.p32[maskidx]
@@ -2195,7 +2223,7 @@ section [%2d] '%s': hash value for symbol %u in chain for bucket %zu wrong\n"),
ERROR (gettext ("\
section [%2d] '%s': hash chain for bucket %zu out of bounds\n"),
idx, section_name (ebl, idx), cnt - (4 + bitmask_words));
- else if (symshdr != NULL
+ else if (symshdr != NULL && symshdr->sh_entsize != 0
&& symidx > symshdr->sh_size / symshdr->sh_entsize)
ERROR (gettext ("\
section [%2d] '%s': symbol reference in chain for bucket %zu out of bounds\n"),
@@ -2223,7 +2251,7 @@ section [%2d] '%s': relocatable files cannot have hash tables\n"),
}
Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
- if (data == NULL)
+ if (data == NULL || data->d_buf == NULL)
{
ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
idx, section_name (ebl, idx));
@@ -2237,6 +2265,10 @@ section [%2d] '%s': relocatable files cannot have hash tables\n"),
ERROR (gettext ("\
section [%2d] '%s': hash table not for dynamic symbol table\n"),
idx, section_name (ebl, idx));
+ else if (symshdr == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': invalid sh_link symbol table section index [%2d]\n"),
+ idx, section_name (ebl, idx), shdr->sh_link);
if (shdr->sh_entsize != (tag == SHT_GNU_HASH
? (gelf_getclass (ebl->elf) == ELFCLASS32
@@ -2292,7 +2324,8 @@ compare_hash_gnu_hash (Ebl *ebl, GElf_Ehdr *ehdr, size_t hash_idx,
GElf_Shdr *gnu_hash_shdr = gelf_getshdr (gnu_hash_scn, &gnu_hash_shdr_mem);
if (hash_shdr == NULL || gnu_hash_shdr == NULL
- || hash_data == NULL || gnu_hash_data == NULL)
+ || hash_data == NULL || hash_data->d_buf == NULL
+ || gnu_hash_data == NULL || gnu_hash_data->d_buf == NULL)
/* None of these pointers should be NULL since we used the
sections already. We are careful nonetheless. */
return;
@@ -2313,27 +2346,70 @@ sh_link in hash sections [%2zu] '%s' and [%2zu] '%s' not identical\n"),
GElf_Shdr sym_shdr_mem;
GElf_Shdr *sym_shdr = gelf_getshdr (sym_scn, &sym_shdr_mem);
- if (sym_data == NULL || sym_shdr == NULL)
+ if (sym_data == NULL || sym_data->d_buf == NULL
+ || sym_shdr == NULL || sym_shdr->sh_entsize == 0)
return;
- int nentries = sym_shdr->sh_size / sym_shdr->sh_entsize;
+ const char *hash_name;
+ const char *gnu_hash_name;
+ hash_name = elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name);
+ gnu_hash_name = elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name);
+
+ if (gnu_hash_data->d_size < 4 * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' does not contain enough data\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
+
+ uint32_t nentries = sym_shdr->sh_size / sym_shdr->sh_entsize;
char *used = alloca (nentries);
memset (used, '\0', nentries);
/* First go over the GNU_HASH table and mark the entries as used. */
const Elf32_Word *gnu_hasharr = (Elf32_Word *) gnu_hash_data->d_buf;
Elf32_Word gnu_nbucket = gnu_hasharr[0];
+ Elf32_Word gnu_symbias = gnu_hasharr[1];
const int bitmap_factor = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 1 : 2;
const Elf32_Word *gnu_bucket = (gnu_hasharr
+ (4 + gnu_hasharr[2] * bitmap_factor));
- const Elf32_Word *gnu_chain = gnu_bucket + gnu_hasharr[0] - gnu_hasharr[1];
+ const Elf32_Word *gnu_chain = gnu_bucket + gnu_hasharr[0];
+
+ if (gnu_hasharr[2] == 0)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' has zero bit mask words\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
+
+ uint64_t used_buf = ((4ULL + gnu_hasharr[2] * bitmap_factor + gnu_nbucket)
+ * sizeof (Elf32_Word));
+ uint32_t max_nsyms = (gnu_hash_data->d_size - used_buf) / sizeof (Elf32_Word);
+ if (used_buf > gnu_hash_data->d_size)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' uses too much data\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
for (Elf32_Word cnt = 0; cnt < gnu_nbucket; ++cnt)
{
- Elf32_Word symidx = gnu_bucket[cnt];
+ Elf32_Word symidx = gnu_bucket[cnt] - gnu_symbias;;
if (symidx != STN_UNDEF)
do
- used[symidx] |= 1;
+ {
+ if (symidx >= max_nsyms || symidx >= nentries)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' invalid symbol index\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
+ used[symidx] |= 1;
+ }
while ((gnu_chain[symidx++] & 1u) == 0);
}
@@ -2342,37 +2418,82 @@ sh_link in hash sections [%2zu] '%s' and [%2zu] '%s' not identical\n"),
if (hash_shdr->sh_entsize == sizeof (Elf32_Word))
{
const Elf32_Word *hasharr = (Elf32_Word *) hash_data->d_buf;
+ if (hash_data->d_size < 2 * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' does not contain enough data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
Elf32_Word nbucket = hasharr[0];
+ Elf32_Word nchain = hasharr[1];
+ uint64_t hash_used = (2ULL + nchain + nbucket) * sizeof (Elf32_Word);
+ if (hash_used > hash_data->d_size)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' uses too much data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
const Elf32_Word *bucket = &hasharr[2];
const Elf32_Word *chain = &hasharr[2 + nbucket];
for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
{
Elf32_Word symidx = bucket[cnt];
- while (symidx != STN_UNDEF)
+ while (symidx != STN_UNDEF && symidx < nentries && symidx < nchain)
{
used[symidx] |= 2;
symidx = chain[symidx];
}
}
}
- else
+ else if (hash_shdr->sh_entsize == sizeof (Elf64_Word))
{
const Elf64_Xword *hasharr = (Elf64_Xword *) hash_data->d_buf;
+ if (hash_data->d_size < 2 * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' does not contain enough data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
Elf64_Xword nbucket = hasharr[0];
+ Elf64_Xword nchain = hasharr[1];
+ uint64_t maxwords = hash_data->d_size / sizeof (Elf64_Xword);
+ if (maxwords < 2
+ || maxwords - 2 < nbucket
+ || maxwords - 2 - nbucket < nchain)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' uses too much data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
const Elf64_Xword *bucket = &hasharr[2];
const Elf64_Xword *chain = &hasharr[2 + nbucket];
for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt)
{
Elf64_Xword symidx = bucket[cnt];
- while (symidx != STN_UNDEF)
+ while (symidx != STN_UNDEF && symidx < nentries && symidx < nchain)
{
used[symidx] |= 2;
symidx = chain[symidx];
}
}
}
+ else
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' invalid sh_entsize\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
/* Now see which entries are not set in one or both hash tables
(unless the symbol is undefined in which case it can be omitted
@@ -2385,7 +2506,7 @@ sh_link in hash sections [%2zu] '%s' and [%2zu] '%s' not identical\n"),
ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"),
hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name));
- for (int cnt = 1; cnt < nentries; ++cnt)
+ for (uint32_t cnt = 1; cnt < nentries; ++cnt)
if (used[cnt] != 0 && used[cnt] != 3)
{
if (used[cnt] == 1)
@@ -2474,6 +2595,10 @@ section [%2d] '%s': invalid symbol index in sh_info\n"),
ERROR (gettext ("\
section [%2d] '%s': cannot get symbol for signature\n"),
idx, section_name (ebl, idx));
+ else if (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name) == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get symbol name for signature\n"),
+ idx, section_name (ebl, idx));
else if (strcmp (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name),
"") == 0)
ERROR (gettext ("\
@@ -2487,7 +2612,7 @@ section [%2d] '%s': signature symbol cannot be empty string\n"),
}
Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
- if (data == NULL)
+ if (data == NULL || data->d_buf == NULL)
ERROR (gettext ("section [%2d] '%s': cannot get data: %s\n"),
idx, section_name (ebl, idx), elf_errmsg (-1));
else
@@ -2563,7 +2688,7 @@ section [%2d] '%s': element %Zu references section [%2d] '%s' without SHF_GROUP
val, section_name (ebl, val));
}
- if (++scnref[val] == 2)
+ if (val < shnum && ++scnref[val] == 2)
ERROR (gettext ("\
section [%2d] '%s' is contained in more than one section group\n"),
val, section_name (ebl, val));
@@ -2648,7 +2773,7 @@ has_copy_reloc (Ebl *ebl, unsigned int symscnndx, unsigned int symndx)
return 0;
Elf_Data *data = elf_getdata (scn, NULL);
- if (data == NULL)
+ if (data == NULL || shdr->sh_entsize == 0)
return 0;
if (shdr->sh_type == SHT_REL)
@@ -2762,7 +2887,7 @@ section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic s
/* The number of elements in the version symbol table must be the
same as the number of symbols. */
- if (shdr->sh_entsize && symshdr->sh_entsize
+ if (shdr->sh_entsize != 0 && symshdr->sh_entsize != 0
&& (shdr->sh_size / shdr->sh_entsize
!= symshdr->sh_size / symshdr->sh_entsize))
ERROR (gettext ("\
@@ -2771,7 +2896,7 @@ section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\
shdr->sh_link, section_name (ebl, shdr->sh_link));
Elf_Data *symdata = elf_getdata (symscn, NULL);
- if (symdata == NULL)
+ if (symdata == NULL || shdr->sh_entsize == 0)
/* The error has already been reported. */
return;
@@ -2862,7 +2987,8 @@ unknown_dependency_p (Elf *elf, const char *fname)
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Elf_Data *data = elf_getdata (scn, NULL);
- if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC && data != NULL)
+ if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC
+ && data != NULL && shdr->sh_entsize != 0)
for (size_t j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j)
{
GElf_Dyn dyn_mem;
@@ -2958,9 +3084,12 @@ section [%2d] '%s': auxiliary entry %d of entry %d has unknown flag\n"),
const char *verstr = elf_strptr (ebl->elf, shdr->sh_link,
aux->vna_name);
if (verstr == NULL)
- ERROR (gettext ("\
+ {
+ ERROR (gettext ("\
section [%2d] '%s': auxiliary entry %d of entry %d has invalid name reference\n"),
- idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+ break;
+ }
else
{
GElf_Word hashval = elf_hash (verstr);
@@ -2974,7 +3103,6 @@ section [%2d] '%s': auxiliary entry %d of entry %d has wrong hash value: %#x, ex
ver_need);
if (unlikely (res !=0))
{
- assert (res > 0);
ERROR (gettext ("\
section [%2d] '%s': auxiliary entry %d of entry %d has duplicate version name '%s'\n"),
idx, section_name (ebl, idx), need->vn_cnt - cnt2,
@@ -3002,9 +3130,12 @@ section [%2d] '%s': auxiliary entry %d of entry %d has wrong next field\n"),
if ((need->vn_next != 0 || cnt > 0)
&& offset < auxoffset)
- ERROR (gettext ("\
+ {
+ ERROR (gettext ("\
section [%2d] '%s': entry %d has invalid offset to next entry\n"),
- idx, section_name (ebl, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
}
}
@@ -3109,7 +3240,6 @@ section [%2d] '%s': entry %d has wrong hash value: %#x, expected %#x\n"),
int res = add_version (NULL, name, def->vd_ndx, ver_def);
if (unlikely (res !=0))
{
- assert (res > 0);
ERROR (gettext ("\
section [%2d] '%s': entry %d has duplicate version name '%s'\n"),
idx, section_name (ebl, idx), cnt, name);
@@ -3129,9 +3259,12 @@ section [%2d] '%s': entry %d has duplicate version name '%s'\n"),
name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
if (name == NULL)
- ERROR (gettext ("\
+ {
+ ERROR (gettext ("\
section [%2d] '%s': entry %d has invalid name reference in auxiliary data\n"),
- idx, section_name (ebl, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
else
{
newname = alloca (sizeof (*newname));
@@ -3160,9 +3293,12 @@ section [%2d] '%s': entry %d has wrong next field in auxiliary data\n"),
if ((def->vd_next != 0 || cnt > 0)
&& offset < auxoffset)
- ERROR (gettext ("\
+ {
+ ERROR (gettext ("\
section [%2d] '%s': entry %d has invalid offset to next entry\n"),
- idx, section_name (ebl, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
}
if (!has_base)
@@ -3201,7 +3337,7 @@ check_attributes (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
}
Elf_Data *data = elf_rawdata (elf_getscn (ebl->elf, idx), NULL);
- if (data == NULL || data->d_size == 0)
+ if (data == NULL || data->d_size == 0 || data->d_buf == NULL)
{
ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
idx, section_name (ebl, idx));
@@ -4084,7 +4220,7 @@ phdr[%d]: no note entries defined for the type of file\n"),
Elf_Data *data = elf_getdata_rawchunk (ebl->elf,
phdr->p_offset, phdr->p_filesz,
ELF_T_NHDR);
- if (data != NULL)
+ if (data != NULL && data->d_buf != NULL)
notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset);
if (notes_size == 0)
@@ -4103,7 +4239,7 @@ check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
return;
Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
- if (data == NULL)
+ if (data == NULL || data->d_buf == NULL)
{
ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
idx, section_name (ebl, idx));
@@ -4296,6 +4432,7 @@ program header offset in ELF header and PHDR entry do not match"));
if (shdr != NULL
&& shdr->sh_type == (is_debuginfo
? SHT_NOBITS : SHT_PROGBITS)
+ && elf_strptr (ebl->elf, shstrndx, shdr->sh_name) != NULL
&& ! strcmp (".eh_frame_hdr",
elf_strptr (ebl->elf, shstrndx, shdr->sh_name)))
{
--
2.1.0
8 years, 8 months
[PATCH] libdw: Check sibling attribute offset still falls inside CU data.
by Mark Wielaard
Found with afl-fuzz.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdw/ChangeLog | 5 +++++
libdw/dwarf_siblingof.c | 7 +++++++
2 files changed, 12 insertions(+)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index bf64c2e..a04aa62 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2014-12-27 Mark Wielaard <mjw(a)redhat.com>
+
+ * dwarf_siblingof.c (dwarf_siblingof): Check sibling attribute offset
+ still falls inside CU data.
+
2014-12-24 Mark Wielaard <mjw(a)redhat.com>
* dwarf_getsrc_die.c (dwarf_getsrc_die): Return the last line record
diff --git a/libdw/dwarf_siblingof.c b/libdw/dwarf_siblingof.c
index 27830ea..f8241b3 100644
--- a/libdw/dwarf_siblingof.c
+++ b/libdw/dwarf_siblingof.c
@@ -79,6 +79,13 @@ dwarf_siblingof (die, result)
/* Something went wrong. */
return -1;
+ size_t size = sibattr.cu->endp - sibattr.cu->startp;
+ if (unlikely (offset >= size))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
+
/* Compute the next address. */
addr = sibattr.cu->startp + offset;
}
--
2.1.0
8 years, 8 months
[PATCH] strings: Produce error when section data falls outside file.
by Mark Wielaard
https://bugzilla.redhat.com/show_bug.cgi?id=1170810
Reported-by: Alexander Cherepanov <cherepan(a)mccme.ru>
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 5 +++++
src/strings.c | 17 +++++++++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 904b3c9..66d6270 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,10 @@
2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+ * strings.c (read_elf): Produce error when section data falls outside
+ file.
+
+2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+
* nm.c (show_symbols): Guard against divide by zero in error check.
Add section index number in error message.
diff --git a/src/strings.c b/src/strings.c
index f60e4b4..d1eb7b2 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -725,8 +725,21 @@ read_elf (Elf *elf, int fd, const char *fname, off64_t fdlen)
actually have content. */
if (shdr != NULL && shdr->sh_type != SHT_NOBITS
&& (shdr->sh_flags & SHF_ALLOC) != 0)
- result |= read_block (fd, fname, fdlen, shdr->sh_offset,
- shdr->sh_offset + shdr->sh_size);
+ {
+ if (shdr->sh_offset > fdlen
+ || fdlen - shdr->sh_offset < shdr->sh_size)
+ {
+ size_t strndx = 0;
+ elf_getshdrstrndx (elf, &strndx);
+ error (0, 0,
+ gettext ("Skipping section %zd '%s' data outside file"),
+ elf_ndxscn (scn), elf_strptr (elf, strndx, shdr->sh_name));
+ result = 1;
+ }
+ else
+ result |= read_block (fd, fname, fdlen, shdr->sh_offset,
+ shdr->sh_offset + shdr->sh_size);
+ }
}
while ((scn = elf_nextscn (elf, scn)) != NULL);
--
2.1.0
8 years, 8 months
[PATCH] nm: Guard against divide by zero in error check.
by Mark Wielaard
Also add section index number in error message.
https://bugzilla.redhat.com/show_bug.cgi?id=1170810
Reported-by: Alexander Cherepanov <cherepan(a)mccme.ru>
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 5 +++++
src/nm.c | 13 ++++++++-----
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 9da2853..904b3c9 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,10 @@
2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+ * nm.c (show_symbols): Guard against divide by zero in error check.
+ Add section index number in error message.
+
+2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+
* nm.c (handle_ar): Skip over /SYM64/ entries.
2014-12-26 Mark Wielaard <mjw(a)redhat.com>
diff --git a/src/nm.c b/src/nm.c
index be91459..915153f 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -1169,14 +1169,17 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
size_t entsize = shdr->sh_entsize;
/* Consistency checks. */
- if (entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, ehdr->e_version))
+ if (entsize == 0
+ || entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, ehdr->e_version))
error (0, 0,
- gettext ("%s: entry size in section `%s' is not what we expect"),
- fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+ gettext ("%s: entry size in section %zd `%s' is not what we expect"),
+ fullname, elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
else if (size % entsize != 0)
error (0, 0,
- gettext ("%s: size of section `%s' is not multiple of entry size"),
- fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+ gettext ("%s: size of section %zd `%s' is not multiple of entry size"),
+ fullname, elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
/* Compute number of entries. Handle buggy entsize values. */
size_t nentries = size / (entsize ?: 1);
--
2.1.0
8 years, 8 months
[PATCH] nm: Handle/Skip /SYM64/ index entries.
by Mark Wielaard
Add a testcase to show eu-nm now handles archives with 64-bit symbol table.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 4 ++++
src/nm.c | 3 ++-
tests/ChangeLog | 4 ++++
tests/run-test-archive64.sh | 14 +++++++++++++-
4 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 18a038d..9da2853 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,9 @@
2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+ * nm.c (handle_ar): Skip over /SYM64/ entries.
+
+2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+
* nm.c (handle_ar): Break on arsym with invalid offset.
2014-12-20 Mark Wielaard <mjw(a)redhat.com>
diff --git a/src/nm.c b/src/nm.c
index 6a9f8e1..be91459 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -493,7 +493,8 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
/* Skip over the index entries. */
if (strcmp (arhdr->ar_name, "/") != 0
- && strcmp (arhdr->ar_name, "//") != 0)
+ && strcmp (arhdr->ar_name, "//") != 0
+ && strcmp (arhdr->ar_name, "/SYM64/") != 0)
{
if (elf_kind (subelf) == ELF_K_ELF)
result |= handle_elf (subelf, new_prefix, arhdr->ar_name,
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 59048f6..6dd553b 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+
+ * run-test-archive64.sh: Add nm test.
+
2014-12-19 Mark Wielaard <mjw(a)redhat.com>
* run-deleted.sh: Don't check libfunc on ppc64.
diff --git a/tests/run-test-archive64.sh b/tests/run-test-archive64.sh
index 43df16e..2d8c21d 100755
--- a/tests/run-test-archive64.sh
+++ b/tests/run-test-archive64.sh
@@ -1,5 +1,5 @@
#! /bin/sh
-# Copyright (C) 2012 Red Hat, Inc.
+# Copyright (C) 2012, 2014 Red Hat, Inc.
# This file is part of elfutils.
#
# This file is free software; you can redistribute it and/or modify
@@ -40,4 +40,16 @@ Archive member 'ccc.o' contains:
ccc3
EOF
+testrun_compare ${abs_top_builddir}/src/nm -P -g testarchive64.a <<\EOF
+testarchive64.a[aaa.o]:
+aaa T 0000000000000000 0000000000000016
+testarchive64.a[bbb.o]:
+bbb T 0000000000000000 0000000000000016
+bbb2 T 0000000000000018 0000000000000016
+testarchive64.a[ccc.o]:
+ccc T 0000000000000000 0000000000000016
+ccc2 T 0000000000000018 0000000000000016
+ccc3 T 0000000000000030 0000000000000016
+EOF
+
exit 0
--
2.1.0
8 years, 8 months
[PATCH] nm: Stop processing ar members on first invalid offset.
by Mark Wielaard
Otherwise we will keep looping on that same invalid entry.
https://bugzilla.redhat.com/show_bug.cgi?id=1170810
Reported-by: Alexander Cherepanov <cherepan(a)mccme.ru>
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 4 ++++
src/nm.c | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 7203dd9..18a038d 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,7 @@
+2014-12-26 Mark Wielaard <mjw(a)redhat.com>
+
+ * nm.c (handle_ar): Break on arsym with invalid offset.
+
2014-12-20 Mark Wielaard <mjw(a)redhat.com>
* readelf.c (print_debug_macinfo_section): Mark cus sentinel files
diff --git a/src/nm.c b/src/nm.c
index 4f2e0e7..6a9f8e1 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -468,7 +468,7 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
{
error (0, 0, gettext ("invalid offset %zu for symbol %s"),
arsym->as_off, arsym->as_name);
- continue;
+ break;
}
printf (gettext ("%s in %s\n"), arsym->as_name, arhdr->ar_name);
--
2.1.0
8 years, 8 months