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.
This commit is contained in:
Alan Modra
2025-10-06 09:08:26 +10:30
parent f2a3ccf127
commit 236cf2bf60

View File

@@ -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)
{