Skip to content

Commit b20f7ed

Browse files
committed
x509storeissuer: add ability to add certificates to the store during the run
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
1 parent 106e565 commit b20f7ed

File tree

1 file changed

+127
-9
lines changed

1 file changed

+127
-9
lines changed

source/x509storeissuer.c

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "perflib/err.h"
3030
#include "perflib/perflib.h"
3131

32+
#define W_PROBABILITY 50
33+
#define MAX_WRITERS 0
3234
#define RUN_TIME 5
3335
#define QUANTILES 5
3436
#define NONCE_CFG "file:servercert.pem"
@@ -38,6 +40,8 @@
3840

3941
static size_t timeout_us = RUN_TIME * 1000000;
4042
static size_t quantiles = QUANTILES;
43+
static size_t max_writers = MAX_WRITERS;
44+
static size_t w_probability = W_PROBABILITY * 65536 / 100;
4145

4246
enum verbosity {
4347
VERBOSITY_TERSE,
@@ -49,6 +53,11 @@ enum verbosity {
4953
VERBOSITY_MAX__
5054
};
5155

56+
static enum mode {
57+
MODE_R,
58+
MODE_RW, /* "MODE_W" is just MODE_RW with 100% write probability */
59+
} mode = MODE_R;
60+
5261
enum nonce_type {
5362
NONCE_PATH,
5463
};
@@ -57,6 +66,7 @@ struct call_times {
5766
uint64_t duration;
5867
uint64_t total_count;
5968
uint64_t total_found;
69+
uint64_t total_added;
6070
uint64_t min_count;
6171
uint64_t max_count;
6272
double avg;
@@ -95,8 +105,11 @@ ALIGN64 struct thread_data {
95105
struct {
96106
uint64_t count;
97107
uint64_t found;
108+
uint64_t added_certs;
98109
OSSL_TIME end_time;
99110
} *q_data;
111+
size_t cert_count;
112+
X509 **certs;
100113
X509_STORE_CTX *ctx;
101114
} *thread_data;
102115

@@ -112,6 +125,28 @@ OSSL_TIME max_time;
112125
#define OSSL_MIN(p, q) ((p) < (q) ? (p) : (q))
113126
#define OSSL_MAX(p, q) ((p) > (q) ? (p) : (q))
114127

128+
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L && \
129+
!defined(__cplusplus)
130+
# define ossl_thread thread_local
131+
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \
132+
!defined(__cplusplus)
133+
# define ossl_thread _Thread_local
134+
#elif defined(__GNUC__)
135+
# define ossl_thread __thread
136+
#elif defined(_MSC_VER)
137+
# define ossl_thread __declspec(thread)
138+
#else
139+
# define ossl_thread
140+
#endif
141+
142+
static ossl_thread uint32_t prng_seed;
143+
144+
static uint32_t prng(void)
145+
{
146+
/* Taken from FreeBSD's lib/libc/stdlib/rand.c */
147+
return (prng_seed = prng_seed * 1103515245U + 12345U);
148+
}
149+
115150
/**
116151
* Extend the x509_pool, by the increment, if inc is 0, the increment size
117152
* is decided automatically.
@@ -482,30 +517,46 @@ do_x509storeissuer(size_t num)
482517
OSSL_TIME q_end;
483518
size_t q = 0;
484519
size_t count = 0;
520+
size_t add = 0;
485521
size_t found = 0;
486522

523+
prng_seed = num;
487524
td->start_time = ossl_time_now();
488525
duration.t = max_time.t - td->start_time.t;
489526
q_end.t = duration.t / quantiles + td->start_time.t;
490527

491528
do {
492-
if (X509_STORE_CTX_get1_issuer(&issuer, td->ctx, x509_nonce) != 0) {
493-
found++;
494-
X509_free(issuer);
529+
if (td->cert_count > 0 && (prng() % 65536 < w_probability)) {
530+
size_t cert_id = add % td->cert_count;
531+
532+
if (!X509_STORE_add_cert(store, td->certs[cert_id])) {
533+
warnx("thread %zu: Failed to add generated certificate %zu"
534+
" to the store", num, cert_id);
535+
} else {
536+
add++;
537+
}
538+
} else {
539+
if (X509_STORE_CTX_get1_issuer(&issuer, td->ctx, x509_nonce) != 0) {
540+
found++;
541+
X509_free(issuer);
542+
}
543+
issuer = NULL;
495544
}
496-
issuer = NULL;
545+
497546
count++;
498547
time = ossl_time_now();
499548
if (time.t >= q_end.t) {
500549
td->q_data[q].count = count;
501550
td->q_data[q].found = found;
551+
td->q_data[q].added_certs = add;
502552
td->q_data[q].end_time = time;
503553
q_end.t = (duration.t * (++q + 1)) / quantiles + td->start_time.t;
504554
}
505555
} while (time.t < max_time.t);
506556

507557
td->q_data[quantiles - 1].count = count;
508558
td->q_data[quantiles - 1].found = found;
559+
td->q_data[quantiles - 1].added_certs = add;
509560
td->q_data[quantiles - 1].end_time = time;
510561
}
511562

@@ -552,17 +603,21 @@ get_calltimes(struct call_times *times, int verbosity)
552603
(q ? thread_data[i].q_data[q - 1].count : 0);
553604
uint64_t found = thread_data[i].q_data[q].found -
554605
(q ? thread_data[i].q_data[q - 1].found : 0);
606+
uint64_t add = thread_data[i].q_data[q].added_certs -
607+
(q ? thread_data[i].q_data[q - 1].added_certs : 0);
555608

556609
times[q].duration += thread_data[i].q_data[q].end_time.t - start_t;
557610
times[q].total_count += count;
558611
times[q].total_found += found;
612+
times[q].total_added += add;
559613
}
560614
}
561615

562616
for (size_t q = 0; q < quantiles; q++) {
563617
times[quantiles].duration += times[q].duration;
564618
times[quantiles].total_count += times[q].total_count;
565619
times[quantiles].total_found += times[q].total_found;
620+
times[quantiles].total_added += times[q].total_added;
566621
}
567622

568623
for (size_t q = (quantiles == 1); q <= quantiles; q++)
@@ -647,14 +702,18 @@ report_result(int verbosity)
647702
printf(": avg: %9.3lf us, median: %9.3lf us"
648703
", min: %9.3lf us @thread %3zu, max: %9.3lf us @thread %3zu"
649704
", stddev: %9.3lf us (%8.4lf%%)"
650-
", hits %9" PRIu64 " of %9" PRIu64 " (%8.4lf%%)\n",
705+
", hits %9" PRIu64 " of %9" PRIu64 " (%8.4lf%%)"
706+
", added certs: %" PRIu64 "\n",
651707
times[i].avg, times[i].median,
652708
times[i].min, times[i].min_idx,
653709
times[i].max, times[i].max_idx,
654710
times[i].stddev,
655711
100.0 * times[i].stddev / times[i].avg,
656-
times[i].total_found, times[i].total_count,
657-
100.0 * times[i].total_found / (times[i].total_count));
712+
times[i].total_found,
713+
(times[i].total_count - times[i].total_added),
714+
100.0 * times[i].total_found
715+
/ (times[i].total_count - times[i].total_added),
716+
times[i].total_added);
658717
}
659718
break;
660719
}
@@ -667,6 +726,7 @@ usage(char * const argv[])
667726
{
668727
fprintf(stderr,
669728
"Usage: %s [-t] [-v] [-q N] [-T time] [-n nonce_type:type_args]"
729+
" [-m mode] [-w writer_threads] [-W percentage]"
670730
" [-l max_certs] [-L max_cert_dirs] [-E] [-C threads]"
671731
" [-V] certsdir [certsdir...] threadcount\n"
672732
"\t-t\tTerse output\n"
@@ -681,6 +741,13 @@ usage(char * const argv[])
681741
"\t\t\tfile:PATH - load nonce certificate from PATH;\n"
682742
"\t\t\tif PATH is relative, the provided certsdir's are searched.\n"
683743
"\t\tDefault: " NONCE_CFG "\n"
744+
"\t-m\tTest mode, can be one of r, rw. Default: r\n"
745+
"\t-w\tMaximum number of threads that attempt addition\n"
746+
"\t\tof the new certificates to the store in rw mode,\n"
747+
"\t\t0 is unlimited. Default: " OPENSSL_MSTR(MAX_WRITERS) "\n"
748+
"\t-W\tProbability of a certificate being written\n"
749+
"\t\tto the store, instead of being queried,\n"
750+
"\t\tin percents. Default: " OPENSSL_MSTR(W_PROBABILITY) "\n"
684751
"\t-l\tLimit on the number of initially loaded certificates.\n"
685752
"\t\tDefault: " OPENSSL_MSTR(MAX_LOAD_CERTS) "\n"
686753
"\t-L\tLimit on the number of initially loaded certificate\n"
@@ -710,8 +777,23 @@ parse_timeout(const char * const optarg)
710777
return (size_t)(timeout_s * 1e6);
711778
}
712779

780+
static double
781+
parse_probability(const char * const optarg)
782+
{
783+
char *endptr = NULL;
784+
double prob;
785+
786+
prob = strtod(optarg, &endptr);
787+
788+
if (endptr == NULL || *endptr != '\0' || prob < 0 || prob > 100)
789+
errx(EXIT_FAILURE, "incorrect probability value: \"%s\"", optarg);
790+
791+
return prob;
792+
}
793+
713794
/**
714795
* Parse nonce configuration string. Currently supported formats:
796+
* * "gen" - generate a nonce certificate
715797
* * "file:PATH" - where PATH is either a relative path (that will be then
716798
* checked against the list of directories provided),
717799
* or an absolute one.
@@ -765,7 +847,7 @@ main(int argc, char *argv[])
765847

766848
parse_nonce_cfg(NONCE_CFG, &nonce_cfg);
767849

768-
while ((opt = getopt(argc, argv, "tvq:T:n:l:L:EC:V")) != -1) {
850+
while ((opt = getopt(argc, argv, "tvq:T:n:m:w:W:l:L:EC:V")) != -1) {
769851
switch (opt) {
770852
case 't': /* terse */
771853
verbosity = VERBOSITY_TERSE;
@@ -788,6 +870,22 @@ main(int argc, char *argv[])
788870
case 'n': /* nonce */
789871
parse_nonce_cfg(optarg, &nonce_cfg);
790872
break;
873+
case 'm': /* mode */
874+
if (strcasecmp(optarg, "r") == 0) {
875+
mode = MODE_R;
876+
} else if (strcasecmp(optarg, "rw") == 0) {
877+
mode = MODE_RW;
878+
} else {
879+
errx(EXIT_FAILURE, "Unknown mode: \"%s\"", optarg);
880+
}
881+
break;
882+
case 'w': /* maximum writers */
883+
max_writers = parse_int(optarg, 0, INT_MAX,
884+
"maximum number of writers");
885+
break;
886+
case 'W': /* percent of writes */
887+
w_probability = (size_t) (parse_probability(optarg) * 65536 / 100);
888+
break;
791889
case 'l': /* max certs to load */
792890
max_load_certs = parse_int(optarg, 0, INT_MAX,
793891
"maximum certificate load count");
@@ -855,7 +953,8 @@ main(int argc, char *argv[])
855953

856954
num_certs += read_certsdirs(argv + dirs_start, argc - dirs_start - 1,
857955
max_load_certs, max_load_cert_dirs,
858-
store, &cert_pool, &total_certs_read);
956+
store, mode == MODE_RW ? &cert_pool : NULL,
957+
&total_certs_read);
859958

860959
if (verbosity >= VERBOSITY_DEBUG_STATS)
861960
fprintf(stderr, "Added %zu certificates to the store"
@@ -881,6 +980,22 @@ main(int argc, char *argv[])
881980
}
882981
}
883982

983+
if (mode == MODE_RW && cert_pool.size > 0) {
984+
const size_t writers = max_writers ? OSSL_MIN(max_writers, threadcount)
985+
: threadcount;
986+
987+
/*
988+
* Point each writer thread at the part of the generated certs
989+
* array it uses for store population.
990+
*/
991+
for (size_t i = 0; i < writers; i++) {
992+
size_t offs = (cert_pool.size * i) / writers;
993+
994+
thread_data[i].certs = cert_pool.entries + offs;
995+
thread_data[i].cert_count = OSSL_MAX(cert_pool.size / writers, 1);
996+
}
997+
}
998+
884999
max_time = ossl_time_add(ossl_time_now(), ossl_us2time(timeout_us));
8851000

8861001
if (!perflib_run_multi_thread_test(do_x509storeissuer, threadcount, &duration))
@@ -889,6 +1004,9 @@ main(int argc, char *argv[])
8891004
if (error)
8901005
errx(EXIT_FAILURE, "Error during test");
8911006

1007+
if (mode == MODE_RW)
1008+
report_store_size(store, "after the test run", verbosity);
1009+
8921010
report_result(verbosity);
8931011

8941012
ret = EXIT_SUCCESS;

0 commit comments

Comments
 (0)