Reputation: 229274
I've distilled a larger program down to the code shown at the bottom. Running this program in valgrind will eventually report this error:
==7234== Invalid read of size 4 ==7234== at 0x34A7275FC8: _IO_file_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.15.so) ==7234== by 0x34A7275EA1: new_do_write (in /usr/lib64/libc-2.15.so) ==7234== by 0x34A7276D44: _IO_do_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.15.so) ==7234== by 0x34A7278DB6: _IO_flush_all_lockp (in /usr/lib64/libc-2.15.so) ==7234== by 0x34A7278F07: _IO_cleanup (in /usr/lib64/libc-2.15.so) ==7234== by 0x34A7238BBF: __run_exit_handlers (in /usr/lib64/libc-2.15.so) ==7234== by 0x34A7238BF4: exit (in /usr/lib64/libc-2.15.so) ==7234== by 0x34A722173B: (below main) (in /usr/lib64/libc-2.15.so) ==7234== Address 0x542f2e0 is 0 bytes inside a block of size 568 free'd ==7234== at 0x4A079AE: free (vg_replace_malloc.c:427) ==7234== by 0x34A726B11C: fclose@@GLIBC_2.2.5 (in /usr/lib64/libc-2.15.so) ==7234== by 0x40087C: writer (t.c:22) ==7234== by 0x34A7607D13: start_thread (in /usr/lib64/libpthread-2.15.so) ==7234== by 0x34A72F167C: clone (in /usr/lib64/libc-2.15.so)
From the above output, this seems to be what's happening:
As far as I can tell, the test program doesn't do anything undefined, but I'd be happy to be wrong on that.
Valgrind hooks into various functions, so it is possible it is a valgrind bug and not glibc.
Or is it a valgrind bug ?
Any ideas on how to determine whether it's valgrind or glibc ?
t.c:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *test(void *arg)
{
return NULL;
}
void *writer(void *arg)
{
for(;;) {
char a[100];
FILE *f = fopen("out", "w");
if(f == NULL)
abort();
fputs("Test", f);
if(fgets(a, 100, stdin))
fputs(a, f);
fclose(f); //line 22
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1,tid2;
pthread_create(&tid1, NULL, writer, NULL);
pthread_create(&tid2, NULL, test, NULL);
pthread_join(tid2, NULL);
//pthread_join(tid1, NULL); //no bug if we wait for writer()
return 0;
}
//compile: gcc t.c -g -pthread
May take several minutes to trigger an error from valgrind, with:
while [ true ]; do
echo test |valgrind --error-exitcode=2 ./a.out || break
done
Environment: Fedora 17, glibc-2.15, gcc-4.7.0-5, kernel 3.5.3-1.fc17.x86_64 , valgrind-3.7.0-4
Upvotes: 2
Views: 1568
Reputation: 182837
You have a race condition. You have a thread that calls exit
, which is documented to close all open stdio streams. You then have another thread that, potentially after exit
has closed it, accesses such a stream. You cannot access a FILE*
after it's closed -- it is permitted to point to garbage.
If a thread does something that makes calling exit
unsafe, you must ensure you don't call exit
. It's really that simple.
Upvotes: 4