Poly1305-AEAD: updated implementation to match draft-irtf-cfrg-chacha20-poly1305-03
[libgcrypt.git] / tests / t-lock.c
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 \f
97 /* Wrapper functions to access Libgcrypt's internal test lock.  */
98 static void
99 external_lock_test_init (int line)
100 {
101   gpg_error_t err;
102
103   err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_INIT);
104   if (err)
105     fail ("init lock failed at %d: %s", line, gpg_strerror (err));
106 }
107
108 static void
109 external_lock_test_lock (int line)
110 {
111   gpg_error_t err;
112
113   err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_LOCK);
114   if (err)
115     fail ("taking lock failed at %d: %s", line, gpg_strerror (err));
116 }
117
118 static void
119 external_lock_test_unlock (int line)
120 {
121   gpg_error_t err;
122
123   err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_UNLOCK);
124   if (err)
125     fail ("releasing lock failed at %d: %s", line, gpg_strerror (err));
126
127 }
128
129 static void
130 external_lock_test_destroy (int line)
131 {
132   gpg_error_t err;
133
134   err = gcry_control (PRIV_CTL_EXTERNAL_LOCK_TEST, EXTERNAL_LOCK_TEST_DESTROY);
135   if (err)
136     fail ("destroying lock failed at %d: %s", line, gpg_strerror (err));
137 }
138
139
140
141 \f
142 /* The nonce thread.  We simply request a couple of nonces and
143    return.  */
144 static THREAD_RET_TYPE
145 nonce_thread (void *argarg)
146 {
147   struct thread_arg_s *arg = argarg;
148   int i;
149   char nonce[NONCE_SIZE];
150
151   for (i = 0; i < N_NONCE_ITERATIONS; i++)
152     {
153       gcry_create_nonce (nonce, sizeof nonce);
154       if (i && !(i%100))
155         info ("thread %d created %d nonces so far", arg->no, i);
156     }
157
158   gcry_free (arg);
159   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 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   for (i=0; i < N_NONCE_THREADS; i++)
201     {
202       arg = gcry_xmalloc (sizeof *arg);
203       arg->no = i;
204       pthread_create (&threads[i], NULL, nonce_thread, arg);
205     }
206
207   for (i=0; i < N_NONCE_THREADS; i++)
208     {
209       rc = pthread_join (threads[i], NULL);
210       if (rc)
211         fail ("pthread_join failed for nonce thread %d: %s",
212               i, strerror (errno));
213       else
214         info ("nonce thread %d has terminated", i);
215     }
216
217 #endif /*!_WIN32*/
218 }
219
220
221 /* Initialze all accounts.  */
222 static void
223 init_accounts (void)
224 {
225   int i;
226
227   for (i=0; i < N_ACCOUNT; i++)
228     account[i] = ACCOUNT_VALUE;
229 }
230
231
232 /* Check that the sum of all accounts matches the intial sum.  */
233 static void
234 check_accounts (void)
235 {
236   int i, sum;
237
238   sum = 0;
239   for (i = 0; i < N_ACCOUNT; i++)
240     sum += account[i];
241   if (sum != N_ACCOUNT * ACCOUNT_VALUE)
242     die ("accounts out of balance");
243 }
244
245
246 static void
247 print_accounts (void)
248 {
249   int i;
250
251   for (i=0; i < N_ACCOUNT; i++)
252     printf ("account %d: %6d\n", i, account[i]);
253 }
254
255
256 /* Get a a random integer value in the range 0 to HIGH.  */
257 static unsigned int
258 get_rand (int high)
259 {
260   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 pick_account (void)
268 {
269   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 pick_value (void)
276 {
277   return get_rand (MAX_TRANSACTION_VALUE);
278 }
279
280
281 /* This is the revision department.  */
282 static THREAD_RET_TYPE
283 revision_thread (void *arg)
284 {
285   (void)arg;
286
287   while (!stop_revision_thread)
288     {
289       external_lock_test_lock (__LINE__);
290       check_accounts ();
291       external_lock_test_unlock (__LINE__);
292     }
293   return THREAD_RET_VALUE;
294 }
295
296
297 /* This is one of our accountants.  */
298 static THREAD_RET_TYPE
299 accountant_thread (void *arg)
300 {
301   int i;
302   int acc1, acc2;
303   int value;
304
305   (void)arg;
306
307   for (i = 0; i < N_TRANSACTIONS; i++)
308     {
309       external_lock_test_lock (__LINE__);
310       acc1 = pick_account ();
311       acc2 = pick_account ();
312       value = pick_value ();
313       account[acc1] += value;
314       account[acc2] -= value;
315       external_lock_test_unlock (__LINE__);
316     }
317   return THREAD_RET_VALUE;
318 }
319
320
321 static void
322 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   external_lock_test_init (__LINE__);
369   stop_revision_thread = 0;
370   pthread_create (&rthread, NULL, revision_thread, NULL);
371
372   for (i=0; i < N_ACCOUNTANTS; i++)
373     pthread_create (&athreads[i], NULL, accountant_thread, NULL);
374
375   for (i=0; i < N_ACCOUNTANTS; i++)
376     {
377       rc = pthread_join (athreads[i], NULL);
378       if (rc)
379         fail ("pthread_join failed for accountant thread %d: %s",
380               i, strerror (errno));
381       else
382         info ("accountant thread %d has terminated", i);
383     }
384
385   stop_revision_thread = 1;
386   rc = pthread_join (rthread, NULL);
387   if (rc)
388     fail ("pthread_join failed for the revision thread: %s", strerror (errno));
389   else
390     info ("revision thread has terminated");
391
392 #endif /*!_WIN32*/
393
394   external_lock_test_destroy (__LINE__);
395 }
396
397
398
399 int
400 main (int argc, char **argv)
401 {
402   int last_argc = -1;
403
404   if (argc)
405     {
406       argc--; argv++;
407     }
408   while (argc && last_argc != argc )
409     {
410       last_argc = argc;
411       if (!strcmp (*argv, "--help"))
412         {
413           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           exit (0);
421         }
422       if (!strcmp (*argv, "--verbose"))
423         {
424           verbose = 1;
425           argc--; argv++;
426         }
427       else if (!strcmp (*argv, "--debug"))
428         {
429           verbose = debug = 1;
430           argc--; argv++;
431         }
432     }
433
434   srand (time(NULL)*getpid());
435
436   if (debug)
437     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
438   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
439   if (!gcry_check_version (GCRYPT_VERSION))
440     die ("version mismatch");
441   /* We are using non-public interfaces - check the exact version.  */
442   if (strcmp (gcry_check_version (NULL), GCRYPT_VERSION))
443     die ("exact version match failed");
444   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
445   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
446
447   check_nonce_lock ();
448
449   init_accounts ();
450   check_accounts ();
451
452   run_test ();
453   check_accounts ();
454
455   /* Run a second time to check deinit code.  */
456   run_test ();
457   check_accounts ();
458
459   if (verbose)
460     print_accounts ();
461
462   return errorcount ? 1 : 0;
463 }