The fuzzers have found the reloc special functions in coff-aarch64.c

All of them need a bfd_reloc_offset_in_range check before accessing
data + reloc_entry->address.  This patch adds the missing checks and
sanity checks reloc offsets in coff_pe_aarch64_relocate_section too.

All of them also need changing to support objdump -W calls to
bfd_simple_get_relocated_section_contents.  At least, secrel_reloc
needs the support, the others might not be present in dwarf debug
sections.

	* coff-aarch64.c (coff_aarch64_rel21_reloc): Range check
	reloc offset.  Support final-linking.
	(coff_aarch64_po12l_reloc): Likewise.
	(coff_aarch64_addr32nb_reloc): Likewise.
	(coff_aarch64_secrel_reloc): Likewise.
	(coff_pe_aarch64_relocate_section): Range check reloc offset.
This commit is contained in:
Alan Modra
2023-01-17 21:53:00 +10:30
parent f07170eb86
commit 066bd43411

View File

@ -39,48 +39,85 @@
#include "libcoff.h" #include "libcoff.h"
/* For these howto special functions,
output_bfd == NULL => final link, or objdump -W and other calls to
bfd_simple_get_relocated_section_contents
output_bfd != NULL && output_bfd != abfd => ld -r
output_bfd != NULL && output_bfd == abfd => gas.
FIXME: ld -r is punted to bfd_perform_relocation. This won't be
correct for cases where the addend needs to be adjusted, eg. for
relocations against section symbols, and the field is split because
bfd_perform_relocation can't write addends to split relocation fields. */
static bfd_reloc_status_type static bfd_reloc_status_type
coff_aarch64_rel21_reloc (bfd *abfd ATTRIBUTE_UNUSED, coff_aarch64_rel21_reloc (bfd *abfd,
arelent *reloc_entry, arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED, asymbol *symbol,
void *data, void *data,
asection *input_section ATTRIBUTE_UNUSED, asection *input_section,
bfd *output_bfd ATTRIBUTE_UNUSED, bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED) char **error_message ATTRIBUTE_UNUSED)
{ {
uint32_t op; if (output_bfd != NULL && output_bfd != abfd)
int32_t param; return bfd_reloc_continue;
op = bfd_getl32 (data + reloc_entry->address); if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
param = reloc_entry->addend; input_section, reloc_entry->address))
return bfd_reloc_outofrange;
if (param > 0xfffff || param < -0x100000) uint32_t op = bfd_getl32 (data + reloc_entry->address);
return bfd_reloc_overflow; bfd_vma relocation = reloc_entry->addend;
bfd_reloc_status_type ret = bfd_reloc_ok;
if (output_bfd == NULL)
{
if (bfd_is_und_section (symbol->section))
{
if ((symbol->flags & BSF_WEAK) == 0)
ret = bfd_reloc_undefined;
}
else if (!bfd_is_com_section (symbol->section))
relocation += (symbol->value
+ symbol->section->output_offset
+ symbol->section->output_section->vma);
bfd_vma addend = ((op >> 3) & 0x1ffffc) | ((op >> 29) & 0x3);
addend = (addend ^ 0x100000) - 0x100000;
relocation += addend;
relocation -= (reloc_entry->address
+ input_section->output_offset
+ input_section->output_section->vma);
relocation = (bfd_signed_vma) relocation >> reloc_entry->howto->rightshift;
}
if (relocation + 0x100000 > 0x1fffff)
ret = bfd_reloc_overflow;
op &= 0x9f00001f; op &= 0x9f00001f;
op |= (param & 0x1ffffc) << 3; op |= (relocation & 0x1ffffc) << 3;
op |= (param & 0x3) << 29; op |= (relocation & 0x3) << 29;
bfd_putl32 (op, data + reloc_entry->address); bfd_putl32 (op, data + reloc_entry->address);
return bfd_reloc_ok; return ret;
} }
static bfd_reloc_status_type static bfd_reloc_status_type
coff_aarch64_po12l_reloc (bfd *abfd ATTRIBUTE_UNUSED, coff_aarch64_po12l_reloc (bfd *abfd,
arelent *reloc_entry, arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED, asymbol *symbol ATTRIBUTE_UNUSED,
void *data, void *data,
asection *input_section ATTRIBUTE_UNUSED, asection *input_section,
bfd *output_bfd ATTRIBUTE_UNUSED, bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED) char **error_message ATTRIBUTE_UNUSED)
{ {
uint32_t op; if (output_bfd != NULL && output_bfd != abfd)
int32_t param; return bfd_reloc_continue;
uint8_t shift;
op = bfd_getl32 (data + reloc_entry->address); if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
param = reloc_entry->addend & 0xfff; input_section, reloc_entry->address))
return bfd_reloc_outofrange;
uint32_t op = bfd_getl32 (data + reloc_entry->address);
bfd_vma relocation = reloc_entry->addend & 0xfff;
int shift;
if ((op & 0xff800000) == 0x3d800000) if ((op & 0xff800000) == 0x3d800000)
{ {
@ -93,53 +130,120 @@ coff_aarch64_po12l_reloc (bfd *abfd ATTRIBUTE_UNUSED,
shift = op >> 30; shift = op >> 30;
} }
if (param & ((1 << shift) - 1)) bfd_reloc_status_type ret = bfd_reloc_ok;
return bfd_reloc_overflow; if (output_bfd == NULL)
{
if (bfd_is_und_section (symbol->section))
{
if ((symbol->flags & BSF_WEAK) == 0)
ret = bfd_reloc_undefined;
}
else if (!bfd_is_com_section (symbol->section))
relocation += (symbol->value
+ symbol->section->output_offset
+ symbol->section->output_section->vma);
bfd_vma addend = (op >> 10) & 0xfff;
addend <<= shift;
relocation += addend;
}
param >>= shift; if (relocation & ((1 << shift) - 1))
ret = bfd_reloc_overflow;
op &= 0xffc003ff; op &= 0xffc003ff;
op |= param << 10; op |= (relocation >> shift << 10) & 0x3ffc00;
bfd_putl32 (op, data + reloc_entry->address); bfd_putl32 (op, data + reloc_entry->address);
return bfd_reloc_ok; return ret;
} }
static bfd_reloc_status_type static bfd_reloc_status_type
coff_aarch64_addr32nb_reloc (bfd *abfd ATTRIBUTE_UNUSED, coff_aarch64_addr32nb_reloc (bfd *abfd,
arelent *reloc_entry, arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED, asymbol *symbol ATTRIBUTE_UNUSED,
void *data, void *data,
asection *input_section ATTRIBUTE_UNUSED, asection *input_section,
bfd *output_bfd ATTRIBUTE_UNUSED, bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED) char **error_message)
{ {
uint64_t val; if (output_bfd != NULL && output_bfd != abfd)
return bfd_reloc_continue;
if ((int64_t) reloc_entry->addend > 0x7fffffff if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
|| (int64_t) reloc_entry->addend < -0x7fffffff) input_section, reloc_entry->address))
return bfd_reloc_overflow; return bfd_reloc_outofrange;
val = reloc_entry->addend; bfd_vma relocation = reloc_entry->addend;
bfd_reloc_status_type ret = bfd_reloc_ok;
if (output_bfd == NULL)
{
if (bfd_is_und_section (symbol->section))
{
if ((symbol->flags & BSF_WEAK) == 0)
ret = bfd_reloc_undefined;
}
else if (!bfd_is_com_section (symbol->section))
relocation += (symbol->value
+ symbol->section->output_offset
+ symbol->section->output_section->vma);
bfd_vma addend = bfd_getl_signed_32 (data + reloc_entry->address);
relocation += addend;
if (bfd_get_flavour (output_bfd) == bfd_target_coff_flavour
&& obj_pe (output_bfd))
relocation -= pe_data (output_bfd)->pe_opthdr.ImageBase;
else
{
*error_message = "unsupported";
return bfd_reloc_dangerous;
}
}
bfd_putl32 ((uint32_t) val, data + reloc_entry->address); if (relocation + 0x80000000 > 0xffffffff)
ret = bfd_reloc_overflow;
return bfd_reloc_ok; bfd_putl32 (relocation, data + reloc_entry->address);
return ret;
} }
static bfd_reloc_status_type static bfd_reloc_status_type
coff_aarch64_secrel_reloc (bfd *abfd ATTRIBUTE_UNUSED, coff_aarch64_secrel_reloc (bfd *abfd,
arelent *reloc_entry, arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED, asymbol *symbol ATTRIBUTE_UNUSED,
void *data, void *data,
asection *input_section ATTRIBUTE_UNUSED, asection *input_section,
bfd *output_bfd ATTRIBUTE_UNUSED, bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED) char **error_message ATTRIBUTE_UNUSED)
{ {
bfd_putl32 (reloc_entry->addend, data + reloc_entry->address); if (output_bfd != NULL && output_bfd != abfd)
return bfd_reloc_continue;
return bfd_reloc_ok; if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
input_section, reloc_entry->address))
return bfd_reloc_outofrange;
bfd_vma relocation = reloc_entry->addend;
bfd_reloc_status_type ret = bfd_reloc_ok;
if (output_bfd == NULL)
{
if (bfd_is_und_section (symbol->section))
{
if ((symbol->flags & BSF_WEAK) == 0)
ret = bfd_reloc_undefined;
}
else if (!bfd_is_com_section (symbol->section))
relocation += (symbol->value
+ symbol->section->output_offset);
bfd_vma addend = bfd_getl_signed_32 (data + reloc_entry->address);
relocation += addend;
}
if (relocation > 0xffffffff)
ret = bfd_reloc_overflow;
bfd_putl32 (relocation, data + reloc_entry->address);
return ret;
} }
#define coff_aarch64_NULL NULL #define coff_aarch64_NULL NULL
@ -438,6 +542,17 @@ coff_pe_aarch64_relocate_section (bfd *output_bfd,
|| (unsigned long) symndx >= obj_raw_syment_count (input_bfd)) || (unsigned long) symndx >= obj_raw_syment_count (input_bfd))
continue; continue;
/* All the relocs handled below operate on 4 bytes. */
if (input_section->size < rel->r_vaddr
|| input_section->size - rel->r_vaddr < 4)
{
_bfd_error_handler
/* xgettext: c-format */
(_("%pB: bad reloc address %#" PRIx64 " in section `%pA'"),
input_bfd, (uint64_t) rel->r_vaddr, input_section);
continue;
}
switch (rel->r_type) switch (rel->r_type)
{ {
case IMAGE_REL_ARM64_ADDR32NB: case IMAGE_REL_ARM64_ADDR32NB: