[PATCH] readelf: print_attributes (-A) robustify and handle non-gnu attributes.
by Mark Wielaard
print_attributes wasn't robust against empty or broken attribute sections.
It also only handled GNU attributes. But the arm backend contains some
none-GNU attributes. The difference is in how to handle the tag arguments.
Adds a new test run-readelf-A.sh for both gnu (ppc32) and non-gnu (arm)
attributes.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 7 +++++
src/readelf.c | 44 +++++++++++++++++++++++-----
tests/ChangeLog | 7 +++++
tests/Makefile.am | 5 ++--
tests/run-readelf-A.sh | 65 +++++++++++++++++++++++++++++++++++++++++
tests/testfileppc32attrs.o.bz2 | Bin 0 -> 228 bytes
6 files changed, 118 insertions(+), 10 deletions(-)
create mode 100755 tests/run-readelf-A.sh
create mode 100644 tests/testfileppc32attrs.o.bz2
diff --git a/src/ChangeLog b/src/ChangeLog
index bab948a..2947f39 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,10 @@
+2014-11-21 Mark Wielaard <mjw(a)redhat.com>
+
+ * readelf.c (print_attributes): Guard against empty section.
+ Document attribute format. Break when vendor name isn't terminated.
+ Add overflow check for subsection_len. Handle both gnu and non-gnu
+ attribute tags.
+
2014-11-18 Mark Wielaard <mjw(a)redhat.com>
* readelf.c (print_cfa_program): Fix sanity check of DW_FORM_block
diff --git a/src/readelf.c b/src/readelf.c
index 08de798..65bf61d 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -3314,11 +3314,12 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
shdr->sh_size, shdr->sh_offset);
Elf_Data *data = elf_rawdata (scn, NULL);
- if (data == NULL)
+ if (data == NULL || data->d_size == 0)
return;
const unsigned char *p = data->d_buf;
+ /* There is only one 'version', A. */
if (unlikely (*p++ != 'A'))
return;
@@ -3329,8 +3330,10 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
return (const unsigned char *) data->d_buf + data->d_size - p;
}
+ /* Loop over the sections. */
while (left () >= 4)
{
+ /* Section length. */
uint32_t len;
memcpy (&len, p, sizeof len);
@@ -3340,19 +3343,23 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
if (unlikely (len > left ()))
break;
+ /* Section vendor name. */
const unsigned char *name = p + sizeof len;
p += len;
unsigned const char *q = memchr (name, '\0', len);
if (unlikely (q == NULL))
- continue;
+ break;
++q;
printf (gettext (" %-13s %4" PRIu32 "\n"), name, len);
+ bool gnu_vendor = (q - name == sizeof "gnu"
+ && !memcmp (name, "gnu", sizeof "gnu"));
+
+ /* Loop over subsections. */
if (shdr->sh_type != SHT_GNU_ATTRIBUTES
- || (q - name == sizeof "gnu"
- && !memcmp (name, "gnu", sizeof "gnu")))
+ || gnu_vendor)
while (q < p)
{
const unsigned char *const sub = q;
@@ -3371,7 +3378,10 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
if (MY_ELFDATA != ehdr->e_ident[EI_DATA])
CONVERT (subsection_len);
- if (unlikely (p - sub < (ptrdiff_t) subsection_len))
+ /* Don't overflow, ptrdiff_t might be 32bits, but signed. */
+ if (unlikely (subsection_len == 0
+ || subsection_len >= (uint32_t) PTRDIFF_MAX
+ || p - sub < (ptrdiff_t) subsection_len))
break;
const unsigned char *r = q + sizeof subsection_len;
@@ -3380,6 +3390,7 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
switch (subsection_tag)
{
default:
+ /* Unknown subsection, print and skip. */
printf (gettext (" %-4u %12" PRIu32 "\n"),
subsection_tag, subsection_len);
break;
@@ -3395,16 +3406,30 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
if (unlikely (r >= q))
break;
+ /* GNU style tags have either a uleb128 value,
+ when lowest bit is not set, or a string
+ when the lowest bit is set.
+ "compatibility" (32) is special. It has
+ both a string and a uleb128 value. For
+ non-gnu we assume 6 till 31 only take ints.
+ XXX see arm backend, do we need a separate
+ hook? */
uint64_t value = 0;
const char *string = NULL;
- if (tag == 32 || (tag & 1) == 0)
+ if (tag == 32 || (tag & 1) == 0
+ || (! gnu_vendor && (tag > 5 && tag < 32)))
{
get_uleb128 (value, r);
if (r > q)
break;
}
- if (tag == 32 || (tag & 1) != 0)
+ if (tag == 32
+ || ((tag & 1) != 0
+ && (gnu_vendor
+ || (! gnu_vendor && tag > 32)))
+ || (! gnu_vendor && tag > 3 && tag < 6))
{
+ string = (const char *) r;
r = memchr (r, '\0', q - r);
if (r == NULL)
break;
@@ -3431,7 +3456,10 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
}
else
{
- assert (tag != 32);
+ /* For "gnu" vendor 32 "compatibility" has
+ already been handled above. */
+ assert (tag != 32
+ || strcmp ((const char *) name, "gnu"));
if (string == NULL)
printf (gettext (" %u: %" PRId64 "\n"),
tag, value);
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 798bb7a..909a61e 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,10 @@
+2014-11-21 Mark Wielaard <mjw(a)redhat.com>
+
+ * run-readelf-A.sh: New test.
+ * testfileppc32attrs.o.bz2: New test file.
+ * Makefile.am (TESTS): Add run-readelf-A.sh.
+ (EXTRA_DIST): Add run-readelf-A.sh and testfileppc32attrs.o.bz2.
+
2014-11-10 Mark Wielaard <mjw(a)redhat.com>
* vdsosyms.c: New test.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index dcaf4f6..5b38113 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -110,7 +110,7 @@ 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-linkmap-cut.sh run-aggregate-size.sh vdsosyms run-readelf-A.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -276,7 +276,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-deleted.sh run-linkmap-cut.sh linkmap-cut-lib.so.bz2 \
linkmap-cut.bz2 linkmap-cut.core.bz2 \
run-aggregate-size.sh testfile-sizes1.o.bz2 testfile-sizes2.o.bz2 \
- testfile-sizes3.o.bz2
+ testfile-sizes3.o.bz2 \
+ run-readelf-A.sh testfileppc32attrs.o.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
diff --git a/tests/run-readelf-A.sh b/tests/run-readelf-A.sh
new file mode 100755
index 0000000..6ca9be8
--- /dev/null
+++ b/tests/run-readelf-A.sh
@@ -0,0 +1,65 @@
+#! /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-addrcfi.sh for testfilearm.
+
+# = testfileppc32attrs.s =
+# .gnu_attribute 8,1
+# .gnu_attribute 12,1
+#
+# gcc -m32 -c testfileppc32attrs.s
+
+testfiles testfilearm testfileppc32attrs.o
+
+testrun_compare ${abs_top_builddir}/src/readelf -A testfilearm <<\EOF
+
+Object attributes section [27] '.ARM.attributes' of 53 bytes at offset 0x718:
+ Owner Size
+ aeabi 52
+ File: 42
+ CPU_name: 7-A
+ CPU_arch: v7
+ CPU_arch_profile: Application
+ ARM_ISA_use: Yes
+ THUMB_ISA_use: Thumb-2
+ VFP_arch: VFPv3-D16
+ ABI_PCS_wchar_t: 4
+ ABI_FP_rounding: Needed
+ ABI_FP_denormal: Needed
+ ABI_FP_exceptions: Needed
+ ABI_FP_number_model: IEEE 754
+ ABI_align8_needed: Yes
+ ABI_align8_preserved: Yes, except leaf SP
+ ABI_enum_size: int
+ ABI_HardFP_use: SP and DP
+ ABI_VFP_args: VFP registers
+ CPU_unaligned_access: v6
+EOF
+
+testrun_compare ${abs_top_builddir}/src/readelf -A testfileppc32attrs.o <<\EOF
+
+Object attributes section [ 4] '.gnu.attributes' of 18 bytes at offset 0x34:
+ Owner Size
+ gnu 17
+ File: 9
+ GNU_Power_ABI_Vector: Generic
+ GNU_Power_ABI_Struct_Return: r3/r4
+EOF
+
+exit 0
diff --git a/tests/testfileppc32attrs.o.bz2 b/tests/testfileppc32attrs.o.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..c8d80a997abfed579a041fb9c1d366e33897116f
GIT binary patch
literal 228
zcmV<A02}{8T4*^jL0KkKS*Ydo9RL8T|HS|7bPOQX1Oy`lKma!5o?wIk1ONg6umHAT
z(^Eu8q#6wxXwYaf85(*)s1sAv7-R{i0GI(80!1c}0iXtkng9bp^Yr8jNHR_7XypbC
z8ALv?2;yus7RZnQW9d07C?thh;{}$9q$&FY6GLFy2G9je5i<otNVLusX5>ocFJU$*
z;-tF;MK~|Si!H(k+Z6-~b^*r`4>c7HV>K}mRWdCQTI+PcS!C#KEgcy}@VHwVT@-we
e!wj-sXLIq0zX!<OGj70P{9VZu;X*>Am(X;z%vNIn
literal 0
HcmV?d00001
--
1.8.3.1
9 years
[COMMITTED] libdwfl: Sanity check the symbol table before use.
by Mark Wielaard
Make sure the number of symbols reported and the first global fit the data.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdwfl/ChangeLog | 5 +++++
libdwfl/dwfl_module_getdwarf.c | 25 +++++++++++++++++++------
src/ChangeLog | 5 +++++
3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 22bc783..460f4e5 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,8 @@
+2014-11-18 Mark Wielaard <mjw(a)redhat.com>
+
+ * dwfl_module_getdwarf.c (find_symtab): Sanity check the data buffer,
+ number of symbols and first_global before use.
+
2014-11-14 Mark Wielaard <mjw(a)redhat.com>
* dwfl_module_getdwarf.c (load_symtab): Don't use tables which have
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index a20875b..c2e9e59 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -1083,7 +1083,7 @@ find_symtab (Dwfl_Module *mod)
mod->symstrdata = elf_getdata (elf_getscn (mod->symfile->elf, strshndx),
NULL);
- if (mod->symstrdata == NULL)
+ if (mod->symstrdata == NULL || mod->symstrdata->d_buf == NULL)
goto elferr;
if (xndxscn == NULL)
@@ -1091,12 +1091,18 @@ find_symtab (Dwfl_Module *mod)
else
{
mod->symxndxdata = elf_getdata (xndxscn, NULL);
- if (mod->symxndxdata == NULL)
+ if (mod->symxndxdata == NULL || mod->symxndxdata->d_buf == NULL)
goto elferr;
}
mod->symdata = elf_getdata (symscn, NULL);
- if (mod->symdata == NULL)
+ if (mod->symdata == NULL || mod->symdata->d_buf == NULL)
+ goto elferr;
+
+ // Sanity check number of symbols.
+ GElf_Shdr shdr_mem, *shdr = gelf_getshdr (symscn, &shdr_mem);
+ if (mod->syments > mod->symdata->d_size / shdr->sh_entsize
+ || (size_t) mod->first_global > mod->syments)
goto elferr;
/* Cache any auxiliary symbol info, when it fails, just ignore aux_sym. */
@@ -1116,7 +1122,7 @@ find_symtab (Dwfl_Module *mod)
mod->aux_symstrdata = elf_getdata (elf_getscn (mod->aux_sym.elf,
aux_strshndx),
NULL);
- if (mod->aux_symstrdata == NULL)
+ if (mod->aux_symstrdata == NULL || mod->aux_symstrdata->d_buf == NULL)
goto aux_cleanup;
if (aux_xndxscn == NULL)
@@ -1124,12 +1130,19 @@ find_symtab (Dwfl_Module *mod)
else
{
mod->aux_symxndxdata = elf_getdata (aux_xndxscn, NULL);
- if (mod->aux_symxndxdata == NULL)
+ if (mod->aux_symxndxdata == NULL
+ || mod->aux_symxndxdata->d_buf == NULL)
goto aux_cleanup;
}
mod->aux_symdata = elf_getdata (aux_symscn, NULL);
- if (mod->aux_symdata == NULL)
+ if (mod->aux_symdata == NULL || mod->aux_symdata->d_buf == NULL)
+ goto aux_cleanup;
+
+ // Sanity check number of aux symbols.
+ shdr = gelf_getshdr (aux_symscn, &shdr_mem);
+ if (mod->aux_syments > mod->aux_symdata->d_size / shdr->sh_entsize
+ || (size_t) mod->aux_first_global > mod->aux_syments)
goto aux_cleanup;
}
}
diff --git a/src/ChangeLog b/src/ChangeLog
index 2ab1594..bab948a 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,8 @@
+2014-11-18 Mark Wielaard <mjw(a)redhat.com>
+
+ * readelf.c (print_cfa_program): Fix sanity check of DW_FORM_block
+ length.
+
2014-11-17 Mark Wielaard <mjw(a)redhat.com>
* readelf.c (handle_verneed): Check vna_next and vn_next exist.
--
1.8.3.1
9 years
[COMMITTED] readelf: Fix sanity check of DW_FORM_block length in print_cfa_program
by Mark Wielaard
We were checking the reg nr, not the length of the block.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/readelf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/readelf.c b/src/readelf.c
index c14bfb6..08de798 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -5025,7 +5025,7 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
get_uleb128 (op2, readp); /* Length of DW_FORM_block. */
printf (" expression r%" PRIu64 " (%s) \n",
op1, regname (op1));
- if ((uint64_t) (endp - readp) < op1)
+ if ((uint64_t) (endp - readp) < op2)
goto invalid;
print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL,
op2, readp);
--
1.8.3.1
9 years
[COMMITTED] libelf: Check for overflow in version_xlate elf_cvt_Verdef and elf_cvt_Verneed.
by Mark Wielaard
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libelf/ChangeLog | 5 +++++
libelf/version_xlate.h | 8 ++++----
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index 9ae24a9..c7e8d30 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,8 @@
+2014-11-18 Mark Wielaard <mjw(a)redhat.com>
+
+ * version_xlate.h (elf_cvt_Verdef): Check for overflow.
+ (elf_cvt_Verneed): Likewise.
+
2014-11-17 Mark Wielaard <mjw(a)redhat.com>
* elf-knowledge.h (SECTION_STRIP_P): Check name is not NULL.
diff --git a/libelf/version_xlate.h b/libelf/version_xlate.h
index 935f77a..16eaa19 100644
--- a/libelf/version_xlate.h
+++ b/libelf/version_xlate.h
@@ -61,7 +61,7 @@ elf_cvt_Verdef (void *dest, const void *src, size_t len, int encode)
GElf_Verdaux *asrc;
/* Test for correct offset. */
- if (def_offset + sizeof (GElf_Verdef) > len)
+ if (def_offset > len || len - def_offset < sizeof (GElf_Verdef))
return;
/* Work the tree from the first record. */
@@ -90,7 +90,7 @@ elf_cvt_Verdef (void *dest, const void *src, size_t len, int encode)
GElf_Verdaux *adest;
/* Test for correct offset. */
- if (aux_offset + sizeof (GElf_Verdaux) > len)
+ if (aux_offset > len || len - aux_offset < sizeof (GElf_Verdaux))
return;
adest = (GElf_Verdaux *) ((char *) dest + aux_offset);
@@ -155,7 +155,7 @@ elf_cvt_Verneed (void *dest, const void *src, size_t len, int encode)
GElf_Vernaux *asrc;
/* Test for correct offset. */
- if (need_offset + sizeof (GElf_Verneed) > len)
+ if (need_offset > len || len - need_offset < sizeof (GElf_Verneed))
return;
/* Work the tree from the first record. */
@@ -182,7 +182,7 @@ elf_cvt_Verneed (void *dest, const void *src, size_t len, int encode)
GElf_Vernaux *adest;
/* Test for correct offset. */
- if (aux_offset + sizeof (GElf_Vernaux) > len)
+ if (aux_offset > len || len - aux_offset < sizeof (GElf_Vernaux))
return;
adest = (GElf_Vernaux *) ((char *) dest + aux_offset);
--
1.8.3.1
9 years
[COMMITTED] readelf: When the version chain ends, stop processing the entries.
by Mark Wielaard
The version definition, auxiliary version, version dependency and needed
version sections chain information together through "next" fields. When
the "next" field is zero there are no more information entries. Stop
processing when we see zero instead of repeatedly processing the same
entry (at offset zero from the current one).
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 7 +++++++
src/readelf.c | 28 ++++++++++++++++++++++++++--
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 727d100..2ab1594 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,12 @@
2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+ * readelf.c (handle_verneed): Check vna_next and vn_next exist.
+ (handle_verdef): Check vda_next and vd_next exist.
+ (handle_versym): Check vd_next, vna_next and vn_next exist.
+ Check vername and filename are not NULL before use.
+
+2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+
* elfcmp.c (main): Check section names are NULL before use.
* objdump.c (section_match): Likewise.
* size.c (show_sysv): Likewise.
diff --git a/src/readelf.c b/src/readelf.c
index bd97ca6..c14bfb6 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -2505,10 +2505,16 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
get_ver_flags (aux->vna_flags),
(unsigned short int) aux->vna_other);
+ if (aux->vna_next == 0)
+ break;
+
auxoffset += aux->vna_next;
}
/* Find the next offset. */
+ if (need->vn_next == 0)
+ break;
+
offset += need->vn_next;
}
}
@@ -2583,10 +2589,15 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
auxoffset, cnt2,
elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name));
+ if (aux->vda_next == 0)
+ break;
+
auxoffset += aux->vda_next;
}
/* Find the next offset. */
+ if (def->vd_next == 0)
+ break;
offset += def->vd_next;
}
}
@@ -2665,6 +2676,8 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
nvername = MAX (nvername, (size_t) (def->vd_ndx & 0x7fff));
+ if (def->vd_next == 0)
+ break;
offset += def->vd_next;
}
}
@@ -2709,9 +2722,13 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
nvername = MAX (nvername,
(size_t) (aux->vna_other & 0x7fff));
+ if (aux->vna_next == 0)
+ break;
auxoffset += aux->vna_next;
}
+ if (need->vn_next == 0)
+ break;
offset += need->vn_next;
}
}
@@ -2763,6 +2780,8 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
= elf_strptr (ebl->elf, defshdr->sh_link, aux->vda_name);
filename[def->vd_ndx & 0x7fff] = NULL;
+ if (def->vd_next == 0)
+ break;
offset += def->vd_next;
}
}
@@ -2800,9 +2819,13 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
filename[aux->vna_other & 0x7fff]
= elf_strptr (ebl->elf, needshdr->sh_link, need->vn_file);
+ if (aux->vna_next == 0)
+ break;
auxoffset += aux->vna_next;
}
+ if (need->vn_next == 0)
+ break;
offset += need->vn_next;
}
}
@@ -2863,10 +2886,11 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
default:
n = printf ("%4d%c%s",
*sym & 0x7fff, *sym & 0x8000 ? 'h' : ' ',
- (unsigned int) (*sym & 0x7fff) < nvername
+ (vername != NULL
+ && (unsigned int) (*sym & 0x7fff) < nvername)
? vername[*sym & 0x7fff] : "???");
if ((unsigned int) (*sym & 0x7fff) < nvername
- && filename[*sym & 0x7fff] != NULL)
+ && filename != NULL && filename[*sym & 0x7fff] != NULL)
n += printf ("(%s)", filename[*sym & 0x7fff]);
printf ("%*s", MAX (0, 33 - (int) n), " ");
break;
--
1.8.3.1
9 years
[COMMITTED] Check elf_strptr didn't fail getting section name.
by Mark Wielaard
Since elf_strptr can fail and return NULL we should always check the result
before usage. Debug sections are only handled by section name, so make sure
the name actually exists.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
backends/ChangeLog | 4 ++++
backends/ppc64_init.c | 15 +++++++++------
libebl/ChangeLog | 4 ++++
libebl/ebldebugscnp.c | 4 ++--
libelf/ChangeLog | 4 ++++
libelf/elf-knowledge.h | 5 +++--
src/ChangeLog | 6 ++++++
src/elfcmp.c | 5 +++--
src/objdump.c | 6 +++---
src/size.c | 7 +++----
10 files changed, 41 insertions(+), 19 deletions(-)
diff --git a/backends/ChangeLog b/backends/ChangeLog
index 82a2bf1..abd22bf 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,7 @@
+2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+
+ * ppc64_init.c (ppc64_init): Check section name is not NULL.
+
2014-10-06 Mark Wielaard <mjw(a)redhat.com>
* libebl_CPU.h (dwarf_peel_type): Removed.
diff --git a/backends/ppc64_init.c b/backends/ppc64_init.c
index 7ea2b23..56e1828 100644
--- a/backends/ppc64_init.c
+++ b/backends/ppc64_init.c
@@ -90,13 +90,16 @@ ppc64_init (elf, machine, eh, ehlen)
if (opd_shdr != NULL
&& (opd_shdr->sh_flags & SHF_ALLOC) != 0
&& opd_shdr->sh_type == SHT_PROGBITS
- && opd_shdr->sh_size > 0
- && strcmp (elf_strptr (elf, ehdr->e_shstrndx,
- opd_shdr->sh_name), ".opd") == 0)
+ && opd_shdr->sh_size > 0)
{
- eh->fd_addr = opd_shdr->sh_addr;
- eh->fd_data = elf_getdata (scn, NULL);
- break;
+ const char *name = elf_strptr (elf, ehdr->e_shstrndx,
+ opd_shdr->sh_name);
+ if (name != NULL && strcmp (name, ".opd") == 0)
+ {
+ eh->fd_addr = opd_shdr->sh_addr;
+ eh->fd_data = elf_getdata (scn, NULL);
+ break;
+ }
}
}
}
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index 5ec7101..b6a0e63 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,7 @@
+2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+
+ * ebldebugscnp.c (ebl_debugscn_p): Check name is not NULL.
+
2014-06-17 Mark Wielaard <mjw(a)redhat.com>
* eblinitreg.c (ebl_func_addr_mask): New function.
diff --git a/libebl/ebldebugscnp.c b/libebl/ebldebugscnp.c
index f2351e2..01a5675 100644
--- a/libebl/ebldebugscnp.c
+++ b/libebl/ebldebugscnp.c
@@ -1,5 +1,5 @@
/* Check section name for being that of a debug informatino section.
- Copyright (C) 2002 Red Hat, Inc.
+ Copyright (C) 2002, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2002.
@@ -40,5 +40,5 @@ ebl_debugscn_p (ebl, name)
Ebl *ebl;
const char *name;
{
- return ebl->debugscn_p (name);
+ return name != NULL && ebl->debugscn_p (name);
}
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index 633a892..9ae24a9 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,7 @@
+2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+
+ * elf-knowledge.h (SECTION_STRIP_P): Check name is not NULL.
+
2014-11-16 Mark Wielaard <mjw(a)redhat.com>
* elf_getshdrstrndx.c: Check there are section headers before
diff --git a/libelf/elf-knowledge.h b/libelf/elf-knowledge.h
index 99fb910..24534b3 100644
--- a/libelf/elf-knowledge.h
+++ b/libelf/elf-knowledge.h
@@ -1,5 +1,5 @@
/* Accumulation of various pieces of knowledge about ELF.
- Copyright (C) 2000-2012 Red Hat, Inc.
+ Copyright (C) 2000-2012, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2000.
@@ -41,7 +41,8 @@
&& (shdr)->sh_type != SHT_NOTE \
&& (((shdr)->sh_type) != SHT_PROGBITS \
/* Never remove .gnu.warning.* sections. */ \
- || (strncmp (name, ".gnu.warning.", sizeof ".gnu.warning." - 1) != 0 \
+ || (name != NULL \
+ && strncmp (name, ".gnu.warning.", sizeof ".gnu.warning." - 1) != 0\
/* We remove .comment sections only if explicitly told to do so. */\
&& (remove_comment \
|| strcmp (name, ".comment") != 0))))
diff --git a/src/ChangeLog b/src/ChangeLog
index 96f21fd..727d100 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,11 @@
2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+ * elfcmp.c (main): Check section names are NULL before use.
+ * objdump.c (section_match): Likewise.
+ * size.c (show_sysv): Likewise.
+
+2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+
* readelf.c (print_debug_frame_section): Warn if ptr_size is not 4
or 8 instead of just calling print_cfa_program.
diff --git a/src/elfcmp.c b/src/elfcmp.c
index 2d85f0b..c420019 100644
--- a/src/elfcmp.c
+++ b/src/elfcmp.c
@@ -1,5 +1,5 @@
/* Compare relevant content of two ELF files.
- Copyright (C) 2005-2012 Red Hat, Inc.
+ Copyright (C) 2005-2012, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
@@ -355,7 +355,8 @@ main (int argc, char *argv[])
sym1->st_name);
const char *name2 = elf_strptr (elf2, shdr2->sh_link,
sym2->st_name);
- if (unlikely (strcmp (name1, name2) != 0
+ if (unlikely (name1 == NULL || name2 == NULL
+ || strcmp (name1, name2) != 0
|| sym1->st_value != sym2->st_value
|| (sym1->st_size != sym2->st_size
&& sym1->st_shndx != SHN_UNDEF)
diff --git a/src/objdump.c b/src/objdump.c
index ebad25d..5376447 100644
--- a/src/objdump.c
+++ b/src/objdump.c
@@ -1,5 +1,5 @@
/* Print information from ELF file in human-readable form.
- Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012 Red Hat, Inc.
+ Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
@@ -460,13 +460,13 @@ section_match (Elf *elf, uint32_t scnndx, GElf_Shdr *shdr, size_t shstrndx)
return true;
struct section_list *runp = section_list;
+ const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
do
{
if (runp->is_name)
{
- if (strcmp (runp->name,
- elf_strptr (elf, shstrndx, shdr->sh_name)) == 0)
+ if (name && strcmp (runp->name, name) == 0)
return true;
}
else
diff --git a/src/size.c b/src/size.c
index 9db55c8..cb67999 100644
--- a/src/size.c
+++ b/src/size.c
@@ -427,10 +427,9 @@ show_sysv (Elf *elf, const char *prefix, const char *fname,
INTERNAL_ERROR (fullname);
/* Ignore all sections which are not used at runtime. */
- if ((shdr->sh_flags & SHF_ALLOC) != 0)
- maxlen = MAX (maxlen,
- (int) strlen (elf_strptr (elf, shstrndx,
- shdr->sh_name)));
+ const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
+ if (name != NULL && (shdr->sh_flags & SHF_ALLOC) != 0)
+ maxlen = MAX (maxlen, (int) strlen (name));
}
fputs_unlocked (fname, stdout);
--
1.8.3.1
9 years
[COMMITTED] readelf: Warn if ptr_size is not 4 or 8 bytes.
by Mark Wielaard
Just warn and don't call print_cfa_program in that case. Bad things will
happen and the result is mostly bogus.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 5 +++++
src/readelf.c | 9 ++++++---
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 737c674..96f21fd 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,8 @@
+2014-11-17 Mark Wielaard <mjw(a)redhat.com>
+
+ * readelf.c (print_debug_frame_section): Warn if ptr_size is not 4
+ or 8 instead of just calling print_cfa_program.
+
2014-11-16 Mark Wielaard <mjw(a)redhat.com>
* readelf (process_elf_file): Set phnum to zero if there aren't
diff --git a/src/readelf.c b/src/readelf.c
index 583b5da..bd97ca6 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -5626,9 +5626,12 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
}
/* Handle the initialization instructions. */
- print_cfa_program (readp, cieend, vma_base, code_alignment_factor,
- data_alignment_factor, version, ptr_size,
- dwflmod, ebl, dbg);
+ if (ptr_size != 4 && ptr_size !=8)
+ printf ("invalid CIE pointer size (%u), must be 4 or 8.\n", ptr_size);
+ else
+ print_cfa_program (readp, cieend, vma_base, code_alignment_factor,
+ data_alignment_factor, version, ptr_size,
+ dwflmod, ebl, dbg);
readp = cieend;
}
}
--
1.8.3.1
9 years
[COMMITTED] libelf: elf_getshdrstrndx cannot use SHN_XINDEX without section headers.
by Mark Wielaard
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libelf/ChangeLog | 5 +++++
libelf/elf_getshdrstrndx.c | 15 +++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index ef5da43..633a892 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,5 +1,10 @@
2014-11-16 Mark Wielaard <mjw(a)redhat.com>
+ * elf_getshdrstrndx.c: Check there are section headers before
+ handling SHN_XINDEX.
+
+2014-11-16 Mark Wielaard <mjw(a)redhat.com>
+
* elf32_getphdr.c (getphdr_wrlock): Check e_phoff isn't zero.
Check for too many pheaders.
* elf_getphdrnum.c (__elf_getphdrnum_rdlock): Check section zero
diff --git a/libelf/elf_getshdrstrndx.c b/libelf/elf_getshdrstrndx.c
index 1dbed4c..6f8d66e 100644
--- a/libelf/elf_getshdrstrndx.c
+++ b/libelf/elf_getshdrstrndx.c
@@ -92,6 +92,13 @@ elf_getshdrstrndx (elf, dst)
if (elf->class == ELFCLASS32)
{
size_t offset;
+ if (unlikely (elf->state.elf32.scns.cnt == 0))
+ {
+ /* Cannot use SHN_XINDEX without section headers. */
+ __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER);
+ result = -1;
+ goto out;
+ }
if (elf->state.elf32.scns.data[0].shdr.e32 != NULL)
{
@@ -146,6 +153,14 @@ elf_getshdrstrndx (elf, dst)
}
else
{
+ if (unlikely (elf->state.elf64.scns.cnt == 0))
+ {
+ /* Cannot use SHN_XINDEX without section headers. */
+ __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER);
+ result = -1;
+ goto out;
+ }
+
if (elf->state.elf64.scns.data[0].shdr.e64 != NULL)
{
num = elf->state.elf64.scns.data[0].shdr.e64->sh_link;
--
1.8.3.1
9 years
[COMMITTED] libelf: Fix handling of (extended) phnum.
by Mark Wielaard
If there is no e_phoff e_phnum cannot be trusted. Extended phnum can only
be gotten if we have an actual section table and a shdr for section zero,
Extended phnum can be too large to fit in the file (or a size_t).
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libelf/ChangeLog | 7 +++++++
libelf/elf32_getphdr.c | 6 ++++--
libelf/elf_getphdrnum.c | 18 +++++++++++++-----
src/ChangeLog | 6 ++++++
src/readelf.c | 7 ++++++-
5 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index beb431f..ef5da43 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,5 +1,12 @@
2014-11-16 Mark Wielaard <mjw(a)redhat.com>
+ * elf32_getphdr.c (getphdr_wrlock): Check e_phoff isn't zero.
+ Check for too many pheaders.
+ * elf_getphdrnum.c (__elf_getphdrnum_rdlock): Check section zero
+ actually exists before handling PN_XNUM.
+
+2014-11-16 Mark Wielaard <mjw(a)redhat.com>
+
* gelf_getnote.c (gelf_getnote): Check padding overflow.
2014-11-16 Mark Wielaard <mjw(a)redhat.com>
diff --git a/libelf/elf32_getphdr.c b/libelf/elf32_getphdr.c
index e74e63f..1b82a48 100644
--- a/libelf/elf32_getphdr.c
+++ b/libelf/elf32_getphdr.c
@@ -76,15 +76,17 @@ __elfw2(LIBELFBITS,getphdr_wrlock) (elf)
size_t phnum;
if (__elf_getphdrnum_rdlock (elf, &phnum) != 0)
goto out;
- if (phnum == 0)
+ if (phnum == 0 || ehdr->e_phoff == 0)
{
__libelf_seterrno (ELF_E_NO_PHDR);
goto out;
}
+ /* Check this doesn't overflow. */
size_t size = phnum * sizeof (ElfW2(LIBELFBITS,Phdr));
- if (ehdr->e_phoff > elf->maximum_size
+ if (phnum > SIZE_MAX / sizeof (ElfW2(LIBELFBITS,Phdr))
+ || ehdr->e_phoff > elf->maximum_size
|| elf->maximum_size - ehdr->e_phoff < size)
{
__libelf_seterrno (ELF_E_INVALID_DATA);
diff --git a/libelf/elf_getphdrnum.c b/libelf/elf_getphdrnum.c
index 99649be..d8e34d7 100644
--- a/libelf/elf_getphdrnum.c
+++ b/libelf/elf_getphdrnum.c
@@ -1,5 +1,5 @@
/* Return number of program headers in the ELF file.
- Copyright (C) 2010 Red Hat, Inc.
+ Copyright (C) 2010, 2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -62,10 +62,18 @@ __elf_getphdrnum_rdlock (elf, dst)
/* If there are no section headers, perhaps this is really just 65536
written without PN_XNUM support. Either that or it's bad data. */
- if (likely (scns->cnt > 0))
- *dst = (elf->class == ELFCLASS32
- ? scns->data[0].shdr.e32->sh_info
- : scns->data[0].shdr.e64->sh_info);
+ if (elf->class == ELFCLASS32)
+ {
+ if (likely (scns->cnt > 0
+ && elf->state.elf32.scns.data[0].shdr.e32 != NULL))
+ *dst = scns->data[0].shdr.e32->sh_info;
+ }
+ else
+ {
+ if (likely (scns->cnt > 0
+ && elf->state.elf64.scns.data[0].shdr.e64 != NULL))
+ *dst = scns->data[0].shdr.e64->sh_info;
+ }
}
return 0;
diff --git a/src/ChangeLog b/src/ChangeLog
index fefd6c1..737c674 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,11 @@
2014-11-16 Mark Wielaard <mjw(a)redhat.com>
+ * readelf (process_elf_file): Set phnum to zero if there aren't
+ actually any pheaders.
+ (print_phdr): Check there actually is a phdr.
+
+2014-11-16 Mark Wielaard <mjw(a)redhat.com>
+
* readelf.c (print_cfa_program): Check block len before calling
print_ops.
diff --git a/src/readelf.c b/src/readelf.c
index 697a0e5..583b5da 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -835,6 +835,11 @@ process_elf_file (Dwfl_Module *dwflmod, int fd)
gettext ("cannot determine number of program headers: %s"),
elf_errmsg (-1));
+ /* If there isn't actually a program header then set phnum to zero.
+ Don't do any extra work. gelf_getphdr will always return NULL. */
+ if (ehdr->e_phoff == 0)
+ phnum = 0;
+
/* For an ET_REL file, libdwfl has adjusted the in-core shdrs
and may have applied relocation to some sections.
So we need to get a fresh Elf handle on the file to display those. */
@@ -1157,7 +1162,7 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\
static void
print_phdr (Ebl *ebl, GElf_Ehdr *ehdr)
{
- if (ehdr->e_phnum == 0)
+ if (ehdr->e_phnum == 0 || ehdr->e_phoff == 0)
/* No program header, this is OK in relocatable objects. */
return;
--
1.8.3.1
9 years
[COMMITTED] readelf: Robustify print_cfa_program.
by Mark Wielaard
Check block len before calling print_ops.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 5 +++++
src/readelf.c | 10 ++++++++++
2 files changed, 15 insertions(+)
diff --git a/src/ChangeLog b/src/ChangeLog
index 7561041..fefd6c1 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,8 @@
+2014-11-16 Mark Wielaard <mjw(a)redhat.com>
+
+ * readelf.c (print_cfa_program): Check block len before calling
+ print_ops.
+
2014-11-14 Mark Wielaard <mjw(a)redhat.com>
* readelf.c (print_debug_frame_section): Sanity Check CIE
diff --git a/src/readelf.c b/src/readelf.c
index 065ee1c..697a0e5 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -4980,6 +4980,12 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
// XXX overflow check
get_uleb128 (op1, readp); /* Length of DW_FORM_block. */
printf (" def_cfa_expression %" PRIu64 "\n", op1);
+ if ((uint64_t) (endp - readp) < op1)
+ {
+ invalid:
+ fputs (gettext (" <INVALID DATA>\n"), stdout);
+ return;
+ }
print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL,
op1, readp);
readp += op1;
@@ -4990,6 +4996,8 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
get_uleb128 (op2, readp); /* Length of DW_FORM_block. */
printf (" expression r%" PRIu64 " (%s) \n",
op1, regname (op1));
+ if ((uint64_t) (endp - readp) < op1)
+ goto invalid;
print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL,
op2, readp);
readp += op2;
@@ -5034,6 +5042,8 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
get_uleb128 (op2, readp); /* Length of DW_FORM_block. */
printf (" val_expression r%" PRIu64 " (%s)\n",
op1, regname (op1));
+ if ((uint64_t) (endp - readp) < op2)
+ goto invalid;
print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0,
NULL, op2, readp);
readp += op2;
--
1.8.3.1
9 years