mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-08 08:26:14 +00:00
291 lines
9.1 KiB
C
291 lines
9.1 KiB
C
/* This program attempts to verify that all functions that are
|
|
supposed to be wrapped by tc_intercepts.c really are wrapped. The
|
|
main way it does this is to cause failures in those functions, so
|
|
as to obtain various error messages which imply that the wrapper
|
|
really did engage.
|
|
|
|
Any regressions shown up by this program are potentially serious
|
|
and should be investigated carefully. */
|
|
|
|
/* Needed for older glibcs (2.3 and older, at least) who don't
|
|
otherwise "know" about some more exotic pthread stuff, in this case
|
|
PTHREAD_MUTEX_ERRORCHECK. */
|
|
#define _GNU_SOURCE 1
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include "safe-pthread.h"
|
|
#include "safe-semaphore.h"
|
|
|
|
#if !defined(__APPLE__)
|
|
|
|
#if defined(__sun__)
|
|
/* Fake __GLIBC_PREREQ on Solaris. Pretend glibc >= 2.4. */
|
|
# define __GLIBC_PREREQ
|
|
#else
|
|
#if !defined(__GLIBC_PREREQ)
|
|
# error "This program needs __GLIBC_PREREQ (in /usr/include/features.h)"
|
|
#endif
|
|
#endif /* __sun__ */
|
|
|
|
short unprotected = 0;
|
|
|
|
void* lazy_child ( void* v ) {
|
|
assert(0); /* does not run */
|
|
}
|
|
|
|
void* racy_child ( void* v ) {
|
|
unprotected = 1234;
|
|
return NULL;
|
|
}
|
|
|
|
int main ( void )
|
|
{
|
|
int r;
|
|
/* pthread_t thr; */
|
|
/* pthread_attr_t thra; */
|
|
pthread_mutexattr_t mxa, mxa2;
|
|
pthread_mutex_t mx, mx2, mx3, mx4;
|
|
pthread_cond_t cv;
|
|
struct timespec abstime;
|
|
pthread_rwlock_t rwl;
|
|
pthread_rwlock_t rwl2;
|
|
pthread_rwlock_t rwl3;
|
|
sem_t s1;
|
|
|
|
# if __GLIBC_PREREQ(2,4)
|
|
fprintf(stderr,
|
|
"\n\n------ This is output for >= glibc 2.4 ------\n");
|
|
# else
|
|
fprintf(stderr,
|
|
"\n\n------ This is output for < glibc 2.4 ------\n");
|
|
# endif
|
|
|
|
/* --------- pthread_create/join --------- */
|
|
|
|
fprintf(stderr,
|
|
"\n---------------- pthread_create/join ----------------\n\n");
|
|
|
|
/* make pthread_create fail */
|
|
/* It's amazingly difficult to make pthread_create fail
|
|
without first soaking up all the machine's resources.
|
|
Instead, in order to demonstrate that it's really wrapped,
|
|
create a child thread, generate a race error, and join with it
|
|
again. */
|
|
/* This just segfaults:
|
|
memset( &thra, 0xFF, sizeof(thra) );
|
|
r= pthread_create( &thr, NULL, lazy_child, NULL ); assert(r);
|
|
*/
|
|
{ pthread_t child;
|
|
r= pthread_create( &child, NULL, racy_child, NULL ); assert(!r);
|
|
sleep(1); /* just to ensure parent thread reports race, not child */
|
|
unprotected = 5678;
|
|
r= pthread_join( child, NULL ); assert(!r);
|
|
}
|
|
|
|
/* make pthread_join fail */
|
|
r= pthread_join( pthread_self(), NULL ); assert(r);
|
|
|
|
/* --------- pthread_mutex_lock et al --------- */
|
|
|
|
fprintf(stderr,
|
|
"\n---------------- pthread_mutex_lock et al ----------------\n\n");
|
|
|
|
/* make pthread_mutex_init fail */
|
|
#if defined(__sun__)
|
|
pthread_mutexattr_init( &mxa );
|
|
memset( mxa.__pthread_mutexattrp, 0xFF, 5 * sizeof(int) );
|
|
#else
|
|
memset( &mxa, 0xFF, sizeof(mxa) );
|
|
#endif
|
|
r= pthread_mutex_init( &mx, &mxa );
|
|
# if __GLIBC_PREREQ(2,4)
|
|
assert(r); /* glibc >= 2.4: the call should fail */
|
|
# else
|
|
assert(!r); /* glibc < 2.4: oh well, glibc didn't bounce this */
|
|
# endif
|
|
|
|
/* make pthread_mutex_destroy fail */
|
|
r= pthread_mutex_init( &mx2, NULL ); assert(!r);
|
|
r= pthread_mutex_lock( &mx2 ); assert(!r);
|
|
r= pthread_mutex_destroy( &mx2 );
|
|
|
|
/* make pthread_mutex_lock fail (skipped on < glibc 2.4 because it
|
|
doesn't fail, hence hangs the test) */
|
|
# if __GLIBC_PREREQ(2,4)
|
|
memset( &mx3, 0xFF, sizeof(mx3) );
|
|
r= pthread_mutex_lock( &mx3 ); assert(r);
|
|
# else
|
|
fprintf(stderr, "\nmake pthread_mutex_lock fail: "
|
|
"skipped on glibc < 2.4\n\n");
|
|
# endif
|
|
|
|
/* make pthread_mutex_trylock fail */
|
|
memset( &mx3, 0xFF, sizeof(mx3) );
|
|
r= pthread_mutex_trylock( &mx3 ); assert(r);
|
|
|
|
/* make pthread_mutex_timedlock fail */
|
|
memset( &abstime, 0, sizeof(abstime) );
|
|
memset( &mx3, 0xFF, sizeof(mx3) );
|
|
r= pthread_mutex_timedlock( &mx3, &abstime ); assert(r);
|
|
|
|
/* make pthread_mutex_unlock fail */
|
|
memset( &mx3, 0xFF, sizeof(mx3) );
|
|
r= pthread_mutex_unlock( &mx3 );
|
|
# if __GLIBC_PREREQ(2,4)
|
|
assert(r);
|
|
# else
|
|
assert(!r);
|
|
# endif
|
|
|
|
/* --------- pthread_cond_wait et al --------- */
|
|
|
|
fprintf(stderr,
|
|
"\n---------------- pthread_cond_wait et al ----------------\n\n");
|
|
|
|
/* make pthread_cond_wait fail. This is difficult. Our cunning
|
|
plan (tm) is to show up at pthread_cond_wait bearing a
|
|
not-locked mutex of the ERRORCHECK flavour and hope (as is
|
|
indeed the case with glibc-2.5) that pthread_cond_wait notices
|
|
it is not locked, and bounces our request. */
|
|
r= pthread_mutexattr_init( &mxa2 ); assert(!r);
|
|
r= pthread_mutexattr_settype( &mxa2, PTHREAD_MUTEX_ERRORCHECK );
|
|
assert(!r);
|
|
r= pthread_mutex_init( &mx4, &mxa2 ); assert(!r);
|
|
r= pthread_cond_init( &cv, NULL ); assert(!r);
|
|
r= pthread_cond_wait( &cv, &mx4 ); assert(r);
|
|
r= pthread_mutexattr_destroy( &mxa2 ); assert(!r);
|
|
|
|
/* make pthread_cond_signal fail. FIXME: can't figure out how
|
|
to */
|
|
r= pthread_cond_signal( &cv ); assert(!r);
|
|
fprintf(stderr, "\nFIXME: can't figure out how to "
|
|
"verify wrap of pthread_cond_signal\n\n");
|
|
|
|
/* make pthread_cond_broadcast fail. FIXME: can't figure out how
|
|
to */
|
|
r= pthread_cond_broadcast( &cv ); assert(!r);
|
|
fprintf(stderr, "\nFIXME: can't figure out how to "
|
|
"verify wrap of pthread_broadcast_signal\n\n");
|
|
|
|
/* make pthread_cond_timedwait fail. */
|
|
memset( &abstime, 0, sizeof(abstime) );
|
|
abstime.tv_nsec = 1000000000 + 1;
|
|
r= pthread_cond_timedwait( &cv, &mx4, &abstime ); assert(r);
|
|
|
|
/* --------- pthread_rwlock_* --------- */
|
|
|
|
fprintf(stderr,
|
|
"\n---------------- pthread_rwlock_* ----------------\n\n");
|
|
|
|
/* pthread_rwlock_init, pthread_rwlock_unlock */
|
|
/* pthread_rwlock_init: can't make glibc's implementation fail.
|
|
However, can demonstrate interceptedness by initialising but not
|
|
locking a lock and then unlocking it. Then the unlock call
|
|
should say "first seen at .. the init call." So this tests
|
|
wrappedness of both calls. */
|
|
r= pthread_rwlock_init( &rwl, NULL ); assert(!r);
|
|
r= pthread_rwlock_unlock( &rwl );
|
|
/* assert(r); *//* glibc doesn't complain. It really ought to. Oh well. */
|
|
|
|
/* We can infer the presence of wrapping for pthread_rwlock_rdlock,
|
|
pthread_rwlock_wrlock and pthread_rwlock_unlock by making
|
|
Thrcheck count the lockedness state, and warning when we unlock
|
|
a not-locked lock. Thusly: */
|
|
r= pthread_rwlock_init( &rwl2, NULL ); assert(!r);
|
|
|
|
/* w-lock it */
|
|
fprintf(stderr, "(1) no error on next line\n");
|
|
r= pthread_rwlock_wrlock( &rwl2 ); assert(!r);
|
|
/* unlock it */
|
|
fprintf(stderr, "(2) no error on next line\n");
|
|
r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
|
|
/* unlock it again, get an error */
|
|
fprintf(stderr, "(3) ERROR on next line\n");
|
|
r= pthread_rwlock_unlock( &rwl2 );
|
|
#if defined(__sun__)
|
|
assert(r);
|
|
#else
|
|
assert(!r);
|
|
#endif
|
|
|
|
/* same game with r-locks */
|
|
r= pthread_rwlock_init( &rwl2, NULL ); assert(!r);
|
|
/* r-lock it twice */
|
|
fprintf(stderr, "(4) no error on next line\n");
|
|
r= pthread_rwlock_rdlock( &rwl2 ); assert(!r);
|
|
fprintf(stderr, "(5) no error on next line\n");
|
|
r= pthread_rwlock_rdlock( &rwl2 ); assert(!r);
|
|
/* unlock it twice */
|
|
fprintf(stderr, "(6) no error on next line\n");
|
|
r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
|
|
fprintf(stderr, "(7) no error on next line\n");
|
|
r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
|
|
/* unlock it again, get an error */
|
|
fprintf(stderr, "(8) ERROR on next line\n");
|
|
r= pthread_rwlock_unlock( &rwl2 );
|
|
#if defined(__sun__)
|
|
assert(r);
|
|
#else
|
|
assert(!r);
|
|
#endif
|
|
|
|
/* Lock rwl3 so the locked-lock-at-dealloc check can complain about
|
|
it. */
|
|
r= pthread_rwlock_init( &rwl3, NULL ); assert(!r);
|
|
r= pthread_rwlock_rdlock( &rwl3 ); assert(!r);
|
|
|
|
/* ------------- sem_* ------------- */
|
|
|
|
/* This is pretty lame, and duplicates tc18_semabuse.c. */
|
|
|
|
fprintf(stderr,
|
|
"\n---------------- sem_* ----------------\n\n");
|
|
|
|
/* verifies wrap of sem_init */
|
|
/* Do sem_init with huge initial count - fails */
|
|
r= sem_init(&s1, 0, ~0); assert(r);
|
|
|
|
/* initialise properly */
|
|
r= sem_init(&s1, 0, 0);
|
|
|
|
/* in glibc, sem_destroy is a no-op; making it fail is
|
|
impossible. */
|
|
fprintf(stderr, "\nFIXME: can't figure out how to verify wrap of "
|
|
"sem_destroy\n\n");
|
|
|
|
/* verifies wrap of sem_wait */
|
|
/* Do 'wait' on a bogus semaphore. This should fail, but on glibc
|
|
it succeeds. */
|
|
memset(&s1, 0x55, sizeof(s1));
|
|
r= sem_wait(&s1); /* assert(r != 0); */
|
|
|
|
/* this only fails with glibc 2.7 or later. */
|
|
r= sem_post(&s1);
|
|
fprintf(stderr, "\nFIXME: can't figure out how to verify wrap of "
|
|
"sem_post\n\n");
|
|
|
|
sem_destroy(&s1);
|
|
|
|
/* ------------- dealloc of mem holding locks ------------- */
|
|
|
|
fprintf(stderr,
|
|
"\n------------ dealloc of mem holding locks ------------\n\n");
|
|
|
|
/* At this point it should complain about deallocation
|
|
of memory containing locked locks:
|
|
rwl3
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else /* defined(__APPLE__) */
|
|
int main ( void )
|
|
{
|
|
fprintf(stderr, "This program does not work on Mac OS X.\n");
|
|
return 0;
|
|
}
|
|
#endif
|