sewardj | b411202 | 2007-11-09 22:49:28 +0000 | [diff] [blame^] | 1 | |
| 2 | /* This program attempts to verify that all functions that are |
| 3 | supposed to be wrapped by tc_intercepts.c really are wrapped. The |
| 4 | main way it does this is to cause failures in those functions, so |
| 5 | as to obtain various error messages which imply that the wrapper |
| 6 | really did engage. |
| 7 | |
| 8 | Any regressions shown up by this program are potentially serious |
| 9 | and should be investigated carefully. */ |
| 10 | |
| 11 | /* Needed for older glibcs (2.3 and older, at least) who don't |
| 12 | otherwise "know" about some more exotic pthread stuff, in this case |
| 13 | PTHREAD_MUTEX_ERRORCHECK. */ |
| 14 | #define _GNU_SOURCE 1 |
| 15 | |
| 16 | #include <stdio.h> |
| 17 | #include <string.h> |
| 18 | #include <assert.h> |
| 19 | #include <unistd.h> |
| 20 | #include <pthread.h> |
| 21 | #include <semaphore.h> |
| 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 | |
| 29 | void* lazy_child ( void* v ) { |
| 30 | assert(0); /* does not run */ |
| 31 | } |
| 32 | |
| 33 | void* racy_child ( void* v ) { |
| 34 | unprotected = 1234; |
| 35 | return NULL; |
| 36 | } |
| 37 | |
| 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 ); 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 really ought to fail, but it doesn't. */ |
| 245 | r= sem_post(&s1); assert(!r); |
| 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 | } |