PR25900, RISC-V: null pointer dereference
[deliverable/binutils-gdb.git] / bfd / bfdio.c
index 13c6a5e549a6294ad7fa2fe7d87376c437191c8a..29834d9c6b69e017225c533b2c29f64f99b949a2 100644 (file)
@@ -1,6 +1,6 @@
 /* Low-level I/O routines for BFDs.
 
-   Copyright (C) 1990-2017 Free Software Foundation, Inc.
+   Copyright (C) 1990-2020 Free Software Foundation, Inc.
 
    Written by Cygnus Support.
 
 #include <limits.h>
 #include "bfd.h"
 #include "libbfd.h"
+#include "aout/ar.h"
+#if defined (_WIN32)
+#include <windows.h>
+#endif
 
 #ifndef S_IXUSR
 #define S_IXUSR 0100    /* Execute by owner.  */
@@ -92,12 +96,7 @@ _bfd_real_fopen (const char *filename, const char *modes)
      In fopen-vms.h, they are separated from the mode with a comma.
      Split here.  */
   vms_attr = strchr (modes, ',');
-  if (vms_attr == NULL)
-    {
-      /* No attributes.  */
-      return close_on_exec (fopen (filename, modes));
-    }
-  else
+  if (vms_attr != NULL)
     {
       /* Attributes found.  Split.  */
       size_t modes_len = strlen (modes) + 1;
@@ -115,13 +114,29 @@ _bfd_real_fopen (const char *filename, const char *modes)
        }
       return close_on_exec (fopen (filename, at[0], at[1], at[2]));
     }
-#else /* !VMS */
-#if defined (HAVE_FOPEN64)
+
+#elif defined (_WIN32)
+  size_t filelen = strlen (filename) + 1;
+
+  if (filelen > MAX_PATH - 1)
+    {
+      FILE *file;
+      char* fullpath = (char *) malloc (filelen + 8);
+
+      /* Add a Microsoft recommended prefix that
+        will allow the extra-long path to work.  */
+      strcpy (fullpath, "\\\\?\\");
+      strcat (fullpath, filename);
+      file = close_on_exec (fopen (fullpath, modes));
+      free (fullpath);
+      return file;
+    }
+
+#elif defined (HAVE_FOPEN64)
   return close_on_exec (fopen64 (filename, modes));
-#else
-  return close_on_exec (fopen (filename, modes));
 #endif
-#endif /* !VMS */
+
+  return close_on_exec (fopen (filename, modes));
 }
 
 /*
@@ -176,27 +191,41 @@ DESCRIPTION
 bfd_size_type
 bfd_bread (void *ptr, bfd_size_type size, bfd *abfd)
 {
-  size_t nread;
+  file_ptr nread;
+  bfd *element_bfd = abfd;
+  ufile_ptr offset = 0;
+
+  while (abfd->my_archive != NULL
+        && !bfd_is_thin_archive (abfd->my_archive))
+    {
+      offset += abfd->origin;
+      abfd = abfd->my_archive;
+    }
+  offset += abfd->origin;
 
   /* If this is an archive element, don't read past the end of
      this element.  */
-  if (abfd->arelt_data != NULL)
+  if (element_bfd->arelt_data != NULL)
     {
-      bfd_size_type maxbytes = arelt_size (abfd);
+      bfd_size_type maxbytes = arelt_size (element_bfd);
 
-      if (abfd->where + size > maxbytes)
+      if (abfd->where < offset || abfd->where - offset >= maxbytes)
        {
-         if (abfd->where >= maxbytes)
-           return 0;
-         size = maxbytes - abfd->where;
+         bfd_set_error (bfd_error_invalid_operation);
+         return -1;
        }
+      if (abfd->where - offset + size > maxbytes)
+       size = maxbytes - (abfd->where - offset);
     }
 
-  if (abfd->iovec)
-    nread = abfd->iovec->bread (abfd, ptr, size);
-  else
-    nread = 0;
-  if (nread != (size_t) -1)
+  if (abfd->iovec == NULL)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return -1;
+    }
+
+  nread = abfd->iovec->bread (abfd, ptr, size);
+  if (nread != -1)
     abfd->where += nread;
 
   return nread;
@@ -205,16 +234,22 @@ bfd_bread (void *ptr, bfd_size_type size, bfd *abfd)
 bfd_size_type
 bfd_bwrite (const void *ptr, bfd_size_type size, bfd *abfd)
 {
-  size_t nwrote;
+  file_ptr nwrote;
 
-  if (abfd->iovec)
-    nwrote = abfd->iovec->bwrite (abfd, ptr, size);
-  else
-    nwrote = 0;
+  while (abfd->my_archive != NULL
+        && !bfd_is_thin_archive (abfd->my_archive))
+    abfd = abfd->my_archive;
 
-  if (nwrote != (size_t) -1)
+  if (abfd->iovec == NULL)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return -1;
+    }
+
+  nwrote = abfd->iovec->bwrite (abfd, ptr, size);
+  if (nwrote != -1)
     abfd->where += nwrote;
-  if (nwrote != size)
+  if ((bfd_size_type) nwrote != size)
     {
 #ifdef ENOSPC
       errno = ENOSPC;
@@ -227,33 +262,36 @@ bfd_bwrite (const void *ptr, bfd_size_type size, bfd *abfd)
 file_ptr
 bfd_tell (bfd *abfd)
 {
+  ufile_ptr offset = 0;
   file_ptr ptr;
 
-  if (abfd->iovec)
+  while (abfd->my_archive != NULL
+        && !bfd_is_thin_archive (abfd->my_archive))
     {
-      bfd *parent_bfd = abfd;
-      ptr = abfd->iovec->btell (abfd);
-
-      while (parent_bfd->my_archive != NULL
-            && !bfd_is_thin_archive (parent_bfd->my_archive))
-       {
-         ptr -= parent_bfd->origin;
-         parent_bfd = parent_bfd->my_archive;
-       }
+      offset += abfd->origin;
+      abfd = abfd->my_archive;
     }
-  else
-    ptr = 0;
+  offset += abfd->origin;
 
+  if (abfd->iovec == NULL)
+    return 0;
+
+  ptr = abfd->iovec->btell (abfd);
   abfd->where = ptr;
-  return ptr;
+  return ptr - offset;
 }
 
 int
 bfd_flush (bfd *abfd)
 {
-  if (abfd->iovec)
-    return abfd->iovec->bflush (abfd);
-  return 0;
+  while (abfd->my_archive != NULL
+        && !bfd_is_thin_archive (abfd->my_archive))
+    abfd = abfd->my_archive;
+
+  if (abfd->iovec == NULL)
+    return 0;
+
+  return abfd->iovec->bflush (abfd);
 }
 
 /* Returns 0 for success, negative value for failure (in which case
@@ -263,11 +301,17 @@ bfd_stat (bfd *abfd, struct stat *statbuf)
 {
   int result;
 
-  if (abfd->iovec)
-    result = abfd->iovec->bstat (abfd, statbuf);
-  else
-    result = -1;
+  while (abfd->my_archive != NULL
+        && !bfd_is_thin_archive (abfd->my_archive))
+    abfd = abfd->my_archive;
+
+  if (abfd->iovec == NULL)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return -1;
+    }
 
+  result = abfd->iovec->bstat (abfd, statbuf);
   if (result < 0)
     bfd_set_error (bfd_error_system_call);
   return result;
@@ -280,79 +324,53 @@ int
 bfd_seek (bfd *abfd, file_ptr position, int direction)
 {
   int result;
-  file_ptr file_position;
-  /* For the time being, a BFD may not seek to it's end.  The problem
-     is that we don't easily have a way to recognize the end of an
-     element in an archive.  */
-
-  BFD_ASSERT (direction == SEEK_SET || direction == SEEK_CUR);
-
-  if (direction == SEEK_CUR && position == 0)
-    return 0;
+  ufile_ptr offset = 0;
 
-  if (abfd->my_archive == NULL || bfd_is_thin_archive (abfd->my_archive))
+  while (abfd->my_archive != NULL
+        && !bfd_is_thin_archive (abfd->my_archive))
     {
-      if (direction == SEEK_SET && (bfd_vma) position == abfd->where)
-       return 0;
+      offset += abfd->origin;
+      abfd = abfd->my_archive;
     }
-  else
-    {
-      /* We need something smarter to optimize access to archives.
-        Currently, anything inside an archive is read via the file
-        handle for the archive.  Which means that a bfd_seek on one
-        component affects the `current position' in the archive, as
-        well as in any other component.
-
-        It might be sufficient to put a spike through the cache
-        abstraction, and look to the archive for the file position,
-        but I think we should try for something cleaner.
+  offset += abfd->origin;
 
-        In the meantime, no optimization for archives.  */
+  if (abfd->iovec == NULL)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return -1;
     }
 
-  file_position = position;
-  if (direction == SEEK_SET)
-    {
-      bfd *parent_bfd = abfd;
+  /* For the time being, a BFD may not seek to it's end.  The problem
+     is that we don't easily have a way to recognize the end of an
+     element in an archive.  */
+  BFD_ASSERT (direction == SEEK_SET || direction == SEEK_CUR);
 
-      while (parent_bfd->my_archive != NULL
-            && !bfd_is_thin_archive (parent_bfd->my_archive))
-       {
-         file_position += parent_bfd->origin;
-         parent_bfd = parent_bfd->my_archive;
-       }
-    }
+  if (direction != SEEK_CUR)
+    position += offset;
 
-  if (abfd->iovec)
-    result = abfd->iovec->bseek (abfd, file_position, direction);
-  else
-    result = -1;
+  if ((direction == SEEK_CUR && position == 0)
+      || (direction == SEEK_SET && (ufile_ptr) position == abfd->where))
+    return 0;
 
+  result = abfd->iovec->bseek (abfd, position, direction);
   if (result != 0)
     {
-      int hold_errno = errno;
-
-      /* Force redetermination of `where' field.  */
-      bfd_tell (abfd);
-
       /* An EINVAL error probably means that the file offset was
         absurd.  */
-      if (hold_errno == EINVAL)
+      if (errno == EINVAL)
        bfd_set_error (bfd_error_file_truncated);
       else
-       {
-         bfd_set_error (bfd_error_system_call);
-         errno = hold_errno;
-       }
+       bfd_set_error (bfd_error_system_call);
     }
   else
     {
       /* Adjust `where' field.  */
-      if (direction == SEEK_SET)
-       abfd->where = position;
-      else
+      if (direction == SEEK_CUR)
        abfd->where += position;
+      else
+       abfd->where = position;
     }
+
   return result;
 }
 
@@ -377,10 +395,7 @@ bfd_get_mtime (bfd *abfd)
   if (abfd->mtime_set)
     return abfd->mtime;
 
-  if (abfd->iovec == NULL)
-    return 0;
-
-  if (abfd->iovec->bstat (abfd, &buf) != 0)
+  if (bfd_stat (abfd, &buf) != 0)
     return 0;
 
   abfd->mtime = buf.st_mtime;          /* Save value in case anyone wants it */
@@ -418,20 +433,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;
-
-  if (abfd->iovec == NULL)
-    return 0;
+  /* 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 (abfd->iovec->bstat (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;
 }
 
 /*
@@ -451,11 +478,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;
 }
 
 /*
@@ -479,10 +519,19 @@ bfd_mmap (bfd *abfd, void *addr, bfd_size_type len,
          int prot, int flags, file_ptr offset,
          void **map_addr, bfd_size_type *map_len)
 {
-  void *ret = (void *)-1;
+  while (abfd->my_archive != NULL
+        && !bfd_is_thin_archive (abfd->my_archive))
+    {
+      offset += abfd->origin;
+      abfd = abfd->my_archive;
+    }
+  offset += abfd->origin;
 
   if (abfd->iovec == NULL)
-    return ret;
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return (void *) -1;
+    }
 
   return abfd->iovec->bmmap (abfd, addr, len, prot, flags, offset,
                             map_addr, map_len);
This page took 0.029451 seconds and 4 git commands to generate.