From: Alan Modra Date: Sun, 5 Oct 2025 22:38:26 +0000 (+1030) Subject: readelf reloc range check X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=236cf2bf6089ba627723303bc530f7aa33640ab7;p=thirdparty%2Fbinutils-gdb.git 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 --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) {