From 733ae98cb8fb0d5653887cfd79aec3cfe5e44846 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Wed, 8 Sep 2021 13:18:02 +0930 Subject: [PATCH] PowerPC64, sanity check r_offset in relocate_section This hardens the powerpc64 linker code transformations. * elf64-ppc.c (is_8byte_reloc, offset_in_range): New functions. (ppc64_elf_relocate_section): Sanity check r_offset before accessing section contents for various code transformations. --- bfd/elf64-ppc.c | 168 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 49 deletions(-) diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index aa997bb42ab..4ebacbd9945 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -4601,6 +4601,26 @@ is_plt_seq_reloc (enum elf_ppc64_reloc_type r_type) || r_type == R_PPC64_PLTSEQ_NOTOC); } +/* Of relocs which might appear paired with TLSGD and TLSLD marker + relocs, return true for those that operate on a dword. */ + +static bool +is_8byte_reloc (enum elf_ppc64_reloc_type r_type) +{ + return (r_type == R_PPC64_PLT_PCREL34 + || r_type == R_PPC64_PLT_PCREL34_NOTOC + || r_type == R_PPC64_PLTCALL); +} + +/* Like bfd_reloc_offset_in_range but without a howto. Return true + iff a field of SIZE bytes at OFFSET is within SEC limits. */ + +static bool +offset_in_range (asection *sec, bfd_vma offset, size_t size) +{ + return offset <= sec->size && size <= sec->size - offset; +} + /* Look through the relocs for a section during the first phase, and calculate needed space in the global offset table, procedure linkage table, and dynamic reloc sections. */ @@ -15222,13 +15242,18 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_LO_DS_OPT: - insn = bfd_get_32 (input_bfd, contents + rel->r_offset - d_offset); - if ((insn & (0x3fu << 26)) != 58u << 26) - abort (); - insn += (14u << 26) - (58u << 26); - bfd_put_32 (input_bfd, insn, contents + rel->r_offset - d_offset); - r_type = R_PPC64_TOC16_LO; - rel->r_info = ELF64_R_INFO (r_symndx, r_type); + if (offset_in_range (input_section, rel->r_offset - d_offset, 4)) + { + insn = bfd_get_32 (input_bfd, + contents + rel->r_offset - d_offset); + if ((insn & (0x3fu << 26)) != 58u << 26) + abort (); + insn += (14u << 26) - (58u << 26); + bfd_put_32 (input_bfd, insn, + contents + rel->r_offset - d_offset); + r_type = R_PPC64_TOC16_LO; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } break; case R_PPC64_TOC16: @@ -15280,7 +15305,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TPREL16_HI: case R_PPC64_GOT_TPREL16_HA: if ((tls_mask & TLS_TLS) != 0 - && (tls_mask & TLS_TPREL) == 0) + && (tls_mask & TLS_TPREL) == 0 + && offset_in_range (input_section, rel->r_offset - d_offset, 4)) { rel->r_offset -= d_offset; bfd_put_32 (input_bfd, NOP, contents + rel->r_offset); @@ -15292,7 +15318,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_TPREL16_LO_DS: if ((tls_mask & TLS_TLS) != 0 - && (tls_mask & TLS_TPREL) == 0) + && (tls_mask & TLS_TPREL) == 0 + && offset_in_range (input_section, rel->r_offset - d_offset, 4)) { toctprel: insn = bfd_get_32 (input_bfd, @@ -15317,7 +15344,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TPREL_PCREL34: if ((tls_mask & TLS_TLS) != 0 - && (tls_mask & TLS_TPREL) == 0) + && (tls_mask & TLS_TPREL) == 0 + && offset_in_range (input_section, rel->r_offset, 8)) { /* pld ra,sym@got@tprel@pcrel -> paddi ra,r13,sym@tprel */ pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset); @@ -15336,7 +15364,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TLS: if ((tls_mask & TLS_TLS) != 0 - && (tls_mask & TLS_TPREL) == 0) + && (tls_mask & TLS_TPREL) == 0 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); insn = _bfd_elf_ppc_at_tls_transform (insn, 13); @@ -15387,13 +15416,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_HA: tls_gd = TLS_GDIE; - if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0) + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) goto tls_gdld_hi; break; case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TLSLD16_HA: - if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0) + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { tls_gdld_hi: if ((tls_mask & tls_gd) != 0) @@ -15412,13 +15443,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: tls_gd = TLS_GDIE; - if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0) + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) goto tls_ldgd_opt; break; case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_LO: - if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0) + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { unsigned int insn1, insn2; @@ -15488,10 +15521,11 @@ ppc64_elf_relocate_section (bfd *output_bfd, } bfd_put_32 (input_bfd, insn1, contents + rel->r_offset - d_offset); - if (offset != (bfd_vma) -1) + if (offset != (bfd_vma) -1 + && offset_in_range (input_section, offset, 4)) { bfd_put_32 (input_bfd, insn2, contents + offset); - if (offset + 8 <= input_section->size) + if (offset_in_range (input_section, offset + 4, 4)) { insn2 = bfd_get_32 (input_bfd, contents + offset + 4); if (insn2 == LD_R2_0R1 + STK_TOC (htab)) @@ -15509,7 +15543,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_GOT_TLSGD_PCREL34: - if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0) + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0 + && offset_in_range (input_section, rel->r_offset, 8)) { pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset); pinsn <<= 32; @@ -15535,7 +15570,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_GOT_TLSLD_PCREL34: - if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0) + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0 + && offset_in_range (input_section, rel->r_offset, 8)) { pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset); pinsn <<= 32; @@ -15555,7 +15591,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TLSGD: if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0 - && rel + 1 < relend) + && rel + 1 < relend + && offset_in_range (input_section, rel->r_offset, + is_8byte_reloc (ELF64_R_TYPE (rel[1].r_info)) + ? 8 : 4)) { unsigned int insn2; enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info); @@ -15571,7 +15610,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; } - if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL) + if (r_type1 == R_PPC64_PLTCALL) bfd_put_32 (output_bfd, NOP, contents + offset + 4); if ((tls_mask & TLS_GDIE) != 0) @@ -15615,7 +15654,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TLSLD: if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0 - && rel + 1 < relend) + && rel + 1 < relend + && offset_in_range (input_section, rel->r_offset, + is_8byte_reloc (ELF64_R_TYPE (rel[1].r_info)) + ? 8 : 4)) { unsigned int insn2; enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info); @@ -15631,7 +15673,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; } - if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL) + if (r_type1 == R_PPC64_PLTCALL) bfd_put_32 (output_bfd, NOP, contents + offset + 4); if (r_type1 == R_PPC64_REL24_NOTOC @@ -15663,7 +15705,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, && rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPREL64) && rel[1].r_offset == rel->r_offset + 8) { - if ((tls_mask & TLS_GD) == 0) + if ((tls_mask & TLS_GD) == 0 + && offset_in_range (input_section, rel->r_offset, 8)) { rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_NONE); if ((tls_mask & TLS_GDIE) != 0) @@ -15678,7 +15721,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, } else { - if ((tls_mask & TLS_LD) == 0) + if ((tls_mask & TLS_LD) == 0 + && offset_in_range (input_section, rel->r_offset, 8)) { bfd_put_64 (output_bfd, 1, contents + rel->r_offset); r_type = R_PPC64_NONE; @@ -15699,7 +15743,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, relocation = TOCstart + htab->sec_info[input_section->id].toc_off; if (!bfd_link_pic (info) && !info->traditional_format - && relocation + 0x80008000 <= 0xffffffff) + && relocation + 0x80008000 <= 0xffffffff + && offset_in_range (input_section, rel->r_offset, 8)) { unsigned int insn1, insn2; @@ -15721,7 +15766,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, relocation -= (rel->r_offset + input_section->output_offset + input_section->output_section->vma); - if (relocation + 0x80008000 <= 0xffffffff) + if (relocation + 0x80008000 <= 0xffffffff + && offset_in_range (input_section, rel->r_offset, 8)) { unsigned int insn1, insn2; @@ -15758,7 +15804,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, && rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_REL16_LO) && rel[1].r_offset == rel->r_offset + 4 && rel[1].r_addend == rel->r_addend + 4 - && relocation + 0x80008000 <= 0xffffffff) + && relocation + 0x80008000 <= 0xffffffff + && offset_in_range (input_section, rel->r_offset - d_offset, 8)) { unsigned int insn1, insn2; offset = rel->r_offset - d_offset; @@ -15793,7 +15840,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, + input_section->output_offset + input_section->output_section->vma) && tocsave_find (htab, NO_INSERT, - &local_syms, rel, input_bfd)) + &local_syms, rel, input_bfd) + && offset_in_range (input_section, rel->r_offset, 4)) { insn = bfd_get_32 (input_bfd, contents + rel->r_offset); if (insn == NOP @@ -15813,6 +15861,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, /* Branch not taken prediction relocations. */ case R_PPC64_ADDR14_BRNTAKEN: case R_PPC64_REL14_BRNTAKEN: + if (!offset_in_range (input_section, rel->r_offset, 4)) + break; insn |= bfd_get_32 (input_bfd, contents + rel->r_offset) & ~(0x01 << 21); /* Fall through. */ @@ -15873,7 +15923,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, /* All of these stubs may modify r2, so there must be a branch and link followed by a nop. The nop is replaced by an insn to restore r2. */ - else if (rel->r_offset + 8 <= input_section->size) + else if (offset_in_range (input_section, rel->r_offset, 8)) { unsigned long br; @@ -16111,7 +16161,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, && (r_type == R_PPC64_REL24 || r_type == R_PPC64_REL24_NOTOC) && relocation == 0 - && addend == 0) + && addend == 0 + && offset_in_range (input_section, rel->r_offset, 4)) { bfd_put_32 (input_bfd, NOP, contents + rel->r_offset); goto copy_reloc; @@ -16127,7 +16178,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, && sec != NULL && sec->output_section != NULL && !discarded_section (sec) - && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))) + && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); if ((insn & (0x3fu << 26 | 0x3)) == 58u << 26 /* ld */) @@ -16150,7 +16202,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, && sec != NULL && sec->output_section != NULL && !discarded_section (sec) - && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))) + && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); if (r_type == R_PPC64_GOT16_LO_DS @@ -16181,7 +16234,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, && sec != NULL && sec->output_section != NULL && !discarded_section (sec) - && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)))) + && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + && offset_in_range (input_section, rel->r_offset, 8))) break; offset = rel->r_offset; @@ -16205,7 +16259,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, && rel + 1 < relend && rel[1].r_offset == rel->r_offset && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT) - && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf))) + && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + && offset_in_range (input_section, rel->r_offset, 8)) { offset = rel->r_offset; pinsn = bfd_get_32 (input_bfd, contents + offset); @@ -16220,7 +16275,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, /* zero means next insn. */ off2 = 8; off2 += offset; - if (off2 + 4 <= input_section->size) + if (offset_in_range (input_section, off2, 4)) { uint64_t pinsn2; bfd_signed_vma addend_off; @@ -16228,7 +16283,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, pinsn2 <<= 32; if ((pinsn2 & (63ULL << 58)) == 1ULL << 58) { - if (off2 + 8 > input_section->size) + if (!offset_in_range (input_section, off2, 8)) break; pinsn2 |= bfd_get_32 (input_bfd, contents + off2 + 4); @@ -16659,7 +16714,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TPREL16_HIGHESTA: if (h != NULL && h->elf.root.type == bfd_link_hash_undefweak - && h->elf.dynindx == -1) + && h->elf.dynindx == -1 + && offset_in_range (input_section, rel->r_offset - d_offset, 4)) { /* Make this relocation against an undefined weak symbol resolve to zero. This is really just a tweak, since @@ -17041,7 +17097,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, htab->notoc_plt = 1; /* Fall through. */ case R_PPC64_PLTCALL: - if (unresolved_reloc) + if (unresolved_reloc + && offset_in_range (input_section, rel->r_offset, + r_type == R_PPC64_PLTCALL ? 8 : 4)) { /* No plt entry. Make this into a direct call. */ bfd_byte *p = contents + rel->r_offset; @@ -17069,7 +17127,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, htab->notoc_plt = 1; /* Fall through. */ case R_PPC64_PLT_PCREL34: - if (unresolved_reloc) + if (unresolved_reloc + && offset_in_range (input_section, rel->r_offset, 8)) { bfd_byte *p = contents + rel->r_offset; bfd_put_32 (input_bfd, PNOP >> 32, p); @@ -17097,9 +17156,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, { bfd_byte *p; nop_it: - p = contents + (rel->r_offset & ~3); - bfd_put_32 (input_bfd, NOP, p); - goto copy_reloc; + if (offset_in_range (input_section, rel->r_offset & ~3, 4)) + { + p = contents + (rel->r_offset & ~3); + bfd_put_32 (input_bfd, NOP, p); + goto copy_reloc; + } } break; @@ -17120,7 +17182,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TOC16_LO: case R_PPC64_TOC16_LO_DS: if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000 - && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn) + && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { bfd_byte *p = contents + (rel->r_offset & ~3); insn = bfd_get_32 (input_bfd, p); @@ -17140,7 +17203,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_TPREL16_HA: - if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000) + if (htab->do_tls_opt + && relocation + addend + 0x8000 < 0x10000 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { bfd_byte *p = contents + (rel->r_offset & ~3); bfd_put_32 (input_bfd, NOP, p); @@ -17150,7 +17215,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_LO_DS: - if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000) + if (htab->do_tls_opt + && relocation + addend + 0x8000 < 0x10000 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { bfd_byte *p = contents + (rel->r_offset & ~3); insn = bfd_get_32 (input_bfd, p); @@ -17234,6 +17301,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TPREL16_LO_DS: case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_LO_DS: + if (!offset_in_range (input_section, rel->r_offset & ~3, 4)) + break; insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); mask = 3; /* If this reloc is against an lq, lxv, or stxv insn, then @@ -17287,7 +17356,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, have different reloc types. */ if (howto->complain_on_overflow != complain_overflow_dont && howto->dst_mask == 0xffff - && (input_section->flags & SEC_CODE) != 0) + && (input_section->flags & SEC_CODE) != 0 + && offset_in_range (input_section, rel->r_offset & ~3, 4)) { enum complain_overflow complain = complain_overflow_signed; @@ -17329,7 +17399,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_PLT_PCREL34_NOTOC: case R_PPC64_D28: case R_PPC64_PCREL28: - if (rel->r_offset + 8 > input_section->size) + if (!offset_in_range (input_section, rel->r_offset, 8)) r = bfd_reloc_outofrange; else { @@ -17358,7 +17428,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_REL16DX_HA: - if (rel->r_offset + 4 > input_section->size) + if (!offset_in_range (input_section, rel->r_offset, 4)) r = bfd_reloc_outofrange; else { -- 2.30.2