From 5d25f540cc3023682bf4c03edff4173dda8d6e32 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 27 May 2021 16:26:24 -0400 Subject: [PATCH 1/6] Updating configure.ac to support coarse timer support Need a compile flag to ensure that we can use the new notime_imply infrastructure. So during configure check for the new registration symbol and add a HAVE_JITTER_NOTIME flag if we find it Signed-off-by: Neil Horman --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 2761a72..d689670 100644 --- a/configure.ac +++ b/configure.ac @@ -92,7 +92,10 @@ AS_IF( AC_MSG_NOTICE([Searching for jitterentropy library]) AC_SEARCH_LIBS(jent_version,jitterentropy, [AM_CONDITIONAL([JITTER], [true]) - AC_DEFINE([HAVE_JITTER],1,[Enable JITTER])], + AC_DEFINE([HAVE_JITTER],1,[Enable JITTER]) + AC_CHECK_LIB(jitterentropy, jent_entropy_switch_notime_impl, + [AC_DEFINE([HAVE_JITTER_NOTIME],1,[Enable JITTER_NOTIME])], + [],-lpthread)], AC_MSG_NOTICE([No Jitterentropy library found]),-lpthread) ], [AC_MSG_NOTICE([Disabling JITTER entropy source])] ) From a8dd63508b65a30649287c4bea26fbbf45762cbe Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 27 May 2021 17:05:30 -0400 Subject: [PATCH 2/6] Add support for registering an external timer jitter now offers support for software timing on platforms with a timer to coarse to support valid measurements. This new code registers our timer thread to take advantage of this feature Signed-off-by: Neil Horman --- rngd_jitter.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/rngd_jitter.c b/rngd_jitter.c index 133c7e4..c637b39 100644 --- a/rngd_jitter.c +++ b/rngd_jitter.c @@ -38,6 +38,85 @@ #include "rngd_entsource.h" #include "ossl_helpers.h" +#ifdef HAVE_JITTER_NOTIME +struct rngd_notime_ctx { + pthread_attr_t notime_pthread_attr; /* pthreads library */ + pthread_t notime_thread_id; /* pthreads thread ID */ +}; + +static int rngd_notime_init(void **ctx) +{ + struct rngd_notime_ctx *thread_ctx; + + thread_ctx = calloc(1, sizeof(struct rngd_notime_ctx)); + if (!thread_ctx) + return -errno; + + *ctx = thread_ctx; + + return 0; +} + +static void rngd_notime_fini(void *ctx) +{ + struct rngd_notime_ctx *thread_ctx = (struct rngd_notime_ctx *)ctx; + + if (thread_ctx) + free(thread_ctx); +} + +static int rngd_notime_start(void *ctx, + void *(*start_routine) (void *), void *arg) +{ + struct rngd_notime_ctx *thread_ctx = (struct rngd_notime_ctx *)ctx; + int ret; + int i; + cpu_set_t *cpus; + size_t cpusize; + + if (!thread_ctx) + return -EINVAL; + + ret = -pthread_attr_init(&thread_ctx->notime_pthread_attr); + if (ret) + return ret; + + /* + * the soft timer function should affine to all cpus + */ + i = sysconf(_SC_NPROCESSORS_CONF); + cpus = CPU_ALLOC(i); + cpusize = CPU_ALLOC_SIZE(i); + CPU_ZERO_S(cpusize, cpus); + for(i=i-1;i>=0;i--) { + CPU_SET(i,cpus); + } + + ret = -pthread_create(&thread_ctx->notime_thread_id, + &thread_ctx->notime_pthread_attr, + start_routine, arg); + + CPU_FREE(cpus); + return ret; +} + +static void rngd_notime_stop(void *ctx) +{ + struct rngd_notime_ctx *thread_ctx = (struct rngd_notime_ctx *)ctx; + + pthread_join(thread_ctx->notime_thread_id, NULL); + pthread_attr_destroy(&thread_ctx->notime_pthread_attr); +} + + +static struct jent_notime_thread rngd_notime_thread_builtin = { + .jent_notime_init = rngd_notime_init, + .jent_notime_fini = rngd_notime_fini, + .jent_notime_start = rngd_notime_start, + .jent_notime_stop = rngd_notime_stop +}; +#endif + /* Read data from the drng in chunks of 128 bytes for AES scrambling */ #define CHUNK_SIZE (AES_BLOCK*8) /* 8 parallel streams */ #define RDRAND_ROUNDS 512 /* 512:1 data reduction */ @@ -296,11 +375,19 @@ int init_jitter_entropy_source(struct rng *ent_src) int i; int size; int flags; + int ret; int core_id = 0; signal(SIGUSR1, jitter_thread_exit_signal); - int ret = jent_entropy_init(); +#ifdef HAVE_JITTER_NOTIME + ret = jent_entropy_switch_notime_impl(&rngd_notime_thread_builtin); + if (ret) { + message_entsrc(ent_src, LOG_DAEMON|LOG_WARNING, "JITTER rng fails to register soft timer: %d\n", ret); + return 1; + } +#endif + ret = jent_entropy_init(); if(ret) { message_entsrc(ent_src,LOG_DAEMON|LOG_WARNING, "JITTER rng fails with code %d\n", ret); return 1; From b402805a5971d7091e1f27a676f2e1321cdd9617 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 28 May 2021 07:31:06 -0400 Subject: [PATCH 3/6] Update rngd_jitter to use common init/fini functions for soft timer When using the software timer, we don't need to define our own init/fini functions, we can just use the common ones from the jitter lib that are provided as convienience. Signed-off-by: Neil Horman --- rngd_jitter.c | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/rngd_jitter.c b/rngd_jitter.c index c637b39..c9dc331 100644 --- a/rngd_jitter.c +++ b/rngd_jitter.c @@ -39,36 +39,10 @@ #include "ossl_helpers.h" #ifdef HAVE_JITTER_NOTIME -struct rngd_notime_ctx { - pthread_attr_t notime_pthread_attr; /* pthreads library */ - pthread_t notime_thread_id; /* pthreads thread ID */ -}; - -static int rngd_notime_init(void **ctx) -{ - struct rngd_notime_ctx *thread_ctx; - - thread_ctx = calloc(1, sizeof(struct rngd_notime_ctx)); - if (!thread_ctx) - return -errno; - - *ctx = thread_ctx; - - return 0; -} - -static void rngd_notime_fini(void *ctx) -{ - struct rngd_notime_ctx *thread_ctx = (struct rngd_notime_ctx *)ctx; - - if (thread_ctx) - free(thread_ctx); -} - static int rngd_notime_start(void *ctx, void *(*start_routine) (void *), void *arg) { - struct rngd_notime_ctx *thread_ctx = (struct rngd_notime_ctx *)ctx; + struct jent_notime_ctx *thread_ctx = (struct jent_notime_ctx *)ctx; int ret; int i; cpu_set_t *cpus; @@ -102,7 +76,7 @@ static int rngd_notime_start(void *ctx, static void rngd_notime_stop(void *ctx) { - struct rngd_notime_ctx *thread_ctx = (struct rngd_notime_ctx *)ctx; + struct jent_notime_ctx *thread_ctx = (struct jent_notime_ctx *)ctx; pthread_join(thread_ctx->notime_thread_id, NULL); pthread_attr_destroy(&thread_ctx->notime_pthread_attr); @@ -110,8 +84,8 @@ static void rngd_notime_stop(void *ctx) static struct jent_notime_thread rngd_notime_thread_builtin = { - .jent_notime_init = rngd_notime_init, - .jent_notime_fini = rngd_notime_fini, + .jent_notime_init = jent_notime_init, + .jent_notime_fini = jent_notime_fini, .jent_notime_start = rngd_notime_start, .jent_notime_stop = rngd_notime_stop }; From 95e0c04c72bac6b0f766caafc3b1972b506a9b16 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 28 May 2021 08:36:17 -0400 Subject: [PATCH 4/6] Adding soft timer flag Add option to jitter to force use of soft timer Signed-off-by: Neil Horman --- rngd.c | 5 +++++ rngd.h | 1 + rngd_jitter.c | 16 ++++++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/rngd.c b/rngd.c index 83fce28..e85e723 100644 --- a/rngd.c +++ b/rngd.c @@ -214,6 +214,11 @@ static struct rng_option jitter_options[] = { .type = VAL_INT, .int_val = 1, }, + [JITTER_OPT_FORCE_INT_TIMER] = { + .key = "force_soft_timer", + .type = VAL_INT, + .int_val = 0, + }, { .key = NULL, } diff --git a/rngd.h b/rngd.h index 150b7e7..6208b95 100644 --- a/rngd.h +++ b/rngd.h @@ -88,6 +88,7 @@ enum { JITTER_OPT_RETRY_COUNT = 3, JITTER_OPT_RETRY_DELAY = 4, JITTER_OPT_USE_AES = 5, + JITTER_OPT_FORCE_INT_TIMER = 6, JITTER_OPT_MAX, }; diff --git a/rngd_jitter.c b/rngd_jitter.c index c9dc331..ce58768 100644 --- a/rngd_jitter.c +++ b/rngd_jitter.c @@ -65,7 +65,9 @@ static int rngd_notime_start(void *ctx, for(i=i-1;i>=0;i--) { CPU_SET(i,cpus); } + pthread_attr_setaffinity_np(&thread_ctx->notime_pthread_attr, cpusize, cpus); + message(LOG_DAEMON|LOG_DEBUG, "starting internal timer %x\n", thread_ctx->notime_thread_id); ret = -pthread_create(&thread_ctx->notime_thread_id, &thread_ctx->notime_pthread_attr, start_routine, arg); @@ -78,6 +80,7 @@ static void rngd_notime_stop(void *ctx) { struct jent_notime_ctx *thread_ctx = (struct jent_notime_ctx *)ctx; + message(LOG_DAEMON|LOG_DEBUG, "stopping internal timer\n"); pthread_join(thread_ctx->notime_thread_id, NULL); pthread_attr_destroy(&thread_ctx->notime_pthread_attr); } @@ -349,27 +352,32 @@ int init_jitter_entropy_source(struct rng *ent_src) int i; int size; int flags; + int entflags = 0; int ret; int core_id = 0; signal(SIGUSR1, jitter_thread_exit_signal); + if (validate_jitter_options(ent_src)) + return 1; + #ifdef HAVE_JITTER_NOTIME ret = jent_entropy_switch_notime_impl(&rngd_notime_thread_builtin); if (ret) { message_entsrc(ent_src, LOG_DAEMON|LOG_WARNING, "JITTER rng fails to register soft timer: %d\n", ret); return 1; } + + if (ent_src->rng_options[JITTER_OPT_FORCE_INT_TIMER].int_val) + entflags |= JENT_FORCE_INTERNAL_TIMER; #endif + ret = jent_entropy_init(); if(ret) { message_entsrc(ent_src,LOG_DAEMON|LOG_WARNING, "JITTER rng fails with code %d\n", ret); return 1; } - if (validate_jitter_options(ent_src)) - return 1; - if (pipe(pipefds)) { message_entsrc(ent_src,LOG_DAEMON|LOG_WARNING, "JITTER rng can't open pipe: %s\n", strerror(errno)); return 1; @@ -425,7 +433,7 @@ int init_jitter_entropy_source(struct rng *ent_src) tdata[i].done = -1; core_id++; tdata[i].buf_sz = ent_src->rng_options[JITTER_OPT_BUF_SZ].int_val; - tdata[i].ec = jent_entropy_collector_alloc(1, 0); + tdata[i].ec = jent_entropy_collector_alloc(1, entflags); tdata[i].slpmode = ent_src->rng_options[JITTER_OPT_RETRY_DELAY].int_val; pthread_create(&threads[i], NULL, thread_entropy_task, &tdata[i]); } From 5700cffaf98e1198ea79fb1cb9aa62e2d2906274 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 28 May 2021 08:43:48 -0400 Subject: [PATCH 5/6] Adding documentation to man page for soft timer jitter option Need to document our new soft timer option Signed-off-by: Neil Horman --- rngd.8.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rngd.8.in b/rngd.8.in index f41e688..bc5266d 100644 --- a/rngd.8.in +++ b/rngd.8.in @@ -197,6 +197,8 @@ Options \fBretry_delay - \fR between each retry for retry_count above, sleep for this many seconds. May also be the special value -1, representing adaptive sleep, where each retry delay will be half the recorded execution time of the last entropy gathering round (default -1) +\fBforce_soft_timer - \fR on platforms with a hardware timer that is too coarse to sample jitter, we can instead use a software based timer loop. Detection and use of this mechanism is automatic, but this can be useful for testing purposes + .TP .B PKCS11 (pkcs11) [Index 6] From 6bc208d88d3c81de45fc63c64810e580d4c5b986 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 28 May 2021 09:20:30 -0400 Subject: [PATCH 6/6] Adjusting jitter source to only use 1 thread with soft timer When using soft timers, we need to limit our thread use to reduce contention between the jitter threads and the timer threads Signed-off-by: Neil Horman --- rngd_jitter.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/rngd_jitter.c b/rngd_jitter.c index ce58768..b68c791 100644 --- a/rngd_jitter.c +++ b/rngd_jitter.c @@ -38,6 +38,8 @@ #include "rngd_entsource.h" #include "ossl_helpers.h" +static bool using_soft_timer = false; + #ifdef HAVE_JITTER_NOTIME static int rngd_notime_start(void *ctx, void *(*start_routine) (void *), void *arg) @@ -67,7 +69,6 @@ static int rngd_notime_start(void *ctx, } pthread_attr_setaffinity_np(&thread_ctx->notime_pthread_attr, cpusize, cpus); - message(LOG_DAEMON|LOG_DEBUG, "starting internal timer %x\n", thread_ctx->notime_thread_id); ret = -pthread_create(&thread_ctx->notime_thread_id, &thread_ctx->notime_pthread_attr, start_routine, arg); @@ -80,14 +81,19 @@ static void rngd_notime_stop(void *ctx) { struct jent_notime_ctx *thread_ctx = (struct jent_notime_ctx *)ctx; - message(LOG_DAEMON|LOG_DEBUG, "stopping internal timer\n"); pthread_join(thread_ctx->notime_thread_id, NULL); pthread_attr_destroy(&thread_ctx->notime_pthread_attr); } +static int rngd_notime_init(void **ctx) +{ + using_soft_timer = true; + return jent_notime_init(ctx); +} + static struct jent_notime_thread rngd_notime_thread_builtin = { - .jent_notime_init = jent_notime_init, + .jent_notime_init = rngd_notime_init, .jent_notime_fini = jent_notime_fini, .jent_notime_start = rngd_notime_start, .jent_notime_stop = rngd_notime_stop @@ -315,6 +321,8 @@ int validate_jitter_options(struct rng *ent_src) int refill = ent_src->rng_options[JITTER_OPT_REFILL].int_val; int delay = ent_src->rng_options[JITTER_OPT_RETRY_DELAY].int_val; int rcount = ent_src->rng_options[JITTER_OPT_RETRY_COUNT].int_val; + int soft_timer = ent_src->rng_options[JITTER_OPT_FORCE_INT_TIMER].int_val; + int num_threads = ent_src->rng_options[JITTER_OPT_THREADS].int_val; /* Need at least one thread to do this work */ if (!threads) { @@ -338,6 +346,12 @@ int validate_jitter_options(struct rng *ent_src) return 1; } +#ifndef HAVE_JITTER_NOTIME + if (soft_timer) { + message_entsrc(ent_src, LOG_DAEMON|LOG_ERR, "JITTER doesn't support soft timers in this build\n"); + return 1; + } +#endif return 0; } @@ -417,7 +431,7 @@ int init_jitter_entropy_source(struct rng *ent_src) tdata = calloc(num_threads, sizeof(struct thread_data)); threads = calloc(num_threads, sizeof(pthread_t)); - message_entsrc(ent_src,LOG_DAEMON|LOG_DEBUG, "JITTER starts %d threads\n", num_threads); + message_entsrc(ent_src,LOG_DAEMON|LOG_DEBUG, "JITTER attempting to start %d threads\n", num_threads); /* * Allocate and init the thread data that we need @@ -436,14 +450,6 @@ int init_jitter_entropy_source(struct rng *ent_src) tdata[i].ec = jent_entropy_collector_alloc(1, entflags); tdata[i].slpmode = ent_src->rng_options[JITTER_OPT_RETRY_DELAY].int_val; pthread_create(&threads[i], NULL, thread_entropy_task, &tdata[i]); - } - - CPU_FREE(cpus); - cpus = NULL; - - /* Make sure all our threads are doing their jobs */ - for (i=0; i < num_threads; i++) { - /* wait until the done state transitions from negative to zero or more */ pthread_mutex_lock(&tdata[i].statemtx); if (tdata[i].done < 0) pthread_cond_wait(&tdata[i].statecond, &tdata[i].statemtx); @@ -453,8 +459,16 @@ int init_jitter_entropy_source(struct rng *ent_src) else message_entsrc(ent_src,LOG_DAEMON|LOG_DEBUG, "CPU Thread %d is ready\n", i); pthread_mutex_unlock(&tdata[i].statemtx); + if (using_soft_timer == true) { + num_threads = 1; + message_entsrc(ent_src, LOG_DAEMON|LOG_INFO, "Limiting jitter to one thread for soft timer use\n"); + break; + } } + CPU_FREE(cpus); + cpus = NULL; + if (ent_src->rng_options[JITTER_OPT_USE_AES].int_val) { /* * Temporarily disable aes so we don't try to use it during init