This is the mail archive of the
elfutils-devel@sourceware.org
mailing list for the elfutils project.
Re: out-of-bounds read / crash in elfutils tools (readelf, nm, ...) with malformed file
- From: Mark Wielaard <mjw at redhat dot com>
- To: elfutils-devel at lists dot fedorahosted dot org
- Date: Fri, 07 Nov 2014 12:58:07 +0100
- Subject: Re: out-of-bounds read / crash in elfutils tools (readelf, nm, ...) with malformed file
On Fri, 2014-11-07 at 01:27 +0100, Hanno Böck wrote:
> Am Thu, 06 Nov 2014 16:11:43 +0100
> schrieb Mark Wielaard <mjw@redhat.com>:
>
> > > (actually this bug report is kind of a fallout of a bug search in
> > > libbfd - various parser bugs in the binutils-tools have been found
> > > and fixed in the past days and I thought I'd run other elf-related
> > > tools on the collection of bug-exposing binaries)
> >
> > Thanks. If you have any other examples please do report them.
>
> Ten to crash readelf -a attached, according to american-fuzzy-lop all
> distinct code paths.
Thanks. eu-readelf didn't sanitize the hash section data before use.
The attached patch should fix that.
Cheers,
Mark
From 5f6cd01d4ca5d5b0fab6dd35d22fbf900f50364f Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw@redhat.com>
Date: Fri, 7 Nov 2014 12:54:02 +0100
Subject: [PATCH] readelf: Sanity check hash section contents before
processing.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reported by: Hanno Böck <hanno@hboeck.de>
Signed-off-by: Mark Wielaard <mjw@redhat.com>
---
src/ChangeLog | 6 ++++++
src/readelf.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index a252cdc..3ff3e31 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,9 @@
+2014-11-07 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_sysv_hash): Sanity check section contents.
+ (handle_sysv_hash64): Likewise.
+ (handle_gnu_hash): Likewise.
+
2014-09-14 Petr Machata <pmachata@redhat.com>
* readelf.c (handle_relocs_rela): Typo fix, test DESTSHDR properly.
diff --git a/src/readelf.c b/src/readelf.c
index 4d3bb36..fba6c03 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -2954,8 +2954,21 @@ handle_sysv_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
return;
}
+ if (unlikely (data->d_size < 2 * sizeof (Elf32_Word)))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in sysv.hash section %d"),
+ (int) elf_ndxscn (scn));
+ return;
+ }
+
Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1];
+
+ uint32_t used_buf = (2 + nchain + nbucket) * sizeof (Elf32_Word);
+ if (used_buf > data->d_size || used_buf + data->d_size < data->d_size)
+ goto invalid_data;
+
Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[2];
Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + nbucket];
@@ -2996,8 +3009,21 @@ handle_sysv_hash64 (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
return;
}
+ if (unlikely (data->d_size < 2 * sizeof (Elf64_Xword)))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in sysv.hash64 section %d"),
+ (int) elf_ndxscn (scn));
+ return;
+ }
+
Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0];
Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1];
+
+ uint32_t used_buf = (2 + nchain + nbucket) * sizeof (Elf64_Xword);
+ if (used_buf > data->d_size || used_buf + data->d_size < data->d_size)
+ goto invalid_data;
+
Elf64_Xword *bucket = &((Elf64_Xword *) data->d_buf)[2];
Elf64_Xword *chain = &((Elf64_Xword *) data->d_buf)[2 + nbucket];
@@ -3037,18 +3063,36 @@ handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
return;
}
+ if (unlikely (data->d_size < 4 * sizeof (Elf32_Word)))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in gnu.hash section %d"),
+ (int) elf_ndxscn (scn));
+ return;
+ }
+
Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
/* Next comes the size of the bitmap. It's measured in words for
the architecture. It's 32 bits for 32 bit archs, and 64 bits for
- 64 bit archs. */
+ 64 bit archs. There is always a bloom filter present, so zero is
+ an invalid value. */
Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2];
if (gelf_getclass (ebl->elf) == ELFCLASS64)
bitmask_words *= 2;
+ if (bitmask_words == 0)
+ goto invalid_data;
+
Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3];
+ /* Is there still room for the sym chain? Check for unsigned overlow. */
+ uint32_t used_buf = (4 + bitmask_words + nbucket) * sizeof (Elf32_Word);
+ uint32_t max_nsyms = (data->d_size - used_buf) / sizeof (Elf32_Word);
+ if (used_buf > data->d_size || used_buf + data->d_size < data->d_size)
+ goto invalid_data;
+
uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t));
Elf32_Word *bitmask = &((Elf32_Word *) data->d_buf)[4];
@@ -3068,6 +3112,8 @@ handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
++nsyms;
if (maxlength < ++lengths[cnt])
++maxlength;
+ if (inner > max_nsyms)
+ goto invalid_data;
}
while ((chain[inner++] & 1) == 0);
}
--
1.9.3