ungetc and fseek/ftell cause segmentation bug

Waclaw Kusnierczyk waku at idi.ntnu.no
Wed Jan 19 00:19:28 UTC 2011


There is indeed something wrong here.

I have rewritten your code to the essentials, to be able to check (a) 
whether the length of the file matters, (b) ensure none of the fseek, 
ftell, fgetc and ungetc functions reports an error before fclose 
crashes.  The code is attached.

The command I used to compile and execute the code is

     gcc $CFLAGS -o foo foo.c && ./foo; echo "return: $?"

CFLAGS is a sequence of flags used to conditionally include parts of the 
code.

To see that the length of the file is insignificant, include -DNEW 
-DLENGTH=n in CFLAGS, with variable n.  (Note, if the file is shorter 
than 2 characters AND you do not include both UNGETC and SEEKSET in 
CFLAGS, there will be a checked runtime failure with return code 12 (the 
second fgetc fails).  That's insubstantial here.)

When I compile the code with CFLAGS="" (no flags), the execution 
succeeds (return value is 0).
Likewise when CFLAGS="-DUNGETC" (unget the first character).
Likewise CFLAGS="-DSEEKEND" (seek to the end after open).
Likewise CFLAGS="-DSEEKSET" (seek to the start before second fgetc).
Likewise in all combinations of two of the three flags.
In all these cases, the output from printf is as expected.

However, with CFLAGS="-DSEEKEND -DSEEKSET -DUNGETC" (ungetc and both 
seeks), the program apparently crashes.  The return value is 134, and an 
invalid pointer error from free() is reported, similar to the one Stefan 
got, see below.

The output from printf reveals that after ungetc and seek to start, the 
position is negative (the actual value depends on the length N of the 
file, and is precisely 1 - N).  This happens for N > 2.  (So for N = 3, 
the third position reported is -2, etc.)

When N = 2, the execution fails with return code 11 (position after seek 
to start is EOF).

For N < 2, the execution fails as for N > 2 (dump, return 134), BUT this 
time it does not happen on fclose, but on the second fgetc (modify the 
code to convince yourself).

What is more fancy, is that if you add an innocent statement such as 
printf("\n") anywhere BEFORE the seek to end, the execution crashes but 
instead of the long dump a segfault is reported on fclose, and the 
return value is 139, not 134.

If this is not a bug, then it would be good to have this interesting 
behavior documented.

Regards,
vQ


On 01/18/2011 03:08 PM, Stefan Sablatnög wrote:
> Hi everybody,
>
> I encounter a bug with ungetc on many different glibc system, though all are
> ubuntu, at least debain I thought this place would be right.
>
> My problem is a segmentation fault on fclose, that happens when I run the
> following short test program. Assume a file named abc exists and it contains at
> least 10 characters. ( I use Hello)
>
> Here is the program:
>
> #include<stdio.h>
>
> int main(int argc, char *argv[])
> {
>      FILE *file = fopen("abc", "rb");
>      if (0 != file)
>      {
>          int pos = 0;
>          int len = 0;
>          int value1;
>
>          pos = ftell(file);
>          if(-1 == pos)
>          {
>              return -1;
>          }
>
>          if(0 != fseek(file, 0, SEEK_END))
>          {
>              return -1;
>          }
>
>          len = ftell(file);
>
>          if(0 != fseek(file, pos, SEEK_SET))
>          {
>              return -1;
>          }
>
>          value1 = fgetc(file);
>          printf("%c\n",value1);
>
>          ungetc(value1, file);
>          value1 = fgetc(file);
>          printf("%c\n",value1);
>
>          ungetc('#', file);
>          value1 = fgetc(file);
>          printf("%c\n",value1);
>
>          ungetc('?', file);
>          fseek(file, 0, SEEK_SET);
>          value1 = fgetc(file);
>          printf("%c\n",value1);
>
>          fclose(file);
>          printf("OK\n");
>      }
> }
>
> So there is nothing very fancy, just some harmless file operations. What I
> receive is:
>
> stefan at pilch2:/tmp$ echo "Hello">abc
> stefan at pilch2:/tmp$ ./test_ungetc
> H
> H
> #
> e
> *** glibc detected *** ./test_ungetc: free(): invalid pointer: 0xb787f000 ***
> ======= Backtrace: =========
> /lib/tls/i686/cmov/libc.so.6(+0x6b591)[0xb776f591]
> /lib/tls/i686/cmov/libc.so.6(+0x6cde8)[0xb7770de8]
> /lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xb7773ecd]
> /lib/tls/i686/cmov/libc.so.6(_IO_free_backup_area+0x34)[0xb776da34]
> /lib/tls/i686/cmov/libc.so.6(_IO_unsave_markers+0x32)[0xb776d752]
> /lib/tls/i686/cmov/libc.so.6(_IO_file_close_it+0x42)[0xb776c542]
> /lib/tls/i686/cmov/libc.so.6(fclose+0x188)[0xb775fae8]
> ./test_ungetc[0x804872f]
> /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb771abd6]
> ./test_ungetc[0x80484e1]
> ======= Memory map: ========
> 08048000-08049000 r-xp 00000000 08:04 16975036   /tmp/test_ungetc
> 08049000-0804a000 r--p 00000000 08:04 16975036   /tmp/test_ungetc
> 0804a000-0804b000 rw-p 00001000 08:04 16975036   /tmp/test_ungetc
> 09f8e000-09faf000 rw-p 00000000 00:00 0          [heap]
> b75bf000-b75dc000 r-xp 00000000 08:04 1387787    /lib/libgcc_s.so.1
> b75dc000-b75dd000 r--p 0001c000 08:04 1387787    /lib/libgcc_s.so.1
> b75dd000-b75de000 rw-p 0001d000 08:04 1387787    /lib/libgcc_s.so.1
> b7600000-b7621000 rw-p 00000000 00:00 0
> b7621000-b7700000 ---p 00000000 00:00 0
> b7703000-b7704000 rw-p 00000000 00:00 0
> b7704000-b7857000 r-xp 00000000 08:04 51428497
> /lib/tls/i686/cmov/libc-2.11.1.so
> b7857000-b7858000 ---p 00153000 08:04 51428497
> /lib/tls/i686/cmov/libc-2.11.1.so
> b7858000-b785a000 r--p 00153000 08:04 51428497
> /lib/tls/i686/cmov/libc-2.11.1.so
> b785a000-b785b000 rw-p 00155000 08:04 51428497
> /lib/tls/i686/cmov/libc-2.11.1.so
> b785b000-b785e000 rw-p 00000000 00:00 0
> b787e000-b7882000 rw-p 00000000 00:00 0
> b7882000-b7883000 r-xp 00000000 00:00 0          [vdso]
> b7883000-b789e000 r-xp 00000000 08:04 3094462    /lib/ld-2.11.1.so
> b789e000-b789f000 r--p 0001a000 08:04 3094462    /lib/ld-2.11.1.so
> b789f000-b78a0000 rw-p 0001b000 08:04 3094462    /lib/ld-2.11.1.so
> bf84c000-bf861000 rw-p 00000000 00:00 0          [stack]
> Aborted
> stefan at pilch2:/tmp$
>
>
> Most annoying of course is the fact, that the call to fclose causes a crash.
>
> Less problematic, but also wrong is the file position after the last fseek,
> fseek is expected to throw away what was put back to the stream, but
> it should position on the first letter (H) not on the 'e'.
>
> I checked the following platforms:
>
> ubuntu 10.04 desktop (including all updates up to January 18th 2011) 32bit
> ubuntu 9.04 on arm
> ubuntu on hppa
> older debian on x86_64
> older debian on sparc32
>
> with glibc versions :
>
> Version: 2.11.1-0ubuntu7.7 (x86 32 bit)
> Version: 2.11.1-0ubuntu7.2 (x86 32 bit)
> Version: 2.7-18 (x86_64)
> Version: 2.10.1-0ubuntu15 (arm)
> Version: 2.6.1-6ubuntu2 (hppa)
> Version: 2.3.6.ds1-13etch10 (sparc32)
>
> all these platforms show this behaviour. Unfortunately I do not have a system
> with glibc version 2.12.2 which seems to be the latest one at the moment.
>
> Is this a known Bug in glibc?
> Is there a workaround?
> Can the circumstances for it to happen be defined more precisely?
> Should I submit this bug to glibc directly?
>
> thanks in advance for your support!
>

-------------- next part --------------
A non-text attachment was scrubbed...
Name: foo.c
Type: text/x-csrc
Size: 1189 bytes
Desc: not available
URL: <https://lists.ubuntu.com/archives/ubuntu-devel-discuss/attachments/20110118/93059526/attachment.c>


More information about the Ubuntu-devel-discuss mailing list