Line data Source code
1 : /* t-lock.c - Check the lock functions
2 : * Copyright (C) 2014 g10 Code GmbH
3 : *
4 : * This file is part of Libgcrypt.
5 : *
6 : * Libgcrypt is free software; you can redistribute it and/or
7 : * modify it under the terms of the GNU Lesser General Public License
8 : * as published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * Libgcrypt is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #if HAVE_CONFIG_H
21 : # include <config.h>
22 : #endif
23 :
24 : #include <stdio.h>
25 : #include <stdlib.h>
26 : #include <string.h>
27 : #include <assert.h>
28 : #include <errno.h>
29 : #include <unistd.h>
30 : #if HAVE_PTHREAD
31 : # include <pthread.h>
32 : #endif
33 :
34 : #define PGMNAME "t-lock"
35 :
36 : #include "t-common.h"
37 :
38 : /* Mingw requires us to include windows.h after winsock2.h which is
39 : included by gcrypt.h. */
40 : #ifdef _WIN32
41 : # include <windows.h>
42 : #endif
43 :
44 : #ifdef _WIN32
45 : # define THREAD_RET_TYPE DWORD WINAPI
46 : # define THREAD_RET_VALUE 0
47 : #else
48 : # define THREAD_RET_TYPE void *
49 : # define THREAD_RET_VALUE NULL
50 : #endif
51 :
52 : #define PRIV_CTL_EXTERNAL_LOCK_TEST 61
53 : #define EXTERNAL_LOCK_TEST_INIT 30111
54 : #define EXTERNAL_LOCK_TEST_LOCK 30112
55 : #define EXTERNAL_LOCK_TEST_UNLOCK 30113
56 : #define EXTERNAL_LOCK_TEST_DESTROY 30114
57 :
58 :
59 : /* Number of threads to run. */
60 : #define N_NONCE_THREADS 8
61 : /* Number of interations. */
62 : #define N_NONCE_ITERATIONS 1000
63 : /* Requested nonce size. */
64 : #define NONCE_SIZE 11
65 :
66 :
67 : /* This tests works by having a a couple of accountant threads which do
68 : random transactions between accounts and a revision threads which
69 : checks that the balance of all accounts is invariant. The idea for
70 : this check is due to Bruno Haible. */
71 : #define N_ACCOUNT 8
72 : #define ACCOUNT_VALUE 42
73 : static int account[N_ACCOUNT];
74 :
75 : /* Number of transactions done by each accountant. */
76 : #define N_TRANSACTIONS 1000
77 :
78 : /* Number of accountants to run. */
79 : #define N_ACCOUNTANTS 5
80 :
81 : /* Maximum transaction value. A quite low value is used so that we
82 : would get an integer overflow. */
83 : #define MAX_TRANSACTION_VALUE 50
84 :
85 : /* Flag to tell the revision thread to finish. */
86 : static volatile int stop_revision_thread;
87 :
88 :
89 : struct thread_arg_s
90 : {
91 : int no;
92 : };
93 :
94 :
95 :
96 :
97 : /* Wrapper functions to access Libgcrypt's internal test lock. */
98 : static void
99 2 : external_lock_test_init (int line)
100 : {
101 : gpg_error_t err;
102 :
103 2 : err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_INIT);
104 2 : if (err)
105 0 : fail ("init lock failed at %d: %s", line, gpg_strerror (err));
106 2 : }
107 :
108 : static void
109 12334 : external_lock_test_lock (int line)
110 : {
111 : gpg_error_t err;
112 :
113 12334 : err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_LOCK);
114 12440 : if (err)
115 0 : fail ("taking lock failed at %d: %s", line, gpg_strerror (err));
116 12440 : }
117 :
118 : static void
119 12440 : external_lock_test_unlock (int line)
120 : {
121 : gpg_error_t err;
122 :
123 12440 : err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_UNLOCK);
124 12340 : if (err)
125 0 : fail ("releasing lock failed at %d: %s", line, gpg_strerror (err));
126 :
127 12340 : }
128 :
129 : static void
130 2 : external_lock_test_destroy (int line)
131 : {
132 : gpg_error_t err;
133 :
134 2 : err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_DESTROY);
135 2 : if (err)
136 0 : fail ("destroying lock failed at %d: %s", line, gpg_strerror (err));
137 2 : }
138 :
139 :
140 :
141 :
142 : /* The nonce thread. We simply request a couple of nonces and
143 : return. */
144 : static THREAD_RET_TYPE
145 8 : nonce_thread (void *argarg)
146 : {
147 8 : struct thread_arg_s *arg = argarg;
148 : int i;
149 : char nonce[NONCE_SIZE];
150 :
151 8008 : for (i = 0; i < N_NONCE_ITERATIONS; i++)
152 : {
153 8000 : gcry_create_nonce (nonce, sizeof nonce);
154 8000 : if (i && !(i%100))
155 72 : info ("thread %d created %d nonces so far", arg->no, i);
156 : }
157 :
158 8 : gcry_free (arg);
159 8 : return THREAD_RET_VALUE;
160 : }
161 :
162 :
163 : /* To check our locking function we run several threads all accessing
164 : the nonce functions. If this function returns we know that there
165 : are no obvious deadlocks or failed lock initialization. */
166 : static void
167 1 : check_nonce_lock (void)
168 : {
169 : struct thread_arg_s *arg;
170 : #ifdef _WIN32
171 : HANDLE threads[N_NONCE_THREADS];
172 : int i;
173 : int rc;
174 :
175 : for (i=0; i < N_NONCE_THREADS; i++)
176 : {
177 : arg = gcry_xmalloc (sizeof *arg);
178 : arg->no = i;
179 : threads[i] = CreateThread (NULL, 0, nonce_thread, arg, 0, NULL);
180 : if (!threads[i])
181 : die ("error creating nonce thread %d: rc=%d",
182 : i, (int)GetLastError ());
183 : }
184 :
185 : for (i=0; i < N_NONCE_THREADS; i++)
186 : {
187 : rc = WaitForSingleObject (threads[i], INFINITE);
188 : if (rc == WAIT_OBJECT_0)
189 : info ("nonce thread %d has terminated", i);
190 : else
191 : fail ("waiting for nonce thread %d failed: %d",
192 : i, (int)GetLastError ());
193 : CloseHandle (threads[i]);
194 : }
195 :
196 : #elif HAVE_PTHREAD
197 : pthread_t threads[N_NONCE_THREADS];
198 : int rc, i;
199 :
200 9 : for (i=0; i < N_NONCE_THREADS; i++)
201 : {
202 8 : arg = gcry_xmalloc (sizeof *arg);
203 8 : arg->no = i;
204 8 : pthread_create (&threads[i], NULL, nonce_thread, arg);
205 : }
206 :
207 9 : for (i=0; i < N_NONCE_THREADS; i++)
208 : {
209 8 : rc = pthread_join (threads[i], NULL);
210 8 : if (rc)
211 0 : fail ("pthread_join failed for nonce thread %d: %s",
212 0 : i, strerror (errno));
213 : else
214 8 : info ("nonce thread %d has terminated", i);
215 : }
216 :
217 : #endif /*!_WIN32*/
218 1 : }
219 :
220 :
221 : /* Initialze all accounts. */
222 : static void
223 1 : init_accounts (void)
224 : {
225 : int i;
226 :
227 9 : for (i=0; i < N_ACCOUNT; i++)
228 8 : account[i] = ACCOUNT_VALUE;
229 1 : }
230 :
231 :
232 : /* Check that the sum of all accounts matches the intial sum. */
233 : static void
234 2443 : check_accounts (void)
235 : {
236 : int i, sum;
237 :
238 2443 : sum = 0;
239 21987 : for (i = 0; i < N_ACCOUNT; i++)
240 19544 : sum += account[i];
241 2443 : if (sum != N_ACCOUNT * ACCOUNT_VALUE)
242 0 : die ("accounts out of balance");
243 2443 : }
244 :
245 :
246 : static void
247 0 : print_accounts (void)
248 : {
249 : int i;
250 :
251 0 : for (i=0; i < N_ACCOUNT; i++)
252 0 : printf ("account %d: %6d\n", i, account[i]);
253 0 : }
254 :
255 :
256 : /* Get a a random integer value in the range 0 to HIGH. */
257 : static unsigned int
258 30000 : get_rand (int high)
259 : {
260 30000 : return (unsigned int)(1+(int)((double)(high+1)*rand ()/(RAND_MAX+1.0))) - 1;
261 : }
262 :
263 :
264 : /* Pick a random account. Note that this fucntion is not
265 : thread-safe. */
266 : static int
267 20000 : pick_account (void)
268 : {
269 20000 : return get_rand (N_ACCOUNT - 1);
270 : }
271 :
272 :
273 : /* Pick a random value for a transaction. This is not thread-safe. */
274 : static int
275 10000 : pick_value (void)
276 : {
277 10000 : return get_rand (MAX_TRANSACTION_VALUE);
278 : }
279 :
280 :
281 : /* This is the revision department. */
282 : static THREAD_RET_TYPE
283 2 : revision_thread (void *arg)
284 : {
285 : (void)arg;
286 :
287 2444 : while (!stop_revision_thread)
288 : {
289 2440 : external_lock_test_lock (__LINE__);
290 2440 : check_accounts ();
291 2440 : external_lock_test_unlock (__LINE__);
292 : }
293 2 : return THREAD_RET_VALUE;
294 : }
295 :
296 :
297 : /* This is one of our accountants. */
298 : static THREAD_RET_TYPE
299 12 : accountant_thread (void *arg)
300 : {
301 : int i;
302 : int acc1, acc2;
303 : int value;
304 :
305 : (void)arg;
306 :
307 9941 : for (i = 0; i < N_TRANSACTIONS; i++)
308 : {
309 9931 : external_lock_test_lock (__LINE__);
310 10000 : acc1 = pick_account ();
311 10000 : acc2 = pick_account ();
312 10000 : value = pick_value ();
313 10000 : account[acc1] += value;
314 10000 : account[acc2] -= value;
315 10000 : external_lock_test_unlock (__LINE__);
316 : }
317 10 : return THREAD_RET_VALUE;
318 : }
319 :
320 :
321 : static void
322 2 : run_test (void)
323 : {
324 : #ifdef _WIN32
325 : HANDLE rthread;
326 : HANDLE athreads[N_ACCOUNTANTS];
327 : int i;
328 : int rc;
329 :
330 : external_lock_test_init (__LINE__);
331 : stop_revision_thread = 0;
332 : rthread = CreateThread (NULL, 0, revision_thread, NULL, 0, NULL);
333 : if (!rthread)
334 : die ("error creating revision thread: rc=%d", (int)GetLastError ());
335 :
336 : for (i=0; i < N_ACCOUNTANTS; i++)
337 : {
338 : athreads[i] = CreateThread (NULL, 0, accountant_thread, NULL, 0, NULL);
339 : if (!athreads[i])
340 : die ("error creating accountant thread %d: rc=%d",
341 : i, (int)GetLastError ());
342 : }
343 :
344 : for (i=0; i < N_ACCOUNTANTS; i++)
345 : {
346 : rc = WaitForSingleObject (athreads[i], INFINITE);
347 : if (rc == WAIT_OBJECT_0)
348 : info ("accountant thread %d has terminated", i);
349 : else
350 : fail ("waiting for accountant thread %d failed: %d",
351 : i, (int)GetLastError ());
352 : CloseHandle (athreads[i]);
353 : }
354 : stop_revision_thread = 1;
355 :
356 : rc = WaitForSingleObject (rthread, INFINITE);
357 : if (rc == WAIT_OBJECT_0)
358 : info ("revision thread has terminated");
359 : else
360 : fail ("waiting for revision thread failed: %d", (int)GetLastError ());
361 : CloseHandle (rthread);
362 :
363 : #else /*!_WIN32*/
364 : pthread_t rthread;
365 : pthread_t athreads[N_ACCOUNTANTS];
366 : int rc, i;
367 :
368 2 : external_lock_test_init (__LINE__);
369 2 : stop_revision_thread = 0;
370 2 : pthread_create (&rthread, NULL, revision_thread, NULL);
371 :
372 12 : for (i=0; i < N_ACCOUNTANTS; i++)
373 10 : pthread_create (&athreads[i], NULL, accountant_thread, NULL);
374 :
375 12 : for (i=0; i < N_ACCOUNTANTS; i++)
376 : {
377 10 : rc = pthread_join (athreads[i], NULL);
378 10 : if (rc)
379 0 : fail ("pthread_join failed for accountant thread %d: %s",
380 0 : i, strerror (errno));
381 : else
382 10 : info ("accountant thread %d has terminated", i);
383 : }
384 :
385 2 : stop_revision_thread = 1;
386 2 : rc = pthread_join (rthread, NULL);
387 2 : if (rc)
388 0 : fail ("pthread_join failed for the revision thread: %s", strerror (errno));
389 : else
390 2 : info ("revision thread has terminated");
391 :
392 : #endif /*!_WIN32*/
393 :
394 2 : external_lock_test_destroy (__LINE__);
395 2 : }
396 :
397 :
398 :
399 : int
400 1 : main (int argc, char **argv)
401 : {
402 1 : int last_argc = -1;
403 :
404 1 : if (argc)
405 : {
406 1 : argc--; argv++;
407 : }
408 2 : while (argc && last_argc != argc )
409 : {
410 0 : last_argc = argc;
411 0 : if (!strcmp (*argv, "--help"))
412 : {
413 0 : puts (
414 : "usage: ./t-lock [options]\n"
415 : "\n"
416 : "Options:\n"
417 : " --verbose Show what is going on\n"
418 : " --debug Flyswatter\n"
419 : );
420 0 : exit (0);
421 : }
422 0 : if (!strcmp (*argv, "--verbose"))
423 : {
424 0 : verbose = 1;
425 0 : argc--; argv++;
426 : }
427 0 : else if (!strcmp (*argv, "--debug"))
428 : {
429 0 : verbose = debug = 1;
430 0 : argc--; argv++;
431 : }
432 : }
433 :
434 1 : srand (time(NULL)*getpid());
435 :
436 1 : if (debug)
437 0 : gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
438 1 : gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
439 1 : if (!gcry_check_version (GCRYPT_VERSION))
440 0 : die ("version mismatch");
441 : /* We are using non-public interfaces - check the exact version. */
442 1 : if (strcmp (gcry_check_version (NULL), GCRYPT_VERSION))
443 0 : die ("exact version match failed");
444 1 : gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
445 1 : gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
446 :
447 1 : check_nonce_lock ();
448 :
449 1 : init_accounts ();
450 1 : check_accounts ();
451 :
452 1 : run_test ();
453 1 : check_accounts ();
454 :
455 : /* Run a second time to check deinit code. */
456 1 : run_test ();
457 1 : check_accounts ();
458 :
459 1 : if (verbose)
460 0 : print_accounts ();
461 :
462 1 : return errorcount ? 1 : 0;
463 : }
|