Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/random/randomdev.c
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
#include <crypto/sha2/sha256.h> | #include <crypto/sha2/sha256.h> | ||||
#include <dev/random/hash.h> | #include <dev/random/hash.h> | ||||
#include <dev/random/randomdev.h> | #include <dev/random/randomdev.h> | ||||
#include <dev/random/random_harvestq.h> | #include <dev/random/random_harvestq.h> | ||||
#define RANDOM_UNIT 0 | #define RANDOM_UNIT 0 | ||||
/* | |||||
* In loadable random, the core randomdev.c / random(9) routines have static | |||||
* visibility and an alternative name to avoid conflicting with the function | |||||
* pointers of the real names in the core kernel. random_alg_context_init | |||||
* installs pointers to the loadable static names into the core kernel's | |||||
* function pointers at SI_SUB_RANDOM:SI_ORDER_SECOND. | |||||
*/ | |||||
#if defined(RANDOM_LOADABLE) | #if defined(RANDOM_LOADABLE) | ||||
#define READ_RANDOM_UIO _read_random_uio | /* Bypass the sys/random.h definitions for interface consumers. */ | ||||
#define READ_RANDOM _read_random | #undef read_random_uio | ||||
#define IS_RANDOM_SEEDED _is_random_seeded | #undef read_random | ||||
static int READ_RANDOM_UIO(struct uio *, bool); | #undef is_random_seeded | ||||
markm: Does this have to be?
I'm prejudiced against #undef. :-) | |||||
cemAuthorUnsubmitted Done Inline ActionsI think it may be possible. Here is my idea (untested): the (LOADABLE) function pointers already have non-conflicting names; leaving the existing (LOADABLE) sys/random.h macros. Then just define the (LOADABLE) version of these symbols with the ordinary names, rather than the suffixed names; they won't conflict. The macro can be avoided with void (read_random)( ... ) { } in the declarations/definitions, and function pointer assignment. I'll give it a shot. I will have intermittent availability for the next handful of days, so hopefully no rush :-). cem: I think it may be possible. Here is my idea (untested): the (LOADABLE) function pointers… | |||||
cemAuthorUnsubmitted Done Inline ActionsOk, fixed! cem: Ok, fixed! | |||||
markmUnsubmitted Not Done Inline ActionsYay! :-) markm: Yay! :-) | |||||
static void READ_RANDOM(void *, u_int); | /* | ||||
static bool IS_RANDOM_SEEDED(void); | * Rename the loaded implementation to avoid conflict with function pointer | ||||
#else | * symbol names. | ||||
#define READ_RANDOM_UIO read_random_uio | */ | ||||
#define READ_RANDOM read_random | #define read_random_uio read_random_uio_ | ||||
#define IS_RANDOM_SEEDED is_random_seeded | #define read_random read_random_ | ||||
#define is_random_seeded is_random_seeded_ | |||||
static int read_random_uio(struct uio *, bool); | |||||
static void read_random(void *, u_int); | |||||
static bool is_random_seeded(void); | |||||
#endif | #endif | ||||
static d_read_t randomdev_read; | static d_read_t randomdev_read; | ||||
static d_write_t randomdev_write; | static d_write_t randomdev_write; | ||||
static d_poll_t randomdev_poll; | static d_poll_t randomdev_poll; | ||||
static d_ioctl_t randomdev_ioctl; | static d_ioctl_t randomdev_ioctl; | ||||
static struct cdevsw random_cdevsw = { | static struct cdevsw random_cdevsw = { | ||||
.d_name = "random", | .d_name = "random", | ||||
.d_version = D_VERSION, | .d_version = D_VERSION, | ||||
.d_read = randomdev_read, | .d_read = randomdev_read, | ||||
.d_write = randomdev_write, | .d_write = randomdev_write, | ||||
.d_poll = randomdev_poll, | .d_poll = randomdev_poll, | ||||
.d_ioctl = randomdev_ioctl, | .d_ioctl = randomdev_ioctl, | ||||
}; | }; | ||||
/* For use with make_dev(9)/destroy_dev(9). */ | /* For use with make_dev(9)/destroy_dev(9). */ | ||||
static struct cdev *random_dev; | static struct cdev *random_dev; | ||||
static void | |||||
random_alg_context_ra_init_alg(void *data) | |||||
{ | |||||
p_random_alg_context = &random_alg_context; | |||||
p_random_alg_context->ra_init_alg(data); | |||||
#if defined(RANDOM_LOADABLE) | #if defined(RANDOM_LOADABLE) | ||||
random_infra_init(READ_RANDOM_UIO, READ_RANDOM, IS_RANDOM_SEEDED); | |||||
#endif | |||||
} | |||||
static void | static void | ||||
random_alg_context_ra_deinit_alg(void *data) | random_alg_context_init(void *dummy __unused) | ||||
{ | { | ||||
_read_random_uio = read_random_uio; | |||||
#if defined(RANDOM_LOADABLE) | _read_random = read_random; | ||||
random_infra_uninit(); | _is_random_seeded = is_random_seeded; | ||||
#endif | |||||
p_random_alg_context->ra_deinit_alg(data); | |||||
p_random_alg_context = NULL; | |||||
} | } | ||||
SYSINIT(random_device, SI_SUB_RANDOM, SI_ORDER_SECOND, random_alg_context_init, | |||||
NULL); | |||||
#endif | |||||
SYSINIT(random_device, SI_SUB_RANDOM, SI_ORDER_THIRD, random_alg_context_ra_init_alg, NULL); | |||||
SYSUNINIT(random_device, SI_SUB_RANDOM, SI_ORDER_THIRD, random_alg_context_ra_deinit_alg, NULL); | |||||
static struct selinfo rsel; | static struct selinfo rsel; | ||||
/* | /* | ||||
* This is the read uio(9) interface for random(4). | * This is the read uio(9) interface for random(4). | ||||
*/ | */ | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags) | randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags) | ||||
{ | { | ||||
return (READ_RANDOM_UIO(uio, (flags & O_NONBLOCK) != 0)); | return (read_random_uio(uio, (flags & O_NONBLOCK) != 0)); | ||||
} | } | ||||
/* | /* | ||||
* If the random device is not seeded, blocks until it is seeded. | * If the random device is not seeded, blocks until it is seeded. | ||||
* | * | ||||
* Returns zero when the random device is seeded. | * Returns zero when the random device is seeded. | ||||
* | * | ||||
* If the 'interruptible' parameter is true, and the device is unseeded, this | * If the 'interruptible' parameter is true, and the device is unseeded, this | ||||
Show All 13 Lines | randomdev_wait_until_seeded(bool interruptible) | ||||
spamcount = 0; | spamcount = 0; | ||||
while (!p_random_alg_context->ra_seeded()) { | while (!p_random_alg_context->ra_seeded()) { | ||||
/* keep tapping away at the pre-read until we seed/unblock. */ | /* keep tapping away at the pre-read until we seed/unblock. */ | ||||
p_random_alg_context->ra_pre_read(); | p_random_alg_context->ra_pre_read(); | ||||
/* Only bother the console every 10 seconds or so */ | /* Only bother the console every 10 seconds or so */ | ||||
if (spamcount == 0) | if (spamcount == 0) | ||||
printf("random: %s unblock wait\n", __func__); | printf("random: %s unblock wait\n", __func__); | ||||
spamcount = (spamcount + 1) % 100; | spamcount = (spamcount + 1) % 100; | ||||
error = tsleep(&random_alg_context, slpflags, "randseed", | error = tsleep(p_random_alg_context, slpflags, "randseed", | ||||
hz / 10); | hz / 10); | ||||
if (error == ERESTART || error == EINTR) { | if (error == ERESTART || error == EINTR) { | ||||
KASSERT(interruptible, | KASSERT(interruptible, | ||||
("unexpected wake of non-interruptible sleep")); | ("unexpected wake of non-interruptible sleep")); | ||||
break; | break; | ||||
} | } | ||||
/* Squash tsleep timeout condition */ | /* Squash tsleep timeout condition */ | ||||
if (error == EWOULDBLOCK) | if (error == EWOULDBLOCK) | ||||
error = 0; | error = 0; | ||||
KASSERT(error == 0, ("unexpected tsleep error %d", error)); | KASSERT(error == 0, ("unexpected tsleep error %d", error)); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
READ_RANDOM_UIO(struct uio *uio, bool nonblock) | read_random_uio(struct uio *uio, bool nonblock) | ||||
{ | { | ||||
/* 16 MiB takes about 0.08 s CPU time on my 2017 AMD Zen CPU */ | /* 16 MiB takes about 0.08 s CPU time on my 2017 AMD Zen CPU */ | ||||
#define SIGCHK_PERIOD (16 * 1024 * 1024) | #define SIGCHK_PERIOD (16 * 1024 * 1024) | ||||
const size_t sigchk_period = SIGCHK_PERIOD; | const size_t sigchk_period = SIGCHK_PERIOD; | ||||
CTASSERT(SIGCHK_PERIOD % PAGE_SIZE == 0); | CTASSERT(SIGCHK_PERIOD % PAGE_SIZE == 0); | ||||
#undef SIGCHK_PERIOD | #undef SIGCHK_PERIOD | ||||
uint8_t *random_buf; | uint8_t *random_buf; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | while (uio->uio_resid > 0 && error == 0) { | ||||
total_read += read_len; | total_read += read_len; | ||||
/* | /* | ||||
* Poll for signals every few MBs to avoid very long | * Poll for signals every few MBs to avoid very long | ||||
* uninterruptible syscalls. | * uninterruptible syscalls. | ||||
*/ | */ | ||||
if (error == 0 && uio->uio_resid != 0 && | if (error == 0 && uio->uio_resid != 0 && | ||||
total_read % sigchk_period == 0) { | total_read % sigchk_period == 0) { | ||||
error = tsleep_sbt(&random_alg_context, PCATCH, | error = tsleep_sbt(p_random_alg_context, PCATCH, | ||||
"randrd", SBT_1NS, 0, C_HARDCLOCK); | "randrd", SBT_1NS, 0, C_HARDCLOCK); | ||||
/* Squash tsleep timeout condition */ | /* Squash tsleep timeout condition */ | ||||
if (error == EWOULDBLOCK) | if (error == EWOULDBLOCK) | ||||
error = 0; | error = 0; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
Show All 16 Lines | |||||
* | * | ||||
* If the tunable 'kern.random.initial_seeding.bypass_before_seeding' is set | * If the tunable 'kern.random.initial_seeding.bypass_before_seeding' is set | ||||
* non-zero, silently fail to emit random data (matching the pre-r346250 | * non-zero, silently fail to emit random data (matching the pre-r346250 | ||||
* behavior). If read_random is called prior to seeding and bypassed because | * behavior). If read_random is called prior to seeding and bypassed because | ||||
* of this tunable, the condition is reported in the read-only sysctl | * of this tunable, the condition is reported in the read-only sysctl | ||||
* 'kern.random.initial_seeding.read_random_bypassed_before_seeding'. | * 'kern.random.initial_seeding.read_random_bypassed_before_seeding'. | ||||
*/ | */ | ||||
void | void | ||||
READ_RANDOM(void *random_buf, u_int len) | read_random(void *random_buf, u_int len) | ||||
{ | { | ||||
KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__)); | KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__)); | ||||
p_random_alg_context->ra_pre_read(); | p_random_alg_context->ra_pre_read(); | ||||
if (len == 0) | if (len == 0) | ||||
return; | return; | ||||
Show All 17 Lines | if (__predict_false(!p_random_alg_context->ra_seeded())) { | ||||
(void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE); | (void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE); | ||||
} | } | ||||
read_rate_increment(roundup2(len, sizeof(uint32_t))); | read_rate_increment(roundup2(len, sizeof(uint32_t))); | ||||
p_random_alg_context->ra_read(random_buf, len); | p_random_alg_context->ra_read(random_buf, len); | ||||
} | } | ||||
bool | bool | ||||
IS_RANDOM_SEEDED(void) | is_random_seeded(void) | ||||
{ | { | ||||
return (p_random_alg_context->ra_seeded()); | return (p_random_alg_context->ra_seeded()); | ||||
} | } | ||||
static __inline void | static __inline void | ||||
randomdev_accumulate(uint8_t *buf, u_int count) | randomdev_accumulate(uint8_t *buf, u_int count) | ||||
{ | { | ||||
static u_int destination = 0; | static u_int destination = 0; | ||||
Show All 34 Lines | randomdev_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) | ||||
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); | random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); | ||||
nbytes = uio->uio_resid; | nbytes = uio->uio_resid; | ||||
while (uio->uio_resid > 0 && error == 0) { | while (uio->uio_resid > 0 && error == 0) { | ||||
c = MIN(uio->uio_resid, PAGE_SIZE); | c = MIN(uio->uio_resid, PAGE_SIZE); | ||||
error = uiomove(random_buf, c, uio); | error = uiomove(random_buf, c, uio); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
randomdev_accumulate(random_buf, c); | randomdev_accumulate(random_buf, c); | ||||
tsleep(&random_alg_context, 0, "randwr", hz/10); | tsleep(p_random_alg_context, 0, "randwr", hz/10); | ||||
} | } | ||||
if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR)) | if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR)) | ||||
/* Partial write, not error. */ | /* Partial write, not error. */ | ||||
error = 0; | error = 0; | ||||
free(random_buf, M_ENTROPY); | free(random_buf, M_ENTROPY); | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 12 Lines | |||||
} | } | ||||
/* This will be called by the entropy processor when it seeds itself and becomes secure */ | /* This will be called by the entropy processor when it seeds itself and becomes secure */ | ||||
void | void | ||||
randomdev_unblock(void) | randomdev_unblock(void) | ||||
{ | { | ||||
selwakeuppri(&rsel, PUSER); | selwakeuppri(&rsel, PUSER); | ||||
wakeup(&random_alg_context); | wakeup(p_random_alg_context); | ||||
printf("random: unblocking device.\n"); | printf("random: unblocking device.\n"); | ||||
/* Do random(9) a favour while we are about it. */ | /* Do random(9) a favour while we are about it. */ | ||||
(void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); | (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); | ||||
} | } | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused, | randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused, | ||||
Show All 22 Lines | randomdev_modevent(module_t mod __unused, int type, void *data __unused) | ||||
switch (type) { | switch (type) { | ||||
case MOD_LOAD: | case MOD_LOAD: | ||||
printf("random: entropy device external interface\n"); | printf("random: entropy device external interface\n"); | ||||
random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw, | random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw, | ||||
RANDOM_UNIT, NULL, UID_ROOT, GID_WHEEL, 0644, "random"); | RANDOM_UNIT, NULL, UID_ROOT, GID_WHEEL, 0644, "random"); | ||||
make_dev_alias(random_dev, "urandom"); /* compatibility */ | make_dev_alias(random_dev, "urandom"); /* compatibility */ | ||||
break; | break; | ||||
case MOD_UNLOAD: | case MOD_UNLOAD: | ||||
destroy_dev(random_dev); | error = EBUSY; | ||||
break; | break; | ||||
case MOD_SHUTDOWN: | case MOD_SHUTDOWN: | ||||
break; | break; | ||||
default: | default: | ||||
error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
Show All 12 Lines |
Does this have to be?
I'm prejudiced against #undef. :-)