Limit bogus archive parsed_size
[binutils-gdb.git] / bfd / bfdio.c
index 92dd9803e70a51fbc5fc634ebb8d8746261673aa..71ac17ec51515e157214769d8efb5fc45971d3df 100644 (file)
@@ -1,6 +1,6 @@
 /* Low-level I/O routines for BFDs.
 
-   Copyright (C) 1990-2019 Free Software Foundation, Inc.
+   Copyright (C) 1990-2020 Free Software Foundation, Inc.
 
    Written by Cygnus Support.
 
@@ -25,6 +25,7 @@
 #include <limits.h>
 #include "bfd.h"
 #include "libbfd.h"
+#include "aout/ar.h"
 
 #ifndef S_IXUSR
 #define S_IXUSR 0100    /* Execute by owner.  */
@@ -415,17 +416,32 @@ DESCRIPTION
        of space for the 15 bazillon byte table it is about to read.
        This function at least allows us to answer the question, "is the
        size reasonable?".
+
+       A return value of zero indicates the file size is unknown.
 */
 
 ufile_ptr
 bfd_get_size (bfd *abfd)
 {
-  struct stat buf;
+  /* A size of 0 means we haven't yet called bfd_stat.  A size of 1
+     means we have a cached value of 0, ie. unknown.  */
+  if (abfd->size <= 1 || bfd_write_p (abfd))
+    {
+      struct stat buf;
 
-  if (bfd_stat (abfd, &buf) != 0)
-    return 0;
+      if (abfd->size == 1 && !bfd_write_p (abfd))
+       return 0;
 
-  return buf.st_size;
+      if (bfd_stat (abfd, &buf) != 0
+         || buf.st_size == 0
+         || buf.st_size - (ufile_ptr) buf.st_size != 0)
+       {
+         abfd->size = 1;
+         return 0;
+       }
+      abfd->size = buf.st_size;
+    }
+  return abfd->size;
 }
 
 /*
@@ -445,11 +461,24 @@ DESCRIPTION
 ufile_ptr
 bfd_get_file_size (bfd *abfd)
 {
+  ufile_ptr file_size, archive_size = (ufile_ptr) -1;
+
   if (abfd->my_archive != NULL
       && !bfd_is_thin_archive (abfd->my_archive))
-    return arelt_size (abfd);
+    {
+      struct areltdata *adata = (struct areltdata *) abfd->arelt_data;
+      archive_size = adata->parsed_size;
+      /* If the archive is compressed we can't compare against file size.  */
+      if (memcmp (((struct ar_hdr *) adata->arch_header)->ar_fmag,
+                 "Z\012", 2) == 0)
+       return archive_size;
+      abfd = abfd->my_archive;
+    }
 
-  return bfd_get_size (abfd);
+  file_size = bfd_get_size (abfd);
+  if (archive_size < file_size)
+    return archive_size;
+  return file_size;
 }
 
 /*