1 /* This program attempts to verify that all functions that are
2    supposed to be wrapped by tc_intercepts.c really are wrapped.  The
3    main way it does this is to cause failures in those functions, so
4    as to obtain various error messages which imply that the wrapper
5    really did engage.
6 
7    Any regressions shown up by this program are potentially serious
8    and should be investigated carefully. */
9 
10 /* Needed for older glibcs (2.3 and older, at least) who don't
11    otherwise "know" about some more exotic pthread stuff, in this case
12    PTHREAD_MUTEX_ERRORCHECK. */
13 #define _GNU_SOURCE 1
14 #include <stdio.h>
15 #include <string.h>
16 #include <assert.h>
17 #include <unistd.h>
18 #include <pthread.h>
19 #include <semaphore.h>
20 
21 #if !defined(__APPLE__)
22 
23 #if !defined(__GLIBC_PREREQ)
24 # error "This program needs __GLIBC_PREREQ (in /usr/include/features.h)"
25 #endif
26 
27 short unprotected = 0;
28 
lazy_child(void * v)29 void* lazy_child ( void* v ) {
30    assert(0); /* does not run */
31 }
32 
racy_child(void * v)33 void* racy_child ( void* v ) {
34    unprotected = 1234;
35    return NULL;
36 }
37 
main(void)38 int main ( void )
39 {
40    int r;
41    /* pthread_t thr; */
42    /* pthread_attr_t thra; */
43    pthread_mutexattr_t mxa, mxa2;
44    pthread_mutex_t mx, mx2, mx3, mx4;
45    pthread_cond_t cv;
46    struct timespec abstime;
47    pthread_rwlock_t rwl;
48    pthread_rwlock_t rwl2;
49    pthread_rwlock_t rwl3;
50    sem_t s1;
51 
52 #  if __GLIBC_PREREQ(2,4)
53    fprintf(stderr,
54            "\n\n------ This is output for >= glibc 2.4 ------\n");
55 #  else
56    fprintf(stderr,
57            "\n\n------ This is output for < glibc 2.4 ------\n");
58 #  endif
59 
60    /* --------- pthread_create/join --------- */
61 
62    fprintf(stderr,
63    "\n---------------- pthread_create/join ----------------\n\n");
64 
65    /* make pthread_create fail */
66    /* It's amazingly difficult to make pthread_create fail
67       without first soaking up all the machine's resources.
68       Instead, in order to demonstrate that it's really wrapped,
69       create a child thread, generate a race error, and join with it
70       again. */
71    /* This just segfaults:
72       memset( &thra, 0xFF, sizeof(thra) );
73       r= pthread_create( &thr, NULL, lazy_child, NULL ); assert(r);
74    */
75    { pthread_t child;
76      r= pthread_create( &child, NULL, racy_child, NULL ); assert(!r);
77      sleep(1); /* just to ensure parent thread reports race, not child */
78      unprotected = 5678;
79      r= pthread_join( child, NULL ); assert(!r);
80    }
81 
82    /* make pthread_join fail */
83    r= pthread_join( pthread_self(), NULL ); assert(r);
84 
85    /* --------- pthread_mutex_lock et al --------- */
86 
87    fprintf(stderr,
88    "\n---------------- pthread_mutex_lock et al ----------------\n\n");
89 
90    /* make pthread_mutex_init fail */
91    memset( &mxa, 0xFF, sizeof(mxa) );
92    r= pthread_mutex_init( &mx, &mxa );
93 #  if __GLIBC_PREREQ(2,4)
94    assert(r); /* glibc >= 2.4: the call should fail */
95 #  else
96    assert(!r); /* glibc < 2.4: oh well, glibc didn't bounce this */
97 #  endif
98 
99    /* make pthread_mutex_destroy fail */
100    r= pthread_mutex_init( &mx2, NULL ); assert(!r);
101    r= pthread_mutex_lock( &mx2 ); assert(!r);
102    r= pthread_mutex_destroy( &mx2 );
103 
104    /* make pthread_mutex_lock fail (skipped on < glibc 2.4 because it
105       doesn't fail, hence hangs the test) */
106 #  if __GLIBC_PREREQ(2,4)
107    memset( &mx3, 0xFF, sizeof(mx3) );
108    r= pthread_mutex_lock( &mx3 ); assert(r);
109 #  else
110    fprintf(stderr, "\nmake pthread_mutex_lock fail: "
111                    "skipped on glibc < 2.4\n\n");
112 #  endif
113 
114    /* make pthread_mutex_trylock fail */
115    memset( &mx3, 0xFF, sizeof(mx3) );
116    r= pthread_mutex_trylock( &mx3 ); assert(r);
117 
118    /* make pthread_mutex_timedlock fail */
119    memset( &abstime, 0, sizeof(abstime) );
120    memset( &mx3, 0xFF, sizeof(mx3) );
121    r= pthread_mutex_timedlock( &mx3, &abstime ); assert(r);
122 
123    /* make pthread_mutex_unlock fail */
124    memset( &mx3, 0xFF, sizeof(mx3) );
125    r= pthread_mutex_unlock( &mx3 );
126 #  if __GLIBC_PREREQ(2,4)
127    assert(r);
128 #  else
129    assert(!r);
130 #  endif
131 
132    /* --------- pthread_cond_wait et al --------- */
133 
134    fprintf(stderr,
135    "\n---------------- pthread_cond_wait et al ----------------\n\n");
136 
137    /* make pthread_cond_wait fail.  This is difficult.  Our cunning
138       plan (tm) is to show up at pthread_cond_wait bearing a
139       not-locked mutex of the ERRORCHECK flavour and hope (as is
140       indeed the case with glibc-2.5) that pthread_cond_wait notices
141       it is not locked, and bounces our request. */
142    r= pthread_mutexattr_init( &mxa2 ); assert(!r);
143    r= pthread_mutexattr_settype( &mxa2, PTHREAD_MUTEX_ERRORCHECK );
144       assert(!r);
145    r= pthread_mutex_init( &mx4, &mxa2 ); assert(!r);
146    r= pthread_cond_init( &cv, NULL ); assert(!r);
147    r= pthread_cond_wait( &cv, &mx4 ); assert(r);
148    r= pthread_mutexattr_destroy( &mxa2 ); assert(!r);
149 
150    /* make pthread_cond_signal fail.  FIXME: can't figure out how
151       to */
152    r= pthread_cond_signal( &cv ); assert(!r);
153    fprintf(stderr, "\nFIXME: can't figure out how to "
154                    "verify wrap of pthread_cond_signal\n\n");
155 
156    /* make pthread_cond_broadcast fail.  FIXME: can't figure out how
157       to */
158    r= pthread_cond_broadcast( &cv ); assert(!r);
159    fprintf(stderr, "\nFIXME: can't figure out how to "
160                    "verify wrap of pthread_broadcast_signal\n\n");
161 
162    /* make pthread_cond_timedwait fail. */
163    memset( &abstime, 0, sizeof(abstime) );
164    abstime.tv_nsec = 1000000000 + 1;
165    r= pthread_cond_timedwait( &cv, &mx4, &abstime ); assert(r);
166 
167    /* --------- pthread_rwlock_* --------- */
168 
169    fprintf(stderr,
170    "\n---------------- pthread_rwlock_* ----------------\n\n");
171 
172    /* pthread_rwlock_init, pthread_rwlock_unlock */
173    /* pthread_rwlock_init: can't make glibc's implementation fail.
174       However, can demonstrate interceptedness by initialising but not
175       locking a lock and then unlocking it.  Then the unlock call
176       should say "first seen at .. the init call."  So this tests
177       wrappedness of both calls. */
178    r= pthread_rwlock_init( &rwl, NULL ); assert(!r);
179    r= pthread_rwlock_unlock( &rwl );
180    /* assert(r); *//* glibc doesn't complain.  It really ought to. Oh well. */
181 
182    /* We can infer the presence of wrapping for pthread_rwlock_rdlock,
183       pthread_rwlock_wrlock and pthread_rwlock_unlock by making
184       Thrcheck count the lockedness state, and warning when we unlock
185       a not-locked lock.  Thusly: */
186    r= pthread_rwlock_init( &rwl2, NULL ); assert(!r);
187 
188    /* w-lock it */
189    fprintf(stderr, "(1) no error on next line\n");
190    r= pthread_rwlock_wrlock( &rwl2 ); assert(!r);
191    /* unlock it */
192    fprintf(stderr, "(2) no error on next line\n");
193    r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
194    /* unlock it again, get an error */
195    fprintf(stderr, "(3)    ERROR on next line\n");
196    r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
197 
198    /* same game with r-locks */
199    r= pthread_rwlock_init( &rwl2, NULL ); assert(!r);
200    /* r-lock it twice */
201    fprintf(stderr, "(4) no error on next line\n");
202    r= pthread_rwlock_rdlock( &rwl2 ); assert(!r);
203    fprintf(stderr, "(5) no error on next line\n");
204    r= pthread_rwlock_rdlock( &rwl2 ); assert(!r);
205    /* unlock it twice */
206    fprintf(stderr, "(6) no error on next line\n");
207    r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
208    fprintf(stderr, "(7) no error on next line\n");
209    r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
210    /* unlock it again, get an error */
211    fprintf(stderr, "(8)    ERROR on next line\n");
212    r= pthread_rwlock_unlock( &rwl2 ); assert(!r);
213 
214    /* Lock rwl3 so the locked-lock-at-dealloc check can complain about
215       it. */
216    r= pthread_rwlock_init( &rwl3, NULL ); assert(!r);
217    r= pthread_rwlock_rdlock( &rwl3 ); assert(!r);
218 
219    /* ------------- sem_* ------------- */
220 
221    /* This is pretty lame, and duplicates tc18_semabuse.c. */
222 
223    fprintf(stderr,
224    "\n---------------- sem_* ----------------\n\n");
225 
226    /* verifies wrap of sem_init */
227    /* Do sem_init with huge initial count - fails */
228    r= sem_init(&s1, 0, ~0); assert(r);
229 
230    /* initialise properly */
231    r= sem_init(&s1, 0, 0);
232 
233    /* in glibc, sem_destroy is a no-op; making it fail is
234       impossible. */
235    fprintf(stderr, "\nFIXME: can't figure out how to verify wrap of "
236                    "sem_destroy\n\n");
237 
238    /* verifies wrap of sem_wait */
239    /* Do 'wait' on a bogus semaphore.  This should fail, but on glibc
240       it succeeds. */
241    memset(&s1, 0x55, sizeof(s1));
242    r= sem_wait(&s1); /* assert(r != 0); */
243 
244    /* this only fails with glibc 2.7 or later. */
245    r= sem_post(&s1);
246    fprintf(stderr, "\nFIXME: can't figure out how to verify wrap of "
247                    "sem_post\n\n");
248 
249    sem_destroy(&s1);
250 
251    /* ------------- dealloc of mem holding locks ------------- */
252 
253    fprintf(stderr,
254    "\n------------ dealloc of mem holding locks ------------\n\n");
255 
256    /* At this point it should complain about deallocation
257       of memory containing locked locks:
258          rwl3
259    */
260 
261    return 0;
262 }
263 
264 #else /* defined(__APPLE__) */
main(void)265 int main ( void )
266 {
267    fprintf(stderr, "This program does not work on Mac OS X.\n");
268    return 0;
269 }
270 #endif
271