Bug 1377

Summary: seg fault inside getwc() when using LD_PRELOADed code
Product: glibc Reporter: manuelarriaga
Component: libcAssignee: GOTO Masanori <gotom>
Status: RESOLVED INVALID    
Severity: critical CC: glibc-bugs, kkylheku, manuelarriaga
Priority: P1 Flags: fweimer: security-
Version: 2.3.4   
Target Milestone: ---   
Host: i686-pc-linux-gnu Target: i686-pc-linux-gnu
Build: i686-pc-linux-gnu Last reconfirmed:

Description manuelarriaga 2005-09-23 22:09:34 UTC
A segmentation fault occurs inside getwc() if this function is called on a file
pointer obtained from a preloaded fopen() which merely returns the (FILE*)
pointer returned by the glibc fopen().

System: Linux kernel 2.6.11.4, gcc 3.3.4

To replicate:

1) compile the following code snippet

/*

Compiled with 

gcc -Wall -W -D_REENTRANT libtest.c -nostartfiles -shared -fPIC
-Wl,-soname,libtest.so -o libtest.so -ldl
 * 
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <dlfcn.h>

FILE *fopen(const char *path, const char *mode)
{
   FILE * (*real_fopen) (const char*,const char*);
   
   real_fopen = dlsym(RTLD_NEXT, "fopen");
   
   if (!real_fopen)
     {
        printf("dlsym() failed\n.");
        return NULL;
     }

   printf("invoking real fopen\n");
   return (*real_fopen)(path,mode);
}


2) compile the following test program

/* 

Compiled with

gcc -o test test.c

*/

#include <stdio.h>
#include <wchar.h>

int main (void) 
{
   
   FILE *fp = fopen("abcd", "r");

   if (!fp)
     {
        printf("inside test program: fp == NULL, quitting.\n");
        return 0;
     }
   
   wint_t w = getwc(fp);
   return 0;
}

3) run

LD_PRELOAD=./libtest.so ./test

This generates a segmentation fault inside getwc().
Comment 1 Jakub Jelinek 2005-09-23 22:23:20 UTC
If you look carefully, you'll see that on several arches, including i?86-linux,
there are several fopen functions:
readelf -Ws /lib/libc.so.6 | grep \ fopen@
  1210: 0046678b   140 FUNC    GLOBAL DEFAULT   11 fopen@GLIBC_2.0
  1882: 003bd512    50 FUNC    GLOBAL DEFAULT   11 fopen@@GLIBC_2.1
  6849: 0046678b   140 FUNC    GLOBAL DEFAULT   11 fopen@GLIBC_2.0
  7521: 003bd512    50 FUNC    GLOBAL DEFAULT   11 fopen@@GLIBC_2.1
When you use dlsym and not dlvsym, you bind to the oldest one, backwards
compatible fopen which doesn't support wide char stdio.
Comment 2 manuelarriaga 2005-09-23 22:34:52 UTC
Subject: Re:  seg fault inside getwc() when using LD_PRELOADed code

Thank you for your email. Is there a way to automatically bind to the
most recent version available on the system? (Or is there a function
which will provide me with the name of the most recent version found
given a symbol name?)

Manuel

On 23 Sep 2005 22:23:24 -0000, jakub at redhat dot com
<sourceware-bugzilla@sourceware.org> wrote:
>
> ------- Additional Comments From jakub at redhat dot com  2005-09-23 22:23 -------
> If you look carefully, you'll see that on several arches, including i?86-linux,
> there are several fopen functions:
> readelf -Ws /lib/libc.so.6 | grep \ fopen@
>   1210: 0046678b   140 FUNC    GLOBAL DEFAULT   11 fopen@GLIBC_2.0
>   1882: 003bd512    50 FUNC    GLOBAL DEFAULT   11 fopen@@GLIBC_2.1
>   6849: 0046678b   140 FUNC    GLOBAL DEFAULT   11 fopen@GLIBC_2.0
>   7521: 003bd512    50 FUNC    GLOBAL DEFAULT   11 fopen@@GLIBC_2.1
> When you use dlsym and not dlvsym, you bind to the oldest one, backwards
> compatible fopen which doesn't support wide char stdio.
>
> --
>            What    |Removed                     |Added
> ----------------------------------------------------------------------------
>              Status|NEW                         |RESOLVED
>          Resolution|                            |INVALID
>
>
> http://sourceware.org/bugzilla/show_bug.cgi?id=1377
>
> ------- You are receiving this mail because: -------
> You reported the bug, or are watching the reporter.
> You are on the CC list for the bug, or are watching someone who is.
>
Comment 3 Kaz Kylheku 2009-11-12 21:46:16 UTC
I'm seeing a crash in getwc on an older installation of glibc (glibc 2.3.4).

The FILE * in this case did not come from fopen, but rather from popen.

No tricks with shared libraries are being played.

$ cat popen_getwc.c
#include <unistd.h>
#include <stdio.h>
#include <wchar.h>

int main(void)
{
  FILE *command = popen("ls", "r");
  wint_t ch = getwc(command);
  pclose(command);
  return ch;
}
$ gcc -Wall popen_getwc.c -o popen_getwc
$ ./popen_getwc
Segmentation fault
$ gcc --version
gcc (GCC) 3.4.3 20050227 (Red Hat 3.4.3-22.1)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ /lib/libc.so.6
GNU C Library stable release version 2.3.4, by Roland McGrath et al.
Copyright (C) 2005 Free Software Foundation, Inc.
[ ... etc ... ]


The crash is unaffected by whether or not we call setlocale to have LC_CTYPE 
set up for multi-byte encodings or not.


I'm sticking the comment here because it affects an glibc version from around 
the time when this original bug was reported, and they seem related. I 
couldn't find anything else in the bug database about a crash in getwc.

It's understandable that using dlsym to get to the wrong version of fopen is 
like sticking a fork in the toaster, hence ``RESOLVED INVALID''.

But is it also ``INVALID'' to be doing getwc on a popen'ed stream?

I'm going to try the workaround of implementing popen from scratch, so that 
the stream is then just created with fdopen.  The fdopen function is not 
affected by this problem; I can drop in fdopen/fclose in the place of 
popen/pclose in the above testcase and it does not crash:

#include <unistd.h>
#include <stdio.h>
#include <wchar.h>

int main(void)
{
  FILE *command = fdopen(0, "r");
  wint_t ch = getwc(command); /* <- no problem */
  pclose(command);
  return ch;
}
Comment 4 Kaz Kylheku 2009-11-13 00:25:21 UTC
(In reply to comment #3)
> I'm going to try the workaround of implementing popen from scratch, so that 
> the stream is then just created with fdopen.

There is a much simpler workaround which may work well for some applications, 
like mine, which have already wrapped streams in an object that can be 
extended with extra context info.

The recipe is: Create the FILE * command stream with popen. Then pull out the 
file descriptor with fileno, duplicate it with dup, and use fdopen to create a 
new FILE * descriptor on the duplicate. Then use the new FILE * in place of 
the old for I/O operations.  Keep the original handle in order to call pclose, 
to collect the process exit status.
Comment 5 Petr Baudis 2009-11-13 18:50:24 UTC
Your bugreport seems valid, however please don't hijack old bugs for this, it
seems to have nothing to do with the original bugreport, open a new bug instead.
Comment 6 Kaz Kylheku 2009-11-14 03:49:03 UTC
(In reply to comment #5)
> Your bugreport seems valid, however please don't hijack old bugs for this, it
> seems to have nothing to do with the original bugreport, open a new bug 
instead.

No can do. I got run over by a bus this morning. But, oops, someone flipped 
the bug status without creating a new record. So it fell through the cracks.
Comment 7 Petr Baudis 2009-11-14 11:08:16 UTC
well, thanks for not making life of your overloaded glibc developers easier; bug
10958