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
27short unprotected = 0;
28
29void* lazy_child ( void* v ) {
30   assert(0); /* does not run */
31}
32
33void* racy_child ( void* v ) {
34   unprotected = 1234;
35   return NULL;
36}
37
38int 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 ); assert(r);
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__) */
265int main ( void )
266{
267   fprintf(stderr, "This program does not work on Mac OS X.\n");
268   return 0;
269}
270#endif
271