sprof: check pread size and offset for overflow
DJ Delorie
dj@redhat.com
Thu Oct 16 01:41:12 GMT 2025
Add a bit of descriptive paranoia to the values we read from
the ELF headers and use to access data.
diff --git a/elf/sprof.c b/elf/sprof.c
index c82c7c9db6..2267391679 100644
--- a/elf/sprof.c
+++ b/elf/sprof.c
@@ -410,6 +410,7 @@ load_shobj (const char *name)
int fd;
ElfW(Shdr) *shdr;
size_t pagesize = getpagesize ();
+ struct stat st;
/* Since we use dlopen() we must be prepared to work around the sometimes
strange lookup rules for the shared objects. If we have a file foo.so
@@ -550,14 +551,30 @@ load_shobj (const char *name)
error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"),
map->l_name);
+ if (fstat (fd, &st) < 0)
+ error (EXIT_FAILURE, errno, _("stat(%s) failure"), map->l_name);
+
+ /* We're depending on data that's being read from the file, so be a
+ bit paranoid here and make sure the requests are reasonable.
+ PREAD would have failed anyway, but this is more robust and
+ explains what happened better. */
+#define PCHECK(s,l) if ((s) < 0 || (l) < 0 \
+ || ((s)+(l)) < 0 || ((s)+(l)) > st.st_size) \
+ error (EXIT_FAILURE, ERANGE, \
+ _("read outside of file extents %ld + %ld > %ld"), \
+ (long int)(s), (long int)(l), st.st_size)
+
/* Map the section header. */
size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr));
shdr = (ElfW(Shdr) *) alloca (size);
+ PCHECK (size, ehdr->e_shoff);
if (pread (fd, shdr, size, ehdr->e_shoff) != size)
error (EXIT_FAILURE, errno, _("reading of section headers failed"));
/* Get the section header string table. */
char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size);
+ PCHECK (shdr[ehdr->e_shstrndx].sh_size,
+ shdr[ehdr->e_shstrndx].sh_offset);
if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size,
shdr[ehdr->e_shstrndx].sh_offset)
!= shdr[ehdr->e_shstrndx].sh_size)
@@ -585,6 +602,7 @@ load_shobj (const char *name)
size_t size = debuglink_entry->sh_size;
char *debuginfo_fname = (char *) alloca (size + 1);
debuginfo_fname[size] = '\0';
+ PCHECK (size, debuglink_entry->sh_offset);
if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset)
!= size)
{
@@ -638,8 +656,13 @@ load_shobj (const char *name)
if (fd2 != -1)
{
ElfW(Ehdr) ehdr2;
+ struct stat st;
+
+ if (fstat (fd2, &st) < 0)
+ error (EXIT_FAILURE, errno, _("stat(%s) failure"), workbuf);
/* Read the ELF header. */
+ PCHECK (sizeof (ehdr2), 0);
if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2))
error (EXIT_FAILURE, errno,
_("reading of ELF header failed"));
@@ -647,12 +670,15 @@ load_shobj (const char *name)
/* Map the section header. */
size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr));
ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size);
+ PCHECK (size, ehdr2.e_shoff);
if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size)
error (EXIT_FAILURE, errno,
_("reading of section headers failed"));
/* Get the section header string table. */
shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size);
+ PCHECK (shdr2[ehdr2.e_shstrndx].sh_size,
+ shdr2[ehdr2.e_shstrndx].sh_offset);
if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size,
shdr2[ehdr2.e_shstrndx].sh_offset)
!= shdr2[ehdr2.e_shstrndx].sh_size)
More information about the Libc-alpha
mailing list