strings: Improve code to detect excessively large minimum string lengths.
[binutils-gdb.git] / binutils / resbin.c
index c35af98180506ee4d23c75eb0eb5f7e976a70960..ad1270f3b4679cbfb71231dde673e932b40dec30 100644 (file)
@@ -1,6 +1,5 @@
 /* resbin.c -- manipulate the Windows binary resource format.
-   Copyright 1997, 1998, 1999, 2002, 2003, 2005, 2006, 2007, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 1997-2023 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
    Rewritten by Kai Tietz, Onevision.
 
@@ -55,7 +54,7 @@ static rc_res_resource *bin_to_res_group_cursor (windres_bfd *, const bfd_byte *
 static rc_res_resource *bin_to_res_group_icon (windres_bfd *, const bfd_byte *, rc_uint_type);
 static rc_res_resource *bin_to_res_version (windres_bfd *, const bfd_byte *, rc_uint_type);
 static rc_res_resource *bin_to_res_userdata (windres_bfd *, const bfd_byte *, rc_uint_type);
-static rc_res_resource *bin_to_res_toolbar (windres_bfd *, const bfd_byte *, rc_uint_type);
+static rc_res_resource *bin_to_res_toolbar (windres_bfd *, const bfd_byte *);
 static void get_version_header (windres_bfd *, const bfd_byte *, rc_uint_type, const char *,
                                unichar **, rc_uint_type *, rc_uint_type *, rc_uint_type *,
                                rc_uint_type *);
@@ -106,7 +105,7 @@ bin_to_res (windres_bfd *wrbfd, rc_res_id type, const bfd_byte *data,
        case RT_VERSION:
          return bin_to_res_version (wrbfd, data, length);
        case RT_TOOLBAR:
-         return  bin_to_res_toolbar (wrbfd, data, length);
+         return  bin_to_res_toolbar (wrbfd, data);
 
        }
     }
@@ -575,8 +574,6 @@ bin_to_res_dialog (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length
        dc->data = NULL;
       else
        {
-         off = (off + 3) &~ 3;
-
          if (length < off + datalen)
            toosmall (_("dialog control data"));
 
@@ -909,7 +906,7 @@ get_version_header (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
   if (length < 8)
     toosmall (key);
 
-  *len = windres_get_16 (wrbfd, data, 2);
+  *len = (windres_get_16 (wrbfd, data, 2) + 3) & ~3;
   *vallen = windres_get_16 (wrbfd, data + 2, 2);
   *type = windres_get_16 (wrbfd, data + 4, 2);
 
@@ -962,13 +959,17 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
   get_version_header (wrbfd, data, length, "VS_VERSION_INFO",
                      (unichar **) NULL, &verlen, &vallen, &type, &off);
 
-  if ((unsigned int) verlen != length)
-    fatal (_("version length %d does not match resource length %lu"),
-          (int) verlen, (unsigned long) length);
+  /* PR 17512: The verlen field does not include padding length.  */
+  if (verlen > length)
+    fatal (_("version length %lu greater than resource length %lu"),
+          (unsigned long) verlen, (unsigned long) length);
 
   if (type != 0)
     fatal (_("unexpected version type %d"), (int) type);
 
+  /* PR 27686: Ignore any padding bytes after the end of the version structure.  */
+  length = verlen;
+
   data += off;
   length -= off;
 
@@ -1027,7 +1028,7 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
 
       if (ch == 'S')
        {
-         rc_ver_stringinfo **ppvs;
+         rc_ver_stringtable **ppvst;
 
          vi->type = VERINFO_STRING;
 
@@ -1041,60 +1042,78 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
          data += off;
          length -= off;
 
-         get_version_header (wrbfd, data, length, (const char *) NULL,
-                             &vi->u.string.language, &verlen, &vallen,
-                             &type, &off);
+         verlen -= off;
 
-         if (vallen != 0)
-           fatal (_("unexpected version stringtable value length %ld"), (long) vallen);
+         vi->u.string.stringtables = NULL;
+         ppvst = &vi->u.string.stringtables;
 
-         data += off;
-         length -= off;
-         verlen -= off;
+         while (verlen > 0)
+           {
+             rc_ver_stringtable *vst;
+             rc_uint_type stverlen;
+             rc_ver_stringinfo **ppvs;
 
-         vi->u.string.strings = NULL;
-         ppvs = &vi->u.string.strings;
+             if (length < 8)
+               toosmall (_("version stringtable"));
 
-         /* It's convenient to round verlen to a 4 byte alignment,
-             since we round the subvariables in the loop.  */
-         verlen = (verlen + 3) &~ 3;
+             vst = (rc_ver_stringtable *) res_alloc (sizeof (rc_ver_stringtable));
 
-         while (verlen > 0)
+             get_version_header (wrbfd, data, length, (const char *) NULL,
+                                 &vst->language, &stverlen, &vallen, &type, &off);
+
+             if (vallen != 0)
+               fatal (_("unexpected version stringtable value length %ld"), (long) vallen);
+
+             data += off;
+             length -= off;
+             verlen -= off;
+
+         stverlen -= off;
+
+         vst->strings = NULL;
+         ppvs = &vst->strings;
+
+         while (stverlen > 0)
            {
              rc_ver_stringinfo *vs;
-             rc_uint_type subverlen, vslen, valoff;
+             rc_uint_type sverlen, vslen, valoff;
 
-             vs = (rc_ver_stringinfo *) res_alloc (sizeof *vs);
+             if (length < 8)
+               toosmall (_("version string"));
 
-             get_version_header (wrbfd, data, length,
-                                 (const char *) NULL, &vs->key, &subverlen,
-                                 &vallen, &type, &off);
+             vs = (rc_ver_stringinfo *) res_alloc (sizeof (rc_ver_stringinfo));
 
-             subverlen = (subverlen + 3) &~ 3;
+             get_version_header (wrbfd, data, length, (const char *) NULL,
+                                 &vs->key, &sverlen, &vallen, &type, &off);
 
              data += off;
              length -= off;
 
              vs->value = get_unicode (wrbfd, data, length, &vslen);
              valoff = vslen * 2 + 2;
-             valoff = (valoff + 3) &3;
+             valoff = (valoff + 3) & ~3;
 
-             if (off + valoff != subverlen)
+             if (off + valoff != sverlen)
                fatal (_("unexpected version string length %ld != %ld + %ld"),
-                      (long) subverlen, (long) off, (long) valoff);
-
-             vs->next = NULL;
-             *ppvs = vs;
-             ppvs = &vs->next;
+                      (long) sverlen, (long) off, (long) valoff);
 
              data += valoff;
              length -= valoff;
 
-             if (verlen < subverlen)
+             if (stverlen < sverlen)
                fatal (_("unexpected version string length %ld < %ld"),
-                      (long) verlen, (long) subverlen);
+                      (long) verlen, (long) sverlen);
+             stverlen -= sverlen;
+             verlen -= sverlen;
+
+             vs->next = NULL;
+             *ppvs = vs;
+             ppvs = &vs->next;
+           }
 
-             verlen -= subverlen;
+         vst->next = NULL;
+         *ppvst = vst;
+         ppvst = &vst->next;
            }
        }
       else if (ch == 'V')
@@ -1147,8 +1166,15 @@ bin_to_res_version (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
              vallen -= 4;
            }
        }
+      else if (ch == 0)
+       {
+         if (length == 8)
+           /* Padding - skip.  */
+           break;
+         fatal (_("nul bytes found in version string"));
+       }
       else
-       fatal (_("unexpected version string"));
+       fatal (_("unexpected version string character: %x"), ch);
 
       vi->next = NULL;
       *pp = vi;
@@ -1190,7 +1216,7 @@ bin_to_res_userdata (windres_bfd *wrbfd ATTRIBUTE_UNUSED, const bfd_byte *data,
 }
 \f
 static rc_res_resource *
-bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type length)
+bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data)
 {
   rc_toolbar *ri;
   rc_res_resource *r;
@@ -1203,7 +1229,6 @@ bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
   ri->items = NULL;
 
   data += 12;
-  length -= 12;
   for (i=0 ; i < ri->nitems; i++)
   {
     rc_toolbar_item *it;
@@ -1212,7 +1237,6 @@ bin_to_res_toolbar (windres_bfd *wrbfd, const bfd_byte *data, rc_uint_type lengt
     it->id.u.id = (int) windres_get_32 (wrbfd, data, 4);
     it->prev = it->next = NULL;
     data += 4;
-    length -= 4;
     if(ri->items) {
       rc_toolbar_item *ii = ri->items;
       while (ii->next != NULL)
@@ -1301,7 +1325,7 @@ resid_to_bin (windres_bfd *wrbfd, rc_uint_type off, rc_res_id id)
       if (wrbfd)
        {
          struct bin_res_id bri;
-         
+
          windres_put_16 (wrbfd, bri.sig, 0xffff);
          windres_put_16 (wrbfd, bri.id, id.u.id);
          set_windres_bfd_content (wrbfd, &bri, off, BIN_RES_ID);
@@ -1539,7 +1563,7 @@ res_to_bin_dialog (windres_bfd *wrbfd, rc_uint_type off, const rc_dialog *dialog
              windres_put_32 (wrbfd, bdc.id, dc->id);
              set_windres_bfd_content (wrbfd, &bdc, off, BIN_DIALOGEX_CONTROL_SIZE);
            }
-       }      
+       }
       off += (dialogex != 0 ? BIN_DIALOGEX_CONTROL_SIZE : BIN_DIALOG_CONTROL_SIZE);
 
       off = resid_to_bin (wrbfd, off, dc->class);
@@ -1557,7 +1581,6 @@ res_to_bin_dialog (windres_bfd *wrbfd, rc_uint_type off, const rc_dialog *dialog
        {
          rc_uint_type saved_off = off;
          rc_uint_type old_off;
-         off += (4 - ((off - off_delta) & 3)) & 3;
 
          old_off = off;
          off = res_to_bin_rcdata (wrbfd, off, dc->data);
@@ -1565,10 +1588,10 @@ res_to_bin_dialog (windres_bfd *wrbfd, rc_uint_type off, const rc_dialog *dialog
            old_off = off = saved_off;
          if (wrbfd)
            windres_put_16 (wrbfd, dc_rclen, off - old_off);
-           }
+       }
       if (wrbfd)
        set_windres_bfd_content (wrbfd, dc_rclen, marker, 2);
-       }
+    }
 
   if (wrbfd)
     {
@@ -1867,6 +1890,7 @@ res_to_bin_stringtable (windres_bfd *wrbfd, rc_uint_type off,
       unichar *s;
 
       slen = (rc_uint_type) st->strings[i].length;
+      if (slen == 0xffffffff) slen = 0;
       s = st->strings[i].string;
 
       length = 2 + slen * 2;
@@ -2004,52 +2028,62 @@ res_to_bin_versioninfo (windres_bfd *wrbfd, rc_uint_type off,
          abort ();
        case VERINFO_STRING:
          {
-           struct bin_ver_info bvsd;
-           rc_uint_type vs_off;
-           const rc_ver_stringinfo *vs;
+           const rc_ver_stringtable *vst;
 
            off = string_to_unicode_bin (wrbfd, off, "StringFileInfo");
-           off += (4 - ((off - off_delta) & 3)) & 3;
-
-           vs_off = off;
-
-           off += BIN_VER_INFO_SIZE;
 
-           off = unicode_to_bin (wrbfd, off, vi->u.string.language);
+           if (!vi->u.string.stringtables)
+             off += (4 - ((off - off_delta) & 3)) & 3;
 
-           for (vs = vi->u.string.strings; vs != NULL; vs = vs->next)
+           for (vst = vi->u.string.stringtables; vst != NULL; vst = vst->next)
              {
-               struct bin_ver_info bvss;
-               rc_uint_type vss_off,str_off;
+               struct bin_ver_info bvst;
+               rc_uint_type vst_off;
+               const rc_ver_stringinfo *vs;
 
                off += (4 - ((off - off_delta) & 3)) & 3;
 
-               vss_off = off;
+               vst_off = off;
                off += BIN_VER_INFO_SIZE;
 
-               off = unicode_to_bin (wrbfd, off, vs->key);
+               off = unicode_to_bin (wrbfd, off, vst->language);
 
-               off += (4 - ((off - off_delta) & 3)) & 3;
+               for (vs = vst->strings; vs != NULL; vs = vs->next)
+                 {
+                   struct bin_ver_info bvs;
+                   rc_uint_type vs_off, str_off;
+
+                   off += (4 - ((off - off_delta) & 3)) & 3;
+
+                   vs_off = off;
+                   off += BIN_VER_INFO_SIZE;
+
+                   off = unicode_to_bin (wrbfd, off, vs->key);
+
+                   off += (4 - ((off - off_delta) & 3)) & 3;
+
+                   str_off = off;
+                   off = unicode_to_bin (wrbfd, off, vs->value);
+
+                   if (wrbfd)
+                     {
+                       windres_put_16 (wrbfd, bvs.size, off - vs_off);
+                       windres_put_16 (wrbfd, bvs.sig1, (off - str_off) / 2);
+                       windres_put_16 (wrbfd, bvs.sig2, 1);
+                       set_windres_bfd_content (wrbfd, &bvs, vs_off,
+                                                BIN_VER_INFO_SIZE);
+                     }
+                 }
 
-               str_off = off;
-               off = unicode_to_bin (wrbfd, off, vs->value);
                if (wrbfd)
                  {
-                   windres_put_16 (wrbfd, bvss.size, off - vss_off);
-                   windres_put_16 (wrbfd, bvss.sig1, (off - str_off) / 2);
-                   windres_put_16 (wrbfd, bvss.sig2, 1);
-                   set_windres_bfd_content (wrbfd, &bvss, vss_off,
-                                            BIN_VER_INFO_SIZE);
+                   windres_put_16 (wrbfd, bvst.size, off - vst_off);
+                   windres_put_16 (wrbfd, bvst.sig1, 0);
+                   windres_put_16 (wrbfd, bvst.sig2, 1);
+                   set_windres_bfd_content (wrbfd, &bvst, vst_off,
+                                            BIN_VER_INFO_SIZE);
                  }
              }
-           if (wrbfd)
-             {
-               windres_put_16 (wrbfd, bvsd.size, off - vs_off);
-               windres_put_16 (wrbfd, bvsd.sig1, 0);
-               windres_put_16 (wrbfd, bvsd.sig2, 0);
-               set_windres_bfd_content (wrbfd, &bvsd, vs_off,
-                                        BIN_VER_INFO_SIZE);
-             }
            break;
          }
 
@@ -2099,9 +2133,9 @@ res_to_bin_versioninfo (windres_bfd *wrbfd, rc_uint_type off,
 
       if (wrbfd)
        {
-         windres_put_16 (wrbfd, bv.size, off-bv_off);
+         windres_put_16 (wrbfd, bv.size, off - bv_off);
          windres_put_16 (wrbfd, bv.sig1, 0);
-         windres_put_16 (wrbfd, bv.sig2, 0);
+         windres_put_16 (wrbfd, bv.sig2, 1);
          set_windres_bfd_content (wrbfd, &bv, bv_off,
                                   BIN_VER_INFO_SIZE);
        }