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