libdwfl: Don't try to convert too many bytes in dwfl_link_map_report

Message ID 20211208133606.7658-1-mark@klomp.org
State Committed
Headers
Series libdwfl: Don't try to convert too many bytes in dwfl_link_map_report |

Commit Message

Mark Wielaard Dec. 8, 2021, 1:36 p.m. UTC
  When trying to read (corrupt) phdrs from a core file we only want
to read and convert the bytes we could read. Also make sure we don't
try to allocate too big buffers.

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

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 libdwfl/ChangeLog  |  6 ++++++
 libdwfl/link_map.c | 17 +++++++++++++++--
 2 files changed, 21 insertions(+), 2 deletions(-)
  

Patch

diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 57b2c494..852b199a 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,9 @@ 
+2021-12-08  Mark Wielaard  <mark@klomp.org>
+
+	* link_map.c (dwfl_link_map_report): Limit malloc size to max
+	possible. When converting make sure we don't exceed the number
+	of bytes available in either in.d_buf nor out.d_buf.
+
 2021-11-18  Matthias Maennich  <maennich@google.com>
 
 	* linux-kernel-modules.c (dwfl_linux_kernel_report_modules):
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
index 1e7d4502..1c298a8e 100644
--- a/libdwfl/link_map.c
+++ b/libdwfl/link_map.c
@@ -847,6 +847,11 @@  dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 	      /* Note this in the !in_ok path.  That means memory_callback
 		 failed.  But the callback might still have reset the d_size
 		 value (to zero).  So explicitly set it here again.  */
+	      if (unlikely (phnum > SIZE_MAX / phent))
+		{
+		  __libdwfl_seterrno (DWFL_E_NOMEM);
+		  return false;
+		}
 	      in.d_size = phnum * phent;
 	      in.d_buf = malloc (in.d_size);
 	      if (unlikely (in.d_buf == NULL))
@@ -876,6 +881,13 @@  dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 		  return false;
 		}
 	      size_t nbytes = phnum * phent;
+	      /* We can only process as many bytes/phnum as there are
+		 in in.d_size. The data might have been truncated.  */
+	      if (nbytes > in.d_size)
+		{
+		  nbytes = in.d_size;
+		  phnum = nbytes / phent;
+		}
 	      void *buf = malloc (nbytes);
 	      Elf32_Phdr (*p32)[phnum] = buf;
 	      Elf64_Phdr (*p64)[phnum] = buf;
@@ -888,10 +900,11 @@  dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 		{
 		  .d_type = ELF_T_PHDR,
 		  .d_version = EV_CURRENT,
-		  .d_size = phnum * phent,
+		  .d_size = nbytes,
 		  .d_buf = buf
 		};
-	      in.d_size = out.d_size;
+	      if (in.d_size > out.d_size)
+		in.d_size = out.d_size;
 	      if (likely ((elfclass == ELFCLASS32
 			   ? elf32_xlatetom : elf64_xlatetom)
 			  (&out, &in, elfdata) != NULL))