Hi,

The attached patch fixes a heap out-of-bounds read in libdw when an inline
DW_FORM_string attribute is not null-terminated inside its unit.

dwarf_formstring returns attrp->valp for DW_FORM_string without checking
for a terminating NUL. dwarf_getattrs passes each attribute to its callback
before __libdw_form_val_len validates the value, and dwarf_attr (through
__libdw_find_attr) returns the matching attribute without computing its
length, so a DW_FORM_string that runs to the end of its unit is returned
unterminated and callers read past the section data. Commit fda09f5f added
this guarantee for the .debug_str and .debug_line_str forms; the patch
extends it to DW_FORM_string by requiring a NUL before cu->endp, which
covers every caller.

Reproducible with eu-readelf --debug-dump=info on a crafted ELF whose
zlib-compressed .debug_info ends in an unterminated DW_FORM_string; ASAN
reports a heap-buffer-overflow READ just past the decompressed section
buffer. The same overread happens in a plain dwarf_diename caller and
inside libdw in dwarf_getsrcfiles (read_srcfiles, unterminated
DW_AT_comp_dir). The patch (git format-patch, applies on HEAD 10b728d8) is
attached. I have small reproducer files for all three cases and can send
them, or add them as a test case, if that helps.

Thanks,
Sujal Tuladhar
From 95295523d58ca3ba65b56fb38a98755fe01485dc Mon Sep 17 00:00:00 2001
From: Sujal Tuladhar <[email protected]>
Date: Thu, 5 Nov 2026 08:08:23 +0000
Subject: [PATCH] libdw: check that DW_FORM_string values are null-terminated

dwarf_formstring returns the value pointer of a DW_FORM_string
attribute as is:

    if (attrp->form == DW_FORM_string)
      /* A simple inlined string.  */
      return (const char *) attrp->valp;

The terminating NUL is only checked by __libdw_form_val_len, but
dwarf_getattrs calls its callback before it computes the length of the
current attribute, and dwarf_attr (through __libdw_find_attr) returns
the matching attribute without computing its length either.  So when
the last attribute of a DIE is a DW_FORM_string that runs to the end
of its unit without a terminating NUL, callers get a string that is
not null-terminated and read past the end of the section data.

Commit fda09f5f ("libdw: check that DWARF strings are null-terminated")
added this guarantee for the .debug_str and .debug_line_str forms.
Extend it to DW_FORM_string by requiring a NUL between the value and
the end of its unit, so every caller of dwarf_formstring is covered.

Reproducible with a crafted ELF whose zlib-compressed .debug_info ends
in an unterminated DW_FORM_string.  AddressSanitizer reports a
heap-buffer-overflow READ just past the decompressed section buffer in
eu-readelf --debug-dump=info, in a plain dwarf_diename caller, and
inside libdw itself in dwarf_getsrcfiles (read_srcfiles reading an
unterminated DW_AT_comp_dir).

	* libdw/dwarf_formstring.c (dwarf_formstring): Check that a
	DW_FORM_string is null-terminated inside its unit.

Signed-off-by: Sujal Tuladhar <[email protected]>
---
 libdw/dwarf_formstring.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/libdw/dwarf_formstring.c b/libdw/dwarf_formstring.c
index 65f03a5e..7450c056 100644
--- a/libdw/dwarf_formstring.c
+++ b/libdw/dwarf_formstring.c
@@ -43,8 +43,20 @@ dwarf_formstring (Dwarf_Attribute *attrp)
 
   /* We found it.  Now determine where the string is stored.  */
   if (attrp->form == DW_FORM_string)
-    /* A simple inlined string.  */
-    return (const char *) attrp->valp;
+    {
+      /* A simple inlined string.  dwarf_getattrs and dwarf_attr hand out
+	 the attribute before its value length is checked, so make sure the
+	 string is null-terminated inside its unit.  */
+      const unsigned char *endp = attrp->cu->endp;
+      if (unlikely (attrp->valp >= endp
+		    || memchr (attrp->valp, '\0',
+			       (size_t) (endp - attrp->valp)) == NULL))
+	{
+	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
+	  return NULL;
+	}
+      return (const char *) attrp->valp;
+    }
 
   Dwarf_CU *cu = attrp->cu;
   Dwarf *dbg = cu->dbg;
-- 
2.43.0

Reply via email to