[Bug libfortran/31880] New: silent data corruption in gfortran read statement

roconnor at health dot usf dot edu gcc-bugzilla@gcc.gnu.org
Wed May 9 15:56:00 GMT 2007


This program should print out "1".
Instead, it prints out "1024":

      program r3

      integer*4 a(1025),b(1025),c(1025),d(2048),e(1022)

      do i=1,2048
         d(i)=i
      end do

      open  (3,file='a',form='unformatted')
      write (3) a,b,c,d,e
      rewind 3
      read  (3) a,b,c,d
      close (3)

      print *,d(1)

      end

output of /opt/bin/gfortran -v:

Using built-in specs.
Target: x86_64-unknown-linux-gnu
Configured with: ../gcc-4.1.2/configure --prefix=/opt --disable-multilib
Thread model: posix
gcc version 4.1.2

It was compiled with:
/opt/bin/gfortran -Wall -o r3 r3.f

and run with:
LD_LIBRARY_PATH=/opt/lib64 ./r3

This patch fixes it:

--- gcc-4.1-4.1.2/gcc-4.1.2/libgfortran/io/unix.c       2006-05-29
22:51:26.000000000 -0400
+++ gcc-4.1-4.1.2_patched/gcc-4.1.2/libgfortran/io/unix.c       2007-05-08
19:22:09.000000000 -0400
@@ -465,7 +465,7 @@
       if (n < 0)
        return NULL;

-      s->physical_offset = where + n;
+      s->physical_offset = m + n;
       s->active += n;
     }
   else
@@ -476,7 +476,7 @@
       if (do_read (s, s->buffer + s->active, &n) != 0)
        return NULL;

-      s->physical_offset = where + n;
+      s->physical_offset = m + n;
       s->active += n;
     }


The problem is that s->physical_offset is being miscalculated as
where+n, which is the requested read start address plus the bytes read
from the disk to fill the buffer.  It should be where+s->active+n
(=m+n), the requested read start address plus the as-yet-unread bytes
still in the buffer plus the bytes read from the disk to fill the
buffer.  The s->logical_offset is correctly set to where+*len.

What happens is that if an unbuffered (<8192 bytes) read of more than
half the buffer (array b in the example) exceeds the data remaining in
the buffer, the remaining data will get moved to the beginning of the
buffer, and some amount of data will be read from disk to fill the
buffer.  If the next read (array c) is of the same length, it will
also exceed the data remaining in the buffer, the remaining data will
get moved to the beginning of the buffer, and this time the length of
the data read from the disk (n) will be the same as the requested read
length (*len).  Since the physical_offset is being set to where+n and
s->logical_offset is set to where+*len, they are equal.  If the next
read is >=8192 bytes (array d), it will be unbuffered, which would
normally cause the data remaining in the buffer to be ignored and
cause a seek to re-read that data along with the rest of the request,
from the disk.  But the code will decide that it doesn't have to seek
because physical_offset=logical_offset.  So the remaining data in the
buffer will be lost.


-- 
           Summary: silent data corruption in gfortran read statement
           Product: gcc
           Version: 4.1.2
            Status: UNCONFIRMED
          Severity: critical
          Priority: P3
         Component: libfortran
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: roconnor at health dot usf dot edu
 GCC build triplet: x86_64-unknown-linux-gnu
  GCC host triplet: x86_64-unknown-linux-gnu
GCC target triplet: x86_64-unknown-linux-gnu


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31880



More information about the Gcc-bugs mailing list