VirtualBox

source: vbox/trunk/src/libs/openssl-3.0.7/test/threadstest.c@ 97673

最後變更 在這個檔案從97673是 97372,由 vboxsync 提交於 2 年 前

libs: Switch to openssl-3.0.7, bugref:10317

檔案大小: 19.8 KB
 
1/*
2 * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10/* test_multi below tests the thread safety of a deprecated function */
11#define OPENSSL_SUPPRESS_DEPRECATED
12
13#if defined(_WIN32)
14# include <windows.h>
15#endif
16
17#include <string.h>
18#include <openssl/crypto.h>
19#include <openssl/rsa.h>
20#include <openssl/aes.h>
21#include <openssl/rsa.h>
22#include "testutil.h"
23#include "threadstest.h"
24
25/* Limit the maximum number of threads */
26#define MAXIMUM_THREADS 10
27
28/* Limit the maximum number of providers loaded into a library context */
29#define MAXIMUM_PROVIDERS 4
30
31static int do_fips = 0;
32static char *privkey;
33static char *config_file = NULL;
34static int multidefault_run = 0;
35static const char *default_provider[] = { "default", NULL };
36
37static int test_lock(void)
38{
39 CRYPTO_RWLOCK *lock = CRYPTO_THREAD_lock_new();
40 int res;
41
42 res = TEST_true(CRYPTO_THREAD_read_lock(lock))
43 && TEST_true(CRYPTO_THREAD_unlock(lock))
44 && TEST_true(CRYPTO_THREAD_write_lock(lock))
45 && TEST_true(CRYPTO_THREAD_unlock(lock));
46
47 CRYPTO_THREAD_lock_free(lock);
48
49 return res;
50}
51
52static CRYPTO_ONCE once_run = CRYPTO_ONCE_STATIC_INIT;
53static unsigned once_run_count = 0;
54
55static void once_do_run(void)
56{
57 once_run_count++;
58}
59
60static void once_run_thread_cb(void)
61{
62 CRYPTO_THREAD_run_once(&once_run, once_do_run);
63}
64
65static int test_once(void)
66{
67 thread_t thread;
68
69 if (!TEST_true(run_thread(&thread, once_run_thread_cb))
70 || !TEST_true(wait_for_thread(thread))
71 || !CRYPTO_THREAD_run_once(&once_run, once_do_run)
72 || !TEST_int_eq(once_run_count, 1))
73 return 0;
74 return 1;
75}
76
77static CRYPTO_THREAD_LOCAL thread_local_key;
78static unsigned destructor_run_count = 0;
79static int thread_local_thread_cb_ok = 0;
80
81static void thread_local_destructor(void *arg)
82{
83 unsigned *count;
84
85 if (arg == NULL)
86 return;
87
88 count = arg;
89
90 (*count)++;
91}
92
93static void thread_local_thread_cb(void)
94{
95 void *ptr;
96
97 ptr = CRYPTO_THREAD_get_local(&thread_local_key);
98 if (!TEST_ptr_null(ptr)
99 || !TEST_true(CRYPTO_THREAD_set_local(&thread_local_key,
100 &destructor_run_count)))
101 return;
102
103 ptr = CRYPTO_THREAD_get_local(&thread_local_key);
104 if (!TEST_ptr_eq(ptr, &destructor_run_count))
105 return;
106
107 thread_local_thread_cb_ok = 1;
108}
109
110static int test_thread_local(void)
111{
112 thread_t thread;
113 void *ptr = NULL;
114
115 if (!TEST_true(CRYPTO_THREAD_init_local(&thread_local_key,
116 thread_local_destructor)))
117 return 0;
118
119 ptr = CRYPTO_THREAD_get_local(&thread_local_key);
120 if (!TEST_ptr_null(ptr)
121 || !TEST_true(run_thread(&thread, thread_local_thread_cb))
122 || !TEST_true(wait_for_thread(thread))
123 || !TEST_int_eq(thread_local_thread_cb_ok, 1))
124 return 0;
125
126#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
127
128 ptr = CRYPTO_THREAD_get_local(&thread_local_key);
129 if (!TEST_ptr_null(ptr))
130 return 0;
131
132# if !defined(OPENSSL_SYS_WINDOWS)
133 if (!TEST_int_eq(destructor_run_count, 1))
134 return 0;
135# endif
136#endif
137
138 if (!TEST_true(CRYPTO_THREAD_cleanup_local(&thread_local_key)))
139 return 0;
140 return 1;
141}
142
143static int test_atomic(void)
144{
145 int val = 0, ret = 0, testresult = 0;
146 uint64_t val64 = 1, ret64 = 0;
147 CRYPTO_RWLOCK *lock = CRYPTO_THREAD_lock_new();
148
149 if (!TEST_ptr(lock))
150 return 0;
151
152 if (CRYPTO_atomic_add(&val, 1, &ret, NULL)) {
153 /* This succeeds therefore we're on a platform with lockless atomics */
154 if (!TEST_int_eq(val, 1) || !TEST_int_eq(val, ret))
155 goto err;
156 } else {
157 /* This failed therefore we're on a platform without lockless atomics */
158 if (!TEST_int_eq(val, 0) || !TEST_int_eq(val, ret))
159 goto err;
160 }
161 val = 0;
162 ret = 0;
163
164 if (!TEST_true(CRYPTO_atomic_add(&val, 1, &ret, lock)))
165 goto err;
166 if (!TEST_int_eq(val, 1) || !TEST_int_eq(val, ret))
167 goto err;
168
169 if (CRYPTO_atomic_or(&val64, 2, &ret64, NULL)) {
170 /* This succeeds therefore we're on a platform with lockless atomics */
171 if (!TEST_uint_eq((unsigned int)val64, 3)
172 || !TEST_uint_eq((unsigned int)val64, (unsigned int)ret64))
173 goto err;
174 } else {
175 /* This failed therefore we're on a platform without lockless atomics */
176 if (!TEST_uint_eq((unsigned int)val64, 1)
177 || !TEST_int_eq((unsigned int)ret64, 0))
178 goto err;
179 }
180 val64 = 1;
181 ret64 = 0;
182
183 if (!TEST_true(CRYPTO_atomic_or(&val64, 2, &ret64, lock)))
184 goto err;
185
186 if (!TEST_uint_eq((unsigned int)val64, 3)
187 || !TEST_uint_eq((unsigned int)val64, (unsigned int)ret64))
188 goto err;
189
190 ret64 = 0;
191 if (CRYPTO_atomic_load(&val64, &ret64, NULL)) {
192 /* This succeeds therefore we're on a platform with lockless atomics */
193 if (!TEST_uint_eq((unsigned int)val64, 3)
194 || !TEST_uint_eq((unsigned int)val64, (unsigned int)ret64))
195 goto err;
196 } else {
197 /* This failed therefore we're on a platform without lockless atomics */
198 if (!TEST_uint_eq((unsigned int)val64, 3)
199 || !TEST_int_eq((unsigned int)ret64, 0))
200 goto err;
201 }
202
203 ret64 = 0;
204 if (!TEST_true(CRYPTO_atomic_load(&val64, &ret64, lock)))
205 goto err;
206
207 if (!TEST_uint_eq((unsigned int)val64, 3)
208 || !TEST_uint_eq((unsigned int)val64, (unsigned int)ret64))
209 goto err;
210
211 testresult = 1;
212 err:
213 CRYPTO_THREAD_lock_free(lock);
214 return testresult;
215}
216
217static OSSL_LIB_CTX *multi_libctx = NULL;
218static int multi_success;
219static OSSL_PROVIDER *multi_provider[MAXIMUM_PROVIDERS + 1];
220static size_t multi_num_threads;
221static thread_t multi_threads[MAXIMUM_THREADS];
222
223static void multi_intialise(void)
224{
225 multi_success = 1;
226 multi_libctx = NULL;
227 multi_num_threads = 0;
228 memset(multi_threads, 0, sizeof(multi_threads));
229 memset(multi_provider, 0, sizeof(multi_provider));
230}
231
232static void thead_teardown_libctx(void)
233{
234 OSSL_PROVIDER **p;
235
236 for (p = multi_provider; *p != NULL; p++)
237 OSSL_PROVIDER_unload(*p);
238 OSSL_LIB_CTX_free(multi_libctx);
239 multi_intialise();
240}
241
242static int thread_setup_libctx(int libctx, const char *providers[])
243{
244 size_t n;
245
246 if (libctx && !TEST_true(test_get_libctx(&multi_libctx, NULL, config_file,
247 NULL, NULL)))
248 return 0;
249
250 if (providers != NULL)
251 for (n = 0; providers[n] != NULL; n++)
252 if (!TEST_size_t_lt(n, MAXIMUM_PROVIDERS)
253 || !TEST_ptr(multi_provider[n] = OSSL_PROVIDER_load(multi_libctx,
254 providers[n]))) {
255 thead_teardown_libctx();
256 return 0;
257 }
258 return 1;
259}
260
261static int teardown_threads(void)
262{
263 size_t i;
264
265 for (i = 0; i < multi_num_threads; i++)
266 if (!TEST_true(wait_for_thread(multi_threads[i])))
267 return 0;
268 return 1;
269}
270
271static int start_threads(size_t n, void (*thread_func)(void))
272{
273 size_t i;
274
275 if (!TEST_size_t_le(multi_num_threads + n, MAXIMUM_THREADS))
276 return 0;
277
278 for (i = 0 ; i < n; i++)
279 if (!TEST_true(run_thread(multi_threads + multi_num_threads++, thread_func)))
280 return 0;
281 return 1;
282}
283
284/* Template multi-threaded test function */
285static int thread_run_test(void (*main_func)(void),
286 size_t num_threads, void (*thread_func)(void),
287 int libctx, const char *providers[])
288{
289 int testresult = 0;
290
291 multi_intialise();
292 if (!thread_setup_libctx(libctx, providers)
293 || !start_threads(num_threads, thread_func))
294 goto err;
295
296 if (main_func != NULL)
297 main_func();
298
299 if (!teardown_threads()
300 || !TEST_true(multi_success))
301 goto err;
302 testresult = 1;
303 err:
304 thead_teardown_libctx();
305 return testresult;
306}
307
308static void thread_general_worker(void)
309{
310 EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
311 EVP_MD *md = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL);
312 EVP_CIPHER_CTX *cipherctx = EVP_CIPHER_CTX_new();
313 EVP_CIPHER *ciph = EVP_CIPHER_fetch(multi_libctx, "AES-128-CBC", NULL);
314 const char *message = "Hello World";
315 size_t messlen = strlen(message);
316 /* Should be big enough for encryption output too */
317 unsigned char out[EVP_MAX_MD_SIZE];
318 const unsigned char key[AES_BLOCK_SIZE] = {
319 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
320 0x0c, 0x0d, 0x0e, 0x0f
321 };
322 const unsigned char iv[AES_BLOCK_SIZE] = {
323 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
324 0x0c, 0x0d, 0x0e, 0x0f
325 };
326 unsigned int mdoutl;
327 int ciphoutl;
328 EVP_PKEY *pkey = NULL;
329 int testresult = 0;
330 int i, isfips;
331
332 isfips = OSSL_PROVIDER_available(multi_libctx, "fips");
333
334 if (!TEST_ptr(mdctx)
335 || !TEST_ptr(md)
336 || !TEST_ptr(cipherctx)
337 || !TEST_ptr(ciph))
338 goto err;
339
340 /* Do some work */
341 for (i = 0; i < 5; i++) {
342 if (!TEST_true(EVP_DigestInit_ex(mdctx, md, NULL))
343 || !TEST_true(EVP_DigestUpdate(mdctx, message, messlen))
344 || !TEST_true(EVP_DigestFinal(mdctx, out, &mdoutl)))
345 goto err;
346 }
347 for (i = 0; i < 5; i++) {
348 if (!TEST_true(EVP_EncryptInit_ex(cipherctx, ciph, NULL, key, iv))
349 || !TEST_true(EVP_EncryptUpdate(cipherctx, out, &ciphoutl,
350 (unsigned char *)message,
351 messlen))
352 || !TEST_true(EVP_EncryptFinal(cipherctx, out, &ciphoutl)))
353 goto err;
354 }
355
356 /*
357 * We want the test to run quickly - not securely.
358 * Therefore we use an insecure bit length where we can (512).
359 * In the FIPS module though we must use a longer length.
360 */
361 pkey = EVP_PKEY_Q_keygen(multi_libctx, NULL, "RSA", isfips ? 2048 : 512);
362 if (!TEST_ptr(pkey))
363 goto err;
364
365 testresult = 1;
366 err:
367 EVP_MD_CTX_free(mdctx);
368 EVP_MD_free(md);
369 EVP_CIPHER_CTX_free(cipherctx);
370 EVP_CIPHER_free(ciph);
371 EVP_PKEY_free(pkey);
372 if (!testresult)
373 multi_success = 0;
374}
375
376static void thread_multi_simple_fetch(void)
377{
378 EVP_MD *md = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL);
379
380 if (md != NULL)
381 EVP_MD_free(md);
382 else
383 multi_success = 0;
384}
385
386static EVP_PKEY *shared_evp_pkey = NULL;
387
388static void thread_shared_evp_pkey(void)
389{
390 char *msg = "Hello World";
391 unsigned char ctbuf[256];
392 unsigned char ptbuf[256];
393 size_t ptlen, ctlen = sizeof(ctbuf);
394 EVP_PKEY_CTX *ctx = NULL;
395 int success = 0;
396 int i;
397
398 for (i = 0; i < 1 + do_fips; i++) {
399 if (i > 0)
400 EVP_PKEY_CTX_free(ctx);
401 ctx = EVP_PKEY_CTX_new_from_pkey(multi_libctx, shared_evp_pkey,
402 i == 0 ? "provider=default"
403 : "provider=fips");
404 if (!TEST_ptr(ctx))
405 goto err;
406
407 if (!TEST_int_ge(EVP_PKEY_encrypt_init(ctx), 0)
408 || !TEST_int_ge(EVP_PKEY_encrypt(ctx, ctbuf, &ctlen,
409 (unsigned char *)msg, strlen(msg)),
410 0))
411 goto err;
412
413 EVP_PKEY_CTX_free(ctx);
414 ctx = EVP_PKEY_CTX_new_from_pkey(multi_libctx, shared_evp_pkey, NULL);
415
416 if (!TEST_ptr(ctx))
417 goto err;
418
419 ptlen = sizeof(ptbuf);
420 if (!TEST_int_ge(EVP_PKEY_decrypt_init(ctx), 0)
421 || !TEST_int_gt(EVP_PKEY_decrypt(ctx, ptbuf, &ptlen, ctbuf, ctlen),
422 0)
423 || !TEST_mem_eq(msg, strlen(msg), ptbuf, ptlen))
424 goto err;
425 }
426
427 success = 1;
428
429 err:
430 EVP_PKEY_CTX_free(ctx);
431 if (!success)
432 multi_success = 0;
433}
434
435static void thread_downgrade_shared_evp_pkey(void)
436{
437#ifndef OPENSSL_NO_DEPRECATED_3_0
438 /*
439 * This test is only relevant for deprecated functions that perform
440 * downgrading
441 */
442 if (EVP_PKEY_get0_RSA(shared_evp_pkey) == NULL)
443 multi_success = 0;
444#else
445 /* Shouldn't ever get here */
446 multi_success = 0;
447#endif
448}
449
450static void thread_provider_load_unload(void)
451{
452 OSSL_PROVIDER *deflt = OSSL_PROVIDER_load(multi_libctx, "default");
453
454 if (!TEST_ptr(deflt)
455 || !TEST_true(OSSL_PROVIDER_available(multi_libctx, "default")))
456 multi_success = 0;
457
458 OSSL_PROVIDER_unload(deflt);
459}
460
461/*
462 * Do work in multiple worker threads at the same time.
463 * Test 0: General worker, using the default provider
464 * Test 1: General worker, using the fips provider
465 * Test 2: Simple fetch worker
466 * Test 3: Worker downgrading a shared EVP_PKEY
467 * Test 4: Worker using a shared EVP_PKEY
468 * Test 5: Worker loading and unloading a provider
469 */
470static int test_multi(int idx)
471{
472 thread_t thread1, thread2;
473 int testresult = 0;
474 OSSL_PROVIDER *prov = NULL, *prov2 = NULL;
475 void (*worker)(void) = NULL;
476 void (*worker2)(void) = NULL;
477 EVP_MD *sha256 = NULL;
478
479 if (idx == 1 && !do_fips)
480 return TEST_skip("FIPS not supported");
481
482#ifdef OPENSSL_NO_DEPRECATED_3_0
483 if (idx == 3)
484 return TEST_skip("Skipping tests for deprected functions");
485#endif
486
487 multi_success = 1;
488 if (!TEST_true(test_get_libctx(&multi_libctx, NULL, config_file,
489 NULL, NULL)))
490 return 0;
491
492 prov = OSSL_PROVIDER_load(multi_libctx, (idx == 1) ? "fips" : "default");
493 if (!TEST_ptr(prov))
494 goto err;
495
496 switch (idx) {
497 case 0:
498 case 1:
499 worker = thread_general_worker;
500 break;
501 case 2:
502 worker = thread_multi_simple_fetch;
503 break;
504 case 3:
505 worker2 = thread_downgrade_shared_evp_pkey;
506 /* fall through */
507 case 4:
508 /*
509 * If available we have both the default and fips providers for this
510 * test
511 */
512 if (do_fips
513 && !TEST_ptr(prov2 = OSSL_PROVIDER_load(multi_libctx, "fips")))
514 goto err;
515 if (!TEST_ptr(shared_evp_pkey = load_pkey_pem(privkey, multi_libctx)))
516 goto err;
517 worker = thread_shared_evp_pkey;
518 break;
519 case 5:
520 /*
521 * We ensure we get an md from the default provider, and then unload the
522 * provider. This ensures the provider remains around but in a
523 * deactivated state.
524 */
525 sha256 = EVP_MD_fetch(multi_libctx, "SHA2-256", NULL);
526 OSSL_PROVIDER_unload(prov);
527 prov = NULL;
528 worker = thread_provider_load_unload;
529 break;
530 default:
531 TEST_error("Invalid test index");
532 goto err;
533 }
534 if (worker2 == NULL)
535 worker2 = worker;
536
537 if (!TEST_true(run_thread(&thread1, worker))
538 || !TEST_true(run_thread(&thread2, worker2)))
539 goto err;
540
541 worker();
542
543 testresult = 1;
544 /*
545 * Don't combine these into one if statement; must wait for both threads.
546 */
547 if (!TEST_true(wait_for_thread(thread1)))
548 testresult = 0;
549 if (!TEST_true(wait_for_thread(thread2)))
550 testresult = 0;
551 if (!TEST_true(multi_success))
552 testresult = 0;
553
554 err:
555 EVP_MD_free(sha256);
556 OSSL_PROVIDER_unload(prov);
557 OSSL_PROVIDER_unload(prov2);
558 OSSL_LIB_CTX_free(multi_libctx);
559 EVP_PKEY_free(shared_evp_pkey);
560 shared_evp_pkey = NULL;
561 multi_libctx = NULL;
562 return testresult;
563}
564
565static char *multi_load_provider = "legacy";
566/*
567 * This test attempts to load several providers at the same time, and if
568 * run with a thread sanitizer, should crash if the core provider code
569 * doesn't synchronize well enough.
570 */
571#define MULTI_LOAD_THREADS 10
572static void test_multi_load_worker(void)
573{
574 OSSL_PROVIDER *prov;
575
576 if (!TEST_ptr(prov = OSSL_PROVIDER_load(NULL, multi_load_provider))
577 || !TEST_true(OSSL_PROVIDER_unload(prov)))
578 multi_success = 0;
579}
580
581static int test_multi_default(void)
582{
583 thread_t thread1, thread2;
584 int testresult = 0;
585 OSSL_PROVIDER *prov = NULL;
586
587 /* Avoid running this test twice */
588 if (multidefault_run) {
589 TEST_skip("multi default test already run");
590 return 1;
591 }
592 multidefault_run = 1;
593
594 multi_success = 1;
595 multi_libctx = NULL;
596 prov = OSSL_PROVIDER_load(multi_libctx, "default");
597 if (!TEST_ptr(prov))
598 goto err;
599
600 if (!TEST_true(run_thread(&thread1, thread_multi_simple_fetch))
601 || !TEST_true(run_thread(&thread2, thread_multi_simple_fetch)))
602 goto err;
603
604 thread_multi_simple_fetch();
605
606 if (!TEST_true(wait_for_thread(thread1))
607 || !TEST_true(wait_for_thread(thread2))
608 || !TEST_true(multi_success))
609 goto err;
610
611 testresult = 1;
612
613 err:
614 OSSL_PROVIDER_unload(prov);
615 return testresult;
616}
617
618static int test_multi_load(void)
619{
620 thread_t threads[MULTI_LOAD_THREADS];
621 int i, res = 1;
622 OSSL_PROVIDER *prov;
623
624 /* The multidefault test must run prior to this test */
625 if (!multidefault_run) {
626 TEST_info("Running multi default test first");
627 res = test_multi_default();
628 }
629
630 /*
631 * We use the legacy provider in test_multi_load_worker because it uses a
632 * child libctx that might hit more codepaths that might be sensitive to
633 * threading issues. But in a no-legacy build that won't be loadable so
634 * we use the default provider instead.
635 */
636 prov = OSSL_PROVIDER_load(NULL, "legacy");
637 if (prov == NULL) {
638 TEST_info("Cannot load legacy provider - assuming this is a no-legacy build");
639 multi_load_provider = "default";
640 }
641 OSSL_PROVIDER_unload(prov);
642
643 multi_success = 1;
644 for (i = 0; i < MULTI_LOAD_THREADS; i++)
645 (void)TEST_true(run_thread(&threads[i], test_multi_load_worker));
646
647 for (i = 0; i < MULTI_LOAD_THREADS; i++)
648 (void)TEST_true(wait_for_thread(threads[i]));
649
650 return res && multi_success;
651}
652
653static void test_lib_ctx_load_config_worker(void)
654{
655 if (!TEST_int_eq(OSSL_LIB_CTX_load_config(multi_libctx, config_file), 1))
656 multi_success = 0;
657}
658
659static int test_lib_ctx_load_config(void)
660{
661 return thread_run_test(&test_lib_ctx_load_config_worker,
662 MAXIMUM_THREADS, &test_lib_ctx_load_config_worker,
663 1, default_provider);
664}
665
666typedef enum OPTION_choice {
667 OPT_ERR = -1,
668 OPT_EOF = 0,
669 OPT_FIPS, OPT_CONFIG_FILE,
670 OPT_TEST_ENUM
671} OPTION_CHOICE;
672
673const OPTIONS *test_get_options(void)
674{
675 static const OPTIONS options[] = {
676 OPT_TEST_OPTIONS_DEFAULT_USAGE,
677 { "fips", OPT_FIPS, '-', "Test the FIPS provider" },
678 { "config", OPT_CONFIG_FILE, '<',
679 "The configuration file to use for the libctx" },
680 { NULL }
681 };
682 return options;
683}
684
685int setup_tests(void)
686{
687 OPTION_CHOICE o;
688 char *datadir;
689
690 while ((o = opt_next()) != OPT_EOF) {
691 switch (o) {
692 case OPT_FIPS:
693 do_fips = 1;
694 break;
695 case OPT_CONFIG_FILE:
696 config_file = opt_arg();
697 break;
698 case OPT_TEST_CASES:
699 break;
700 default:
701 return 0;
702 }
703 }
704
705 if (!TEST_ptr(datadir = test_get_argument(0)))
706 return 0;
707
708 privkey = test_mk_file_path(datadir, "rsakey.pem");
709 if (!TEST_ptr(privkey))
710 return 0;
711
712 /* Keep first to validate auto creation of default library context */
713 ADD_TEST(test_multi_default);
714
715 ADD_TEST(test_lock);
716 ADD_TEST(test_once);
717 ADD_TEST(test_thread_local);
718 ADD_TEST(test_atomic);
719 ADD_TEST(test_multi_load);
720 ADD_ALL_TESTS(test_multi, 6);
721 ADD_TEST(test_lib_ctx_load_config);
722 return 1;
723}
724
725void cleanup_tests(void)
726{
727 OPENSSL_free(privkey);
728}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette