PowerPC64, sanity check r_offset in relocate_section
authorAlan Modra <amodra@gmail.com>
Wed, 8 Sep 2021 03:48:02 +0000 (13:18 +0930)
committerAlan Modra <amodra@gmail.com>
Wed, 8 Sep 2021 12:52:34 +0000 (22:22 +0930)
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

index aa997bb42aba4d832df40455d21582fce7813e53..4ebacbd99457bcc6ea1d239159bd9d8b065115dc 100644 (file)
@@ -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
            {