Fix potential address violation parsing a corrupt Alpha VMS file.
[binutils-gdb.git] / bfd / vms-alpha.c
index 4a9881be552f064cade5776176fd2f647d72b973..5f1b24aeb58743046b2971e89469a897a54341b7 100644 (file)
@@ -622,14 +622,29 @@ static bfd_boolean
 _bfd_vms_slurp_eihs (bfd *abfd, unsigned int offset)
 {
   unsigned char *p = PRIV (recrd.rec) + offset;
-  unsigned int gstvbn = bfd_getl32 (p + EIHS__L_GSTVBN);
-  unsigned int gstsize ATTRIBUTE_UNUSED = bfd_getl32 (p + EIHS__L_GSTSIZE);
-  unsigned int dstvbn = bfd_getl32 (p + EIHS__L_DSTVBN);
-  unsigned int dstsize = bfd_getl32 (p + EIHS__L_DSTSIZE);
-  unsigned int dmtvbn = bfd_getl32 (p + EIHS__L_DMTVBN);
-  unsigned int dmtbytes = bfd_getl32 (p + EIHS__L_DMTBYTES);
+  unsigned int gstvbn;
+  unsigned int gstsize ATTRIBUTE_UNUSED;
+  unsigned int dstvbn;
+  unsigned int dstsize;
+  unsigned int dmtvbn;
+  unsigned int dmtbytes;
   asection *section;
 
+  /* PR 21611: Check that offset is valid.  */
+  if (offset > PRIV (recrd.rec_size) - (EIHS__L_DMTBYTES + 4))
+    {
+      _bfd_error_handler (_("Unable to read EIHS record at offset %#x"), offset); 
+      bfd_set_error (bfd_error_file_truncated);
+      return FALSE;
+    }
+
+  gstvbn   = bfd_getl32 (p + EIHS__L_GSTVBN);
+  gstsize  = bfd_getl32 (p + EIHS__L_GSTSIZE);
+  dstvbn   = bfd_getl32 (p + EIHS__L_DSTVBN);
+  dstsize  = bfd_getl32 (p + EIHS__L_DSTSIZE);
+  dmtvbn   = bfd_getl32 (p + EIHS__L_DMTVBN);
+  dmtbytes = bfd_getl32 (p + EIHS__L_DMTBYTES);
+
 #if VMS_DEBUG
   vms_debug (8, "_bfd_vms_slurp_ihs\n");
   vms_debug (4, "EIHS record gstvbn %d gstsize %d dstvbn %d dstsize %d dmtvbn %d dmtbytes %d\n",
@@ -677,7 +692,7 @@ _bfd_vms_slurp_eihs (bfd *abfd, unsigned int offset)
          return FALSE;
        }
 
-      if (_bfd_vms_slurp_object_records (abfd) != TRUE)
+      if (!_bfd_vms_slurp_object_records (abfd))
        return FALSE;
 
       abfd->flags |= HAS_SYMS;
@@ -1121,19 +1136,28 @@ add_symbol (bfd *abfd, const unsigned char *ascic)
 static bfd_boolean
 _bfd_vms_slurp_egsd (bfd *abfd)
 {
-  int gsd_type, gsd_size;
+  int gsd_type;
+  unsigned int gsd_size;
   unsigned char *vms_rec;
   unsigned long base_addr;
 
   vms_debug2 ((2, "EGSD\n"));
 
+  if (PRIV (recrd.rec_size) < 8)
+    {
+      _bfd_error_handler (_("Corrupt EGSD record: its size (%#x) is too small"),
+                         PRIV (recrd.rec_size));
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
   PRIV (recrd.rec) += 8;       /* Skip type, size, align pad.  */
   PRIV (recrd.rec_size) -= 8;
 
   /* Calculate base address for each section.  */
   base_addr = 0L;
 
-  while (PRIV (recrd.rec_size) > 0)
+  while (PRIV (recrd.rec_size) > 4)
     {
       vms_rec = PRIV (recrd.rec);
 
@@ -1142,6 +1166,15 @@ _bfd_vms_slurp_egsd (bfd *abfd)
 
       vms_debug2 ((3, "egsd_type %d\n", gsd_type));
 
+      /* PR 21615: Check for size overflow.  */
+      if (PRIV (recrd.rec_size) < gsd_size)
+       {
+         _bfd_error_handler (_("Corrupt EGSD record: size (%#x) is larger than remaining space (%#x)"),
+                             gsd_size, PRIV (recrd.rec_size));
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+
       switch (gsd_type)
        {
        case EGSD__C_PSC:
@@ -1257,6 +1290,8 @@ _bfd_vms_slurp_egsd (bfd *abfd)
                 struct vms_esdf *esdf = (struct vms_esdf *)vms_rec;
 
                entry->value = bfd_getl64 (esdf->value);
+               if (PRIV (sections) == NULL)
+                 return FALSE;
                entry->section = PRIV (sections)[bfd_getl32 (esdf->psindx)];
 
                 if (old_flags & EGSY__V_NORM)
@@ -1291,7 +1326,11 @@ _bfd_vms_slurp_egsd (bfd *abfd)
             entry->symbol_vector = bfd_getl32 (egst->value);
 
             if (old_flags & EGSY__V_REL)
-              entry->section = PRIV (sections)[bfd_getl32 (egst->psindx)];
+             {
+               if (PRIV (sections) == NULL)
+                 return FALSE;
+               entry->section = PRIV (sections)[bfd_getl32 (egst->psindx)];
+             }
             else
               entry->section = bfd_abs_section_ptr;
 
@@ -1323,6 +1362,8 @@ _bfd_vms_slurp_egsd (bfd *abfd)
       PRIV (recrd.rec) += gsd_size;
     }
 
+  /* FIXME: Should we complain if PRIV (recrd.rec_size) is not zero ?  */
+
   if (PRIV (gsd_sym_count) > 0)
     abfd->flags |= HAS_SYMS;
 
@@ -1379,6 +1420,8 @@ image_set_ptr (bfd *abfd, bfd_vma vma, int sect, struct bfd_link_info *info)
 
   vms_debug2 ((4, "image_set_ptr (0x%08x, sect=%d)\n", (unsigned)vma, sect));
 
+  if (PRIV (sections) == NULL)
+    return;
   sec = PRIV (sections)[sect];
 
   if (info)
@@ -1456,7 +1499,7 @@ dst_retrieve_location (bfd *abfd, unsigned int loc)
 /* Write multiple bytes to section image.  */
 
 static bfd_boolean
-image_write (bfd *abfd, unsigned char *ptr, int size)
+image_write (bfd *abfd, unsigned char *ptr, unsigned int size)
 {
 #if VMS_DEBUG
   _bfd_vms_debug (8, "image_write from (%p, %d) to (%ld)\n", ptr, size,
@@ -1603,14 +1646,16 @@ _bfd_vms_etir_name (int cmd)
 #define HIGHBIT(op) ((op & 0x80000000L) == 0x80000000L)
 
 static void
-_bfd_vms_get_value (bfd *abfd, const unsigned char *ascic,
+_bfd_vms_get_value (bfd *abfd,
+                   const unsigned char *ascic,
+                   const unsigned char *max_ascic,
                     struct bfd_link_info *info,
                     bfd_vma *vma,
                     struct alpha_vms_link_hash_entry **hp)
 {
   char name[257];
-  int len;
-  int i;
+  unsigned int len;
+  unsigned int i;
   struct alpha_vms_link_hash_entry *h;
 
   /* Not linking.  Do not try to resolve the symbol.  */
@@ -1622,6 +1667,14 @@ _bfd_vms_get_value (bfd *abfd, const unsigned char *ascic,
     }
 
   len = *ascic;
+  if (ascic + len >= max_ascic)
+    {
+      _bfd_error_handler (_("Corrupt vms value"));
+      *vma = 0;
+      *hp = NULL;
+      return;
+    }
+
   for (i = 0; i < len; i++)
     name[i] = ascic[i + 1];
   name[i] = 0;
@@ -1691,7 +1744,12 @@ static bfd_vma
 alpha_vms_fix_sec_rel (bfd *abfd, struct bfd_link_info *info,
                        unsigned int rel, bfd_vma vma)
 {
-  asection *sec = PRIV (sections)[rel & RELC_MASK];
+  asection *sec;
+
+  if (PRIV (sections) == NULL)
+    return 0;
+
+  sec = PRIV (sections)[rel & RELC_MASK];
 
   if (info)
     {
@@ -1735,6 +1793,15 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
 
       ptr += 4;
 
+      /* PR 21589 and 21579: Check for a corrupt ETIR record.  */
+      if (cmd_length < 4 || (ptr + cmd_length > maxptr + 4))
+       {
+       corrupt_etir:
+         _bfd_error_handler (_("Corrupt ETIR record encountered"));
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+
 #if VMS_DEBUG
       _bfd_vms_debug (4, "etir: %s(%d)\n",
                       _bfd_vms_etir_name (cmd), cmd);
@@ -1748,7 +1815,7 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
 
              stack 32 bit value of symbol (high bits set to 0).  */
         case ETIR__C_STA_GBL:
-          _bfd_vms_get_value (abfd, ptr, info, &op1, &h);
+          _bfd_vms_get_value (abfd, ptr, maxptr, info, &op1, &h);
           _bfd_vms_push (abfd, op1, alpha_vms_sym_to_ctxt (h));
           break;
 
@@ -1757,6 +1824,8 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
 
              stack 32 bit value, sign extend to 64 bit.  */
         case ETIR__C_STA_LW:
+         if (ptr + 4 >= maxptr)
+           goto corrupt_etir;
           _bfd_vms_push (abfd, bfd_getl32 (ptr), RELC_NONE);
           break;
 
@@ -1765,6 +1834,8 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
 
              stack 64 bit value of symbol.  */
         case ETIR__C_STA_QW:
+         if (ptr + 8 >= maxptr)
+           goto corrupt_etir;
           _bfd_vms_push (abfd, bfd_getl64 (ptr), RELC_NONE);
           break;
 
@@ -1778,6 +1849,8 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
           {
             int psect;
 
+           if (ptr + 12 >= maxptr)
+             goto corrupt_etir;
             psect = bfd_getl32 (ptr);
             if ((unsigned int) psect >= PRIV (section_count))
               {
@@ -1867,6 +1940,8 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
           {
             int size;
 
+           if (ptr + 4 >= maxptr)
+             goto corrupt_etir;
             size = bfd_getl32 (ptr);
             _bfd_vms_pop (abfd, &op1, &rel1);
             if (rel1 != RELC_NONE)
@@ -1879,7 +1954,7 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
           /* Store global: write symbol value
              arg: cs   global symbol name.  */
         case ETIR__C_STO_GBL:
-          _bfd_vms_get_value (abfd, ptr, info, &op1, &h);
+          _bfd_vms_get_value (abfd, ptr, maxptr, info, &op1, &h);
           if (h && h->sym)
             {
               if (h->sym->typ == EGSD__C_SYMG)
@@ -1901,7 +1976,7 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
           /* Store code address: write address of entry point
              arg: cs   global symbol name (procedure).  */
         case ETIR__C_STO_CA:
-          _bfd_vms_get_value (abfd, ptr, info, &op1, &h);
+          _bfd_vms_get_value (abfd, ptr, maxptr, info, &op1, &h);
           if (h && h->sym)
             {
               if (h->sym->flags & EGSY__V_NORM)
@@ -1946,8 +2021,10 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
              da        data.  */
         case ETIR__C_STO_IMM:
           {
-            int size;
+            unsigned int size;
 
+           if (ptr + 4 >= maxptr)
+             goto corrupt_etir;
             size = bfd_getl32 (ptr);
             image_write (abfd, ptr + 4, size);
           }
@@ -1960,7 +2037,7 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
              store global longword: store 32bit value of symbol
              arg: cs   symbol name.  */
         case ETIR__C_STO_GBL_LW:
-          _bfd_vms_get_value (abfd, ptr, info, &op1, &h);
+          _bfd_vms_get_value (abfd, ptr, maxptr, info, &op1, &h);
 #if 0
           abort ();
 #endif
@@ -2013,7 +2090,7 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
              da        signature.  */
 
         case ETIR__C_STC_LP_PSB:
-          _bfd_vms_get_value (abfd, ptr + 4, info, &op1, &h);
+          _bfd_vms_get_value (abfd, ptr + 4, maxptr, info, &op1, &h);
           if (h && h->sym)
             {
               if (h->sym->typ == EGSD__C_SYMG)
@@ -2109,6 +2186,8 @@ _bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
           /* Augment relocation base: increment image location counter by offset
              arg: lw   offset value.  */
         case ETIR__C_CTL_AUGRB:
+         if (ptr + 4 >= maxptr)
+           goto corrupt_etir;
           op1 = bfd_getl32 (ptr);
           image_inc_ptr (abfd, op1);
           break;
@@ -2411,7 +2490,7 @@ _bfd_vms_slurp_object_records (bfd * abfd)
         default:
           err = FALSE;
        }
-      if (err != TRUE)
+      if (!err)
        {
          vms_debug2 ((2, "slurp type %d failed\n", type));
          return FALSE;
@@ -2551,14 +2630,14 @@ alpha_vms_object_p (bfd *abfd)
        goto error_ret;
       vms_debug2 ((2, "file type is image\n"));
 
-      if (_bfd_vms_slurp_eihd (abfd, &eisd_offset, &eihs_offset) != TRUE)
+      if (!_bfd_vms_slurp_eihd (abfd, &eisd_offset, &eihs_offset))
         goto err_wrong_format;
 
-      if (_bfd_vms_slurp_eisd (abfd, eisd_offset) != TRUE)
+      if (!_bfd_vms_slurp_eisd (abfd, eisd_offset))
         goto err_wrong_format;
 
       /* EIHS is optional.  */
-      if (eihs_offset != 0 && _bfd_vms_slurp_eihs (abfd, eihs_offset) != TRUE)
+      if (eihs_offset != 0 && !_bfd_vms_slurp_eihs (abfd, eihs_offset))
         goto err_wrong_format;
     }
   else
@@ -2578,10 +2657,10 @@ alpha_vms_object_p (bfd *abfd)
           vms_debug2 ((2, "file type is module\n"));
 
           type = bfd_getl16 (PRIV (recrd.rec));
-          if (type != EOBJ__C_EMH || _bfd_vms_slurp_ehdr (abfd) != TRUE)
+          if (type != EOBJ__C_EMH || !_bfd_vms_slurp_ehdr (abfd))
             goto err_wrong_format;
 
-          if (_bfd_vms_slurp_object_records (abfd) != TRUE)
+          if (!_bfd_vms_slurp_object_records (abfd))
             goto err_wrong_format;
         }
       else
@@ -3976,13 +4055,13 @@ alpha_vms_write_object_contents (bfd *abfd)
     {
       if (abfd->section_count > 0)                     /* we have sections */
         {
-          if (_bfd_vms_write_ehdr (abfd) != TRUE)
+          if (!_bfd_vms_write_ehdr (abfd))
             return FALSE;
-          if (_bfd_vms_write_egsd (abfd) != TRUE)
+          if (!_bfd_vms_write_egsd (abfd))
             return FALSE;
-          if (_bfd_vms_write_etir (abfd, EOBJ__C_ETIR) != TRUE)
+          if (!_bfd_vms_write_etir (abfd, EOBJ__C_ETIR))
             return FALSE;
-          if (_bfd_vms_write_eeom (abfd) != TRUE)
+          if (!_bfd_vms_write_eeom (abfd))
             return FALSE;
         }
     }
@@ -5005,6 +5084,8 @@ alpha_vms_slurp_relocs (bfd *abfd)
                 return FALSE;
               }
 
+           if (PRIV (sections) == NULL)
+             return FALSE;
             sec = PRIV (sections)[cur_psect];
             if (sec == bfd_abs_section_ptr)
               {
@@ -5063,8 +5144,12 @@ alpha_vms_slurp_relocs (bfd *abfd)
                   reloc->sym_ptr_ptr = sym;
               }
             else if (cur_psidx >= 0)
-              reloc->sym_ptr_ptr =
-                PRIV (sections)[cur_psidx]->symbol_ptr_ptr;
+             {
+               if (PRIV (sections) == NULL)
+                 return FALSE;
+               reloc->sym_ptr_ptr =
+                 PRIV (sections)[cur_psidx]->symbol_ptr_ptr;
+             }
             else
               reloc->sym_ptr_ptr = NULL;
 
@@ -5118,6 +5203,11 @@ alpha_vms_canonicalize_reloc (bfd *abfd, asection *section, arelent **relptr,
   *relptr = (arelent *) NULL;
   return section->reloc_count;
 }
+
+/* Install a new set of internal relocs.  */
+
+#define alpha_vms_set_reloc _bfd_generic_set_reloc
+
 \f
 /* This is just copied from ecoff-alpha, needs to be fixed probably.  */
 
@@ -5573,6 +5663,13 @@ evax_bfd_print_emh (FILE *file, unsigned char *rec, unsigned int rec_len)
   /* xgettext:c-format */
   fprintf (file, _("  EMH %u (len=%u): "), subtype, rec_len);
 
+  /* PR 21618: Check for invalid lengths.  */
+  if (rec_len < sizeof (* emh))
+    {
+      fprintf (file, _("   Error: The length is less than the length of an EMH record\n"));
+      return;
+    }
+  
   switch (subtype)
     {
     case EMH__C_MHD:
@@ -5636,6 +5733,14 @@ evax_bfd_print_eeom (FILE *file, unsigned char *rec, unsigned int rec_len)
   struct vms_eeom *eeom = (struct vms_eeom *)rec;
 
   fprintf (file, _("  EEOM (len=%u):\n"), rec_len);
+
+  /* PR 21618: Check for invalid lengths.  */
+  if (rec_len < sizeof (* eeom))
+    {
+      fprintf (file, _("   Error: The length is less than the length of an EEOM record\n"));
+      return;
+    }
+  
   fprintf (file, _("   number of cond linkage pairs: %u\n"),
            (unsigned)bfd_getl32 (eeom->total_lps));
   fprintf (file, _("   completion code: %u\n"),
@@ -5725,6 +5830,12 @@ evax_bfd_print_egsd (FILE *file, unsigned char *rec, unsigned int rec_len)
                n, type, len);
       n++;
 
+      if (off + len > rec_len || off + len < off)
+       {
+         fprintf (file, _("   Error: length larger than remaining space in record\n"));
+         return;
+       }
+
       switch (type)
         {
         case EGSD__C_PSC:
@@ -5970,6 +6081,12 @@ evax_bfd_print_etir (FILE *file, const char *name,
       size = bfd_getl16 (etir->size);
       buf = rec + off + sizeof (struct vms_etir);
 
+      if (off + size > rec_len || off + size < off)
+       {
+         fprintf (file, _("   Error: length larger than remaining space in record\n"));
+         return;
+       }
+
       /* xgettext:c-format */
       fprintf (file, _("   (type: %3u, size: 4+%3u): "), type, size - 4);
       switch (type)
@@ -8157,9 +8274,9 @@ alpha_vms_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
         h = NULL;
 
       h_root = (struct bfd_link_hash_entry *) h;
-      if (_bfd_generic_link_add_one_symbol
-          (info, abfd, sym.name, sym.flags, sym.section, sym.value,
-           NULL, FALSE, FALSE, &h_root) == FALSE)
+      if (!_bfd_generic_link_add_one_symbol (info, abfd, sym.name, sym.flags,
+                                            sym.section, sym.value, NULL,
+                                            FALSE, FALSE, &h_root))
         return FALSE;
       h = (struct alpha_vms_link_hash_entry *) h_root;
 
@@ -9072,9 +9189,9 @@ vms_close_and_cleanup (bfd * abfd)
     {
       /* Last step on VMS is to convert the file to variable record length
         format.  */
-      if (bfd_cache_close (abfd) != TRUE)
+      if (!bfd_cache_close (abfd))
        return FALSE;
-      if (_bfd_vms_convert_to_var_unix_filename (abfd->filename) != TRUE)
+      if (!_bfd_vms_convert_to_var_unix_filename (abfd->filename))
        return FALSE;
     }
 #endif
@@ -9275,6 +9392,7 @@ bfd_vms_get_data (bfd *abfd)
 #define vms_bfd_discard_group             bfd_generic_discard_group
 #define vms_section_already_linked        _bfd_generic_section_already_linked
 #define vms_bfd_define_common_symbol      bfd_generic_define_common_symbol
+#define vms_bfd_define_start_stop      bfd_generic_define_start_stop
 #define vms_bfd_copy_private_header_data  _bfd_generic_bfd_copy_private_header_data
 
 #define vms_bfd_copy_private_bfd_data    _bfd_generic_bfd_copy_private_bfd_data
@@ -9322,6 +9440,7 @@ bfd_vms_get_data (bfd *abfd)
   _bfd_generic_section_already_linked
 
 #define alpha_vms_bfd_define_common_symbol bfd_generic_define_common_symbol
+#define alpha_vms_bfd_define_start_stop bfd_generic_define_start_stop
 #define alpha_vms_bfd_link_just_syms _bfd_generic_link_just_syms
 #define alpha_vms_bfd_copy_link_hash_symbol_type \
   _bfd_generic_copy_link_hash_symbol_type