From 5e5c0394d82c53e97750fe7b18023e6f84157b81 Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mark@klomp.org>
Date: Sat, 8 Feb 2025 21:44:56 +0100
Subject: [PATCH] libelf, readelf: Use validate_str also to check dynamic
 symstr data

When dynsym/str was read through eu-readelf --dynamic by readelf
process_symtab the string data was not validated, possibly printing
unallocated memory past the end of the symstr data. Fix this by
turning the elf_strptr validate_str function into a generic
lib/system.h helper function and use it in readelf to validate the
strings before use.

	* libelf/elf_strptr.c (validate_str): Remove to...
	* lib/system.h (validate_str): ... here. Make inline, simplify
	check and document.
	* src/readelf.c (process_symtab): Use validate_str on symstr_data.

https://sourceware.org/bugzilla/show_bug.cgi?id=32654

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 lib/system.h        | 27 +++++++++++++++++++++++++++
 libelf/elf_strptr.c | 18 ------------------
 src/readelf.c       | 18 +++++++++++++++---
 3 files changed, 42 insertions(+), 21 deletions(-)

diff --git a/lib/system.h b/lib/system.h
index 0db12d99..0698e5ff 100644
--- a/lib/system.h
+++ b/lib/system.h
@@ -34,6 +34,7 @@
 #include <config.h>
 
 #include <errno.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <string.h>
@@ -117,6 +118,32 @@ startswith (const char *str, const char *prefix)
   return strncmp (str, prefix, strlen (prefix)) == 0;
 }
 
+/* Return TRUE if STR[FROM] is a valid string with a zero terminator
+   at or before STR[TO - 1].  Note FROM is an index into the STR
+   array, while TO is the maximum size of the STR array.  This
+   function returns FALSE when TO is zero or FROM >= TO.  */
+static inline bool
+validate_str (const char *str, size_t from, size_t to)
+{
+#if HAVE_DECL_MEMRCHR
+  // Check end first, which is likely a zero terminator,
+  // to prevent function call
+  return (to > 0
+	  && (str[to - 1] == '\0'
+	      || (to > from
+		  && memrchr (&str[from], '\0', to - from - 1) != NULL)));
+#else
+  do {
+    if (to <= from)
+      return false;
+
+    to--;
+  } while (str[to]);
+
+  return true;
+#endif
+}
+
 /* A special gettext function we use if the strings are too short.  */
 #define sgettext(Str) \
   ({ const char *__res = strrchr (_(Str), '|');			      \
diff --git a/libelf/elf_strptr.c b/libelf/elf_strptr.c
index 79a24d25..c5a94f82 100644
--- a/libelf/elf_strptr.c
+++ b/libelf/elf_strptr.c
@@ -53,24 +53,6 @@ get_zdata (Elf_Scn *strscn)
   return zdata;
 }
 
-static bool validate_str (const char *str, size_t from, size_t to)
-{
-#if HAVE_DECL_MEMRCHR
-  // Check end first, which is likely a zero terminator, to prevent function call
-  return ((to > 0 && str[to - 1]  == '\0')
-	  || (to - from > 0 && memrchr (&str[from], '\0', to - from - 1) != NULL));
-#else
-  do {
-    if (to <= from)
-      return false;
-
-    to--;
-  } while (str[to]);
-
-  return true;
-#endif
-}
-
 char *
 elf_strptr (Elf *elf, size_t idx, size_t offset)
 {
diff --git a/src/readelf.c b/src/readelf.c
index 6526db07..c43fda35 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -2639,6 +2639,7 @@ process_symtab (Ebl *ebl, unsigned int nsyms, Elf64_Word idx,
       char typebuf[64];
       char bindbuf[64];
       char scnbuf[64];
+      const char *sym_name;
       Elf32_Word xndx;
       GElf_Sym sym_mem;
       GElf_Sym *sym
@@ -2650,6 +2651,19 @@ process_symtab (Ebl *ebl, unsigned int nsyms, Elf64_Word idx,
       /* Determine the real section index.  */
       if (likely (sym->st_shndx != SHN_XINDEX))
         xndx = sym->st_shndx;
+      if (use_dynamic_segment == true)
+	{
+	  if (validate_str (symstr_data->d_buf, sym->st_name,
+			    symstr_data->d_size))
+	    sym_name = (char *)symstr_data->d_buf + sym->st_name;
+	  else
+	    sym_name = NULL;
+	}
+      else
+	sym_name = elf_strptr (ebl->elf, idx, sym->st_name);
+
+      if (sym_name == NULL)
+	sym_name = "???";
 
       printf (_ ("\
 %5u: %0*" PRIx64 " %6" PRId64 " %-7s %-6s %-9s %6s %s"),
@@ -2662,9 +2676,7 @@ process_symtab (Ebl *ebl, unsigned int nsyms, Elf64_Word idx,
               get_visibility_type (GELF_ST_VISIBILITY (sym->st_other)),
               ebl_section_name (ebl, sym->st_shndx, xndx, scnbuf,
                                 sizeof (scnbuf), NULL, shnum),
-              use_dynamic_segment == true
-                  ? (char *)symstr_data->d_buf + sym->st_name
-                  : elf_strptr (ebl->elf, idx, sym->st_name));
+              sym_name);
 
       if (versym_data != NULL)
         {
-- 
2.27.0

