Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/random/randomdev.c
Show First 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | while (!p_random_alg_context->ra_seeded()) { | ||||
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) | ||||
{ | { | ||||
uint8_t *random_buf; | |||||
int error; | |||||
ssize_t read_len, total_read, c; | |||||
/* 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 | ||||
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); | uint8_t *random_buf; | ||||
size_t total_read, read_len; | |||||
ssize_t bufsize; | |||||
int error; | |||||
KASSERT(uio->uio_rw == UIO_READ, ("%s: bogus write", __func__)); | |||||
KASSERT(uio->uio_resid >= 0, ("%s: bogus negative resid", __func__)); | |||||
p_random_alg_context->ra_pre_read(); | p_random_alg_context->ra_pre_read(); | ||||
error = 0; | error = 0; | ||||
/* (Un)Blocking logic */ | /* (Un)Blocking logic */ | ||||
if (!p_random_alg_context->ra_seeded()) { | if (!p_random_alg_context->ra_seeded()) { | ||||
if (nonblock) | if (nonblock) | ||||
error = EWOULDBLOCK; | error = EWOULDBLOCK; | ||||
else | else | ||||
error = randomdev_wait_until_seeded( | error = randomdev_wait_until_seeded( | ||||
SEEDWAIT_INTERRUPTIBLE); | SEEDWAIT_INTERRUPTIBLE); | ||||
} | } | ||||
if (error == 0) { | if (error != 0) | ||||
read_rate_increment((uio->uio_resid + sizeof(uint32_t))/sizeof(uint32_t)); | return (error); | ||||
read_rate_increment(howmany(uio->uio_resid + 1, sizeof(uint32_t))); | |||||
total_read = 0; | total_read = 0; | ||||
while (uio->uio_resid && !error) { | |||||
read_len = uio->uio_resid; | /* Easy to deal with the trivial 0 byte case. */ | ||||
if (__predict_false(uio->uio_resid == 0)) | |||||
return (0); | |||||
/* | /* | ||||
* Belt-and-braces. | * If memory is plentiful, use maximally sized requests to avoid | ||||
* Round up the read length to a crypto block size multiple, | * per-call algorithm overhead. But fall back to a single page | ||||
* which is what the underlying generator is expecting. | * allocation if the full request isn't immediately available. | ||||
* See the random_buf size requirements in the Fortuna code. | |||||
*/ | */ | ||||
read_len = roundup(read_len, RANDOM_BLOCKSIZE); | bufsize = MIN(sigchk_period, (size_t)uio->uio_resid); | ||||
/* Work in chunks page-sized or less */ | random_buf = malloc(bufsize, M_ENTROPY, M_NOWAIT); | ||||
read_len = MIN(read_len, PAGE_SIZE); | if (random_buf == NULL) { | ||||
bufsize = PAGE_SIZE; | |||||
random_buf = malloc(bufsize, M_ENTROPY, M_WAITOK); | |||||
} | |||||
error = 0; | |||||
while (uio->uio_resid > 0 && error == 0) { | |||||
read_len = MIN((size_t)uio->uio_resid, bufsize); | |||||
p_random_alg_context->ra_read(random_buf, read_len); | p_random_alg_context->ra_read(random_buf, read_len); | ||||
c = MIN(uio->uio_resid, read_len); | |||||
/* | /* | ||||
* uiomove() may yield the CPU before each 'c' bytes | * uiomove() may yield the CPU before each 'read_len' bytes (up | ||||
* (up to PAGE_SIZE) are copied out. | * to bufsize) are copied out. | ||||
*/ | */ | ||||
error = uiomove(random_buf, c, uio); | error = uiomove(random_buf, read_len, uio); | ||||
total_read += c; | 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(&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; | ||||
} | } | ||||
} | } | ||||
/* | |||||
* Short reads due to signal interrupt should not indicate error. | |||||
* Instead, the uio will reflect that the read was shorter than | |||||
* requested. | |||||
*/ | |||||
if (error == ERESTART || error == EINTR) | if (error == ERESTART || error == EINTR) | ||||
error = 0; | error = 0; | ||||
} | |||||
explicit_bzero(random_buf, bufsize); | |||||
free(random_buf, M_ENTROPY); | free(random_buf, M_ENTROPY); | ||||
return (error); | return (error); | ||||
} | } | ||||
/*- | /*- | ||||
* Kernel API version of read_random(). This is similar to read_random_uio(), | * Kernel API version of read_random(). This is similar to read_random_uio(), | ||||
* except it doesn't interface with uio(9). It cannot assumed that random_buf | * except it doesn't interface with uio(9). It cannot assumed that random_buf | ||||
* is a multiple of RANDOM_BLOCKSIZE bytes. | * is a multiple of RANDOM_BLOCKSIZE bytes. | ||||
* | * | ||||
* 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) | ||||
{ | { | ||||
u_int read_directly_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; | ||||
/* (Un)Blocking logic */ | /* (Un)Blocking logic */ | ||||
Show All 12 Lines | if (random_bypass_before_seeding) { | ||||
/* Avoid potentially leaking stack garbage */ | /* Avoid potentially leaking stack garbage */ | ||||
memset(random_buf, 0, len); | memset(random_buf, 0, len); | ||||
return; | return; | ||||
} | } | ||||
(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); | ||||
* The underlying generator expects multiples of | |||||
* RANDOM_BLOCKSIZE. | |||||
*/ | |||||
read_directly_len = rounddown(len, RANDOM_BLOCKSIZE); | |||||
if (read_directly_len > 0) | |||||
p_random_alg_context->ra_read(random_buf, read_directly_len); | |||||
if (read_directly_len < len) { | |||||
uint8_t remainder_buf[RANDOM_BLOCKSIZE]; | |||||
p_random_alg_context->ra_read(remainder_buf, | |||||
sizeof(remainder_buf)); | |||||
memcpy((char *)random_buf + read_directly_len, remainder_buf, | |||||
len - read_directly_len); | |||||
explicit_bzero(remainder_buf, sizeof(remainder_buf)); | |||||
} | |||||
} | } | ||||
bool | bool | ||||
IS_RANDOM_SEEDED(void) | IS_RANDOM_SEEDED(void) | ||||
{ | { | ||||
return (p_random_alg_context->ra_seeded()); | return (p_random_alg_context->ra_seeded()); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 191 Lines • Show Last 20 Lines |