[binutils-gdb] readelf reloc range check

Alan Modra amodra@sourceware.org
Mon Oct 6 03:05:24 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=236cf2bf6089ba627723303bc530f7aa33640ab7

commit 236cf2bf6089ba627723303bc530f7aa33640ab7
Author: Alan Modra <amodra@gmail.com>
Date:   Mon Oct 6 09:08:26 2025 +1030

    readelf reloc range check
    
    A fuzzed object file hit this sanitizer error.
    readelf.c:16764:9: runtime error: pointer index expression with base
    0x6dd4491e1590 overflowed to 0xe7af96d4491e17a1
    
    The same could occur in any of the IN_RANGE reloc checks, where the
    reloc address is calculated as "start + r_offset" then compared
    against "start" and "end".  So don't do that.  Compare r_offset
    against the memory size, first.
    
            * readelf.c (IN_RANGE): Delete.
            (in_range): New inline funcion.
            (target_specific_reloc_handling): Replace "end" param with
            "size".  Update uses.  Replace IN_RANGE with in_range.
            (apply_relocations): Delete "end" variable.  Update
            target_specific_reloc_handling calls and replace IN_RANGE.
            Avoid pointer overflow.

Diff:
---
 binutils/readelf.c | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/binutils/readelf.c b/binutils/readelf.c
index fd9722c8afc..2d1d20413c4 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -15503,12 +15503,13 @@ process_syminfo (Filedata * filedata)
   return true;
 }
 
-/* A macro which evaluates to TRUE if the region ADDR .. ADDR + NELEM
-   is contained by the region START .. END.  The types of ADDR, START
-   and END should all be the same.  Note both ADDR + NELEM and END
-   point to just beyond the end of the regions that are being tested.  */
-#define IN_RANGE(START,END,ADDR,NELEM)		\
-  (((ADDR) >= (START)) && ((ADDR) < (END)) && ((ADDR) + (NELEM) <= (END)))
+/* Check that reloc at R_OFFSET of size R_SIZE can apply to LEN bytes.  */
+
+static inline bool
+in_range (size_t len, bfd_vma r_offset, unsigned int r_size)
+{
+  return r_offset <= len && r_size <= len - r_offset;
+}
 
 /* Check to see if the given reloc needs to be handled in a target specific
    manner.  If so then process the reloc and return TRUE otherwise return
@@ -15522,7 +15523,7 @@ static bool
 target_specific_reloc_handling (Filedata *filedata,
 				Elf_Internal_Rela *reloc,
 				unsigned char *start,
-				unsigned char *end,
+				size_t size,
 				Elf_Internal_Sym *symtab,
 				uint64_t num_syms)
 {
@@ -15550,9 +15551,9 @@ target_specific_reloc_handling (Filedata *filedata,
 		unsigned int reloc_size = 0;
 		int leb_ret = 0;
 
-		if (reloc->r_offset < (size_t) (end - start))
-		  value = read_leb128 (start + reloc->r_offset, end, false,
-				       &reloc_size, &leb_ret);
+		if (reloc->r_offset < size)
+		  value = read_leb128 (start + reloc->r_offset, start + size,
+				       false, &reloc_size, &leb_ret);
 		if (leb_ret != 0 || reloc_size == 0 || reloc_size > 8)
 		  error (_("LoongArch ULEB128 field at 0x%lx contains invalid "
 			   "ULEB128 value\n"),
@@ -15650,8 +15651,8 @@ target_specific_reloc_handling (Filedata *filedata,
 		    break;
 		  case 11: /* R_MSP430_GNU_SET_ULEB128 */
 		  case 22: /* R_MSP430X_GNU_SET_ULEB128 */
-		    if (reloc->r_offset < (size_t) (end - start))
-		      read_leb128 (start + reloc->r_offset, end, false,
+		    if (reloc->r_offset < size)
+		      read_leb128 (start + reloc->r_offset, start + size, false,
 				   &reloc_size, &leb_ret);
 		    break;
 		  default:
@@ -15671,7 +15672,7 @@ target_specific_reloc_handling (Filedata *filedata,
 		    value = reloc->r_addend + (symtab[sym_index].st_value
 					       - saved_sym->st_value);
 
-		    if (IN_RANGE (start, end, start + reloc->r_offset, reloc_size))
+		    if (in_range (size, reloc->r_offset, reloc_size))
 		      byte_put (start + reloc->r_offset, value, reloc_size);
 		    else
 		      /* PR 21137 */
@@ -15731,7 +15732,7 @@ target_specific_reloc_handling (Filedata *filedata,
 		    value = reloc->r_addend + (symtab[sym_index].st_value
 					       - saved_sym->st_value);
 
-		    if (IN_RANGE (start, end, start + reloc->r_offset, reloc_size))
+		    if (in_range (size, reloc->r_offset, reloc_size))
 		      byte_put (start + reloc->r_offset, value, reloc_size);
 		    else
 		      error (_("MN10300 sym diff reloc contains invalid offset:"
@@ -15784,7 +15785,7 @@ target_specific_reloc_handling (Filedata *filedata,
 	    break;
 
 	  case 0x41: /* R_RL78_ABS32.  */
-	    if (IN_RANGE (start, end, start + reloc->r_offset, 4))
+	    if (in_range (size, reloc->r_offset, 4))
 	      byte_put (start + reloc->r_offset, value, 4);
 	    else
 	      error (_("RL78 sym diff reloc contains invalid offset: "
@@ -15794,7 +15795,7 @@ target_specific_reloc_handling (Filedata *filedata,
 	    return true;
 
 	  case 0x43: /* R_RL78_ABS16.  */
-	    if (IN_RANGE (start, end, start + reloc->r_offset, 2))
+	    if (in_range (size, reloc->r_offset, 2))
 	      byte_put (start + reloc->r_offset, value, 2);
 	    else
 	      error (_("RL78 sym diff reloc contains invalid offset: "
@@ -16627,7 +16628,6 @@ apply_relocations (Filedata *filedata,
 		   uint64_t *num_relocs_return)
 {
   Elf_Internal_Shdr * relsec;
-  unsigned char * end = start + size;
 
   if (relocs_return != NULL)
     {
@@ -16698,7 +16698,7 @@ apply_relocations (Filedata *filedata,
 
 	  reloc_type = get_reloc_type (filedata, rp->r_info);
 
-	  if (target_specific_reloc_handling (filedata, rp, start, end, symtab, num_syms))
+	  if (target_specific_reloc_handling (filedata, rp, start, size, symtab, num_syms))
 	    continue;
 	  else if (is_none_reloc (filedata, reloc_type))
 	    continue;
@@ -16761,8 +16761,7 @@ apply_relocations (Filedata *filedata,
 	      continue;
 	    }
 
-	  rloc = start + rp->r_offset;
-	  if (!IN_RANGE (start, end, rloc, reloc_size))
+	  if (!in_range (size, rp->r_offset, reloc_size))
 	    {
 	      warn (_("skipping invalid relocation offset %#" PRIx64
 		      " in section %s\n"),
@@ -16770,6 +16769,7 @@ apply_relocations (Filedata *filedata,
 		    printable_section_name (filedata, section));
 	      continue;
 	    }
+	  rloc = start + rp->r_offset;
 
 	  sym_index = get_reloc_symindex (rp->r_info);
 	  if (sym_index >= num_syms)
@@ -16856,7 +16856,7 @@ apply_relocations (Filedata *filedata,
       free (symtab);
       /* Let the target specific reloc processing code know that
 	 we have finished with these relocs.  */
-      target_specific_reloc_handling (filedata, NULL, NULL, NULL, NULL, 0);
+      target_specific_reloc_handling (filedata, NULL, NULL, 0, NULL, 0);
 
       if (relocs_return)
 	{


More information about the Binutils-cvs mailing list