Skip to content

Commit

Permalink
virtio-rng support (#1732)
Browse files Browse the repository at this point in the history
This introduces support for the virtio entropy device. When available, this
device will serve as the main source of entropy for the random number
generator. A hardware entropy source (e.g. rdseed and rdrand on x86-64) serves
as a fallback option when a virtio entropy device is not found.

Entropy data is stored in a double-buffered fashion with the intent to keep
entropy available for randomize functions without the need to block. Should an
underrun in entropy data occur, the aforementioned fallback option is used to
fulfill the request.

Other changes include adding a mutex to guard chacha20 usage and splitting
large getrandom(2) requests into smaller chunks to avoid starving other work.
  • Loading branch information
wjhun authored May 31, 2022
1 parent d4e2a57 commit 6092477
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 47 deletions.
4 changes: 3 additions & 1 deletion platform/pc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ SRCS-kernel.elf= \
$(SRCDIR)/virtio/virtio_mmio.c \
$(SRCDIR)/virtio/virtio_net.c \
$(SRCDIR)/virtio/virtio_pci.c \
$(SRCDIR)/virtio/virtio_rng.c \
$(SRCDIR)/virtio/virtio_storage.c \
$(SRCDIR)/virtio/virtio_scsi.c \
$(SRCDIR)/virtio/virtqueue.c \
Expand Down Expand Up @@ -408,6 +409,7 @@ endif
ifneq ($(ENABLE_BALLOON),)
QEMU_BALLOON= -device virtio-balloon-pci
endif
QEMU_RNG= -device virtio-rng-pci
ifneq ($(ENABLE_QMP),)
QEMU_QMP= -qmp unix:$(ROOTDIR)/qmp-sock,server,nowait
#QEMU_QMP= -qmp tcp:localhost:4444,server,nowait
Expand All @@ -418,7 +420,7 @@ QEMU_FLAGS=
#QEMU_FLAGS+= -d int -D int.log
#QEMU_FLAGS+= -s -S

QEMU_COMMON= $(QEMU_MACHINE) $(QEMU_MEMORY) $(QEMU_BALLOON) $(QEMU_DISPLAY) $(QEMU_PCI) $(QEMU_SERIAL) $(QEMU_STORAGE) -device isa-debug-exit -no-reboot $(QEMU_FLAGS) $(QEMU_QMP)
QEMU_COMMON= $(QEMU_MACHINE) $(QEMU_MEMORY) $(QEMU_BALLOON) $(QEMU_DISPLAY) $(QEMU_PCI) $(QEMU_RNG) $(QEMU_SERIAL) $(QEMU_STORAGE) -device isa-debug-exit -no-reboot $(QEMU_FLAGS) $(QEMU_QMP)

run: image
$(QEMU) $(QEMU_COMMON) $(QEMU_USERNET) $(QEMU_ACCEL) || exit $$(($$?>>1))
Expand Down
5 changes: 3 additions & 2 deletions platform/pc/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ static boolean hw_seed(u64 * seed, boolean rdseed)
return false;
}

u64 random_seed(void)
u64 hw_get_seed(void)
{
u64 seed = 0;
if (have_rdseed && hw_seed(&seed, true))
return seed;
if (have_rdrand && hw_seed(&seed, false))
return seed;
return (u64)now(CLOCK_ID_MONOTONIC_RAW);
return (u64)now(CLOCK_ID_REALTIME);
}

static void init_hwrand(void)
Expand Down Expand Up @@ -579,4 +579,5 @@ void detect_devices(kernel_heaps kh, storage_attach sa)
init_acpi(kh);

init_virtio_balloon(kh);
init_virtio_rng(kh);
}
4 changes: 3 additions & 1 deletion platform/riscv-virt/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ SRCS-kernel.elf= \
$(SRCDIR)/virtio/virtio_mmio.c \
$(SRCDIR)/virtio/virtio_net.c \
$(SRCDIR)/virtio/virtio_pci.c \
$(SRCDIR)/virtio/virtio_rng.c \
$(SRCDIR)/virtio/virtio_scsi.c \
$(SRCDIR)/virtio/virtio_storage.c \
$(SRCDIR)/virtio/virtqueue.c \
Expand Down Expand Up @@ -333,6 +334,7 @@ QEMU_USERNET= -device $(NETWORK)$(NETWORK_BUS),netdev=n0 -netdev user,id=n0,host
ifneq ($(ENABLE_BALLOON),)
QEMU_BALLOON= -device virtio-balloon-pci
endif
QEMU_RNG= -device virtio-rng-pci
ifneq ($(ENABLE_QMP),)
QEMU_QMP= -qmp unix:$(ROOTDIR)/qmp-sock,server,nowait
#QEMU_QMP= -qmp tcp:localhost:4444,server,nowait
Expand All @@ -347,7 +349,7 @@ endif

#QEMU_FLAGS+= -monitor telnet:127.0.0.1:9999,server,nowait

QEMU_COMMON= $(QEMU_MACHINE) $(QEMU_MEMORY) $(QEMU_BALLOON) $(QEMU_KERNEL) $(QEMU_DISPLAY) $(QEMU_PCI) $(QEMU_SERIAL) $(QEMU_STORAGE) -no-reboot $(QEMU_FLAGS) $(QEMU_QMP)
QEMU_COMMON= $(QEMU_MACHINE) $(QEMU_MEMORY) $(QEMU_BALLOON) $(QEMU_KERNEL) $(QEMU_DISPLAY) $(QEMU_PCI) $(QEMU_RNG) $(QEMU_SERIAL) $(QEMU_STORAGE) -no-reboot $(QEMU_FLAGS) $(QEMU_QMP)

run: image
$(QEMU) $(QEMU_COMMON) $(QEMU_USERNET) $(QEMU_ACCEL)
Expand Down
6 changes: 3 additions & 3 deletions platform/riscv-virt/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@
#define init_dump(p, len)
#endif

u64 random_seed(void)
u64 hw_get_seed(void)
{
// XXX add qemu random device? virtio-rng-device?
return rdtsc();
return (u64)now(CLOCK_ID_REALTIME);
}

extern void *START, *END;
Expand Down Expand Up @@ -199,4 +198,5 @@ void detect_devices(kernel_heaps kh, storage_attach sa)
init_virtio_blk(kh, sa);
init_virtio_scsi(kh, sa);
init_virtio_balloon(kh);
init_virtio_rng(kh);
}
4 changes: 3 additions & 1 deletion platform/virt/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ SRCS-kernel.elf= \
$(SRCDIR)/virtio/virtio_mmio.c \
$(SRCDIR)/virtio/virtio_net.c \
$(SRCDIR)/virtio/virtio_pci.c \
$(SRCDIR)/virtio/virtio_rng.c \
$(SRCDIR)/virtio/virtio_scsi.c \
$(SRCDIR)/virtio/virtio_storage.c \
$(SRCDIR)/virtio/virtqueue.c \
Expand Down Expand Up @@ -402,6 +403,7 @@ QEMU_USERNET= -device $(NETWORK)$(NETWORK_BUS),netdev=n0 -netdev user,id=n0,host
ifneq ($(ENABLE_BALLOON),)
QEMU_BALLOON= -device virtio-balloon-pci
endif
QEMU_RNG= -device virtio-rng-pci
ifneq ($(ENABLE_QMP),)
QEMU_QMP= -qmp unix:$(ROOTDIR)/qmp-sock,server,nowait
#QEMU_QMP= -qmp tcp:localhost:4444,server,nowait
Expand All @@ -419,7 +421,7 @@ QEMU_FLAGS+= -semihosting

#QEMU_FLAGS+= -monitor telnet:127.0.0.1:9999,server,nowait

QEMU_COMMON= $(QEMU_MACHINE) $(QEMU_MEMORY) $(QEMU_BALLOON) $(QEMU_KERNEL) $(QEMU_DISPLAY) $(QEMU_PCI) $(QEMU_SERIAL) $(QEMU_STORAGE) -no-reboot $(QEMU_FLAGS) $(QEMU_QMP)
QEMU_COMMON= $(QEMU_MACHINE) $(QEMU_MEMORY) $(QEMU_BALLOON) $(QEMU_KERNEL) $(QEMU_DISPLAY) $(QEMU_PCI) $(QEMU_RNG) $(QEMU_SERIAL) $(QEMU_STORAGE) -no-reboot $(QEMU_FLAGS) $(QEMU_QMP)

run: image
$(QEMU) $(QEMU_COMMON) $(QEMU_USERNET) $(QEMU_ACCEL)
Expand Down
5 changes: 3 additions & 2 deletions platform/virt/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

BSS_RO_AFTER_INIT struct uefi_boot_params boot_params;

u64 random_seed(void)
u64 hw_get_seed(void)
{
#if 0 // gcc not taking +rng feature modifier...encode manually?
if (field_from_u64(read_psr(ID_AA64ISAR0_EL1), ID_AA64ISAR0_EL1_RNDR)
Expand All @@ -38,7 +38,7 @@ u64 random_seed(void)
}
#endif
/* likely not a good fallback - look for another */
return rdtsc();
return (u64)now(CLOCK_ID_REALTIME);
}

static void uefi_mem_map_iterate(uefi_mem_map mem_map, range_handler h)
Expand Down Expand Up @@ -378,4 +378,5 @@ void detect_devices(kernel_heaps kh, storage_attach sa)
init_virtio_scsi(kh, sa);
init_nvme(kh, sa);
init_virtio_balloon(kh);
init_virtio_rng(kh);
}
2 changes: 1 addition & 1 deletion src/kernel/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ void kernel_runtime_init(kernel_heaps kh)

/* RNG, stack canaries */
init_debug("RNG");
init_random();
init_random(locked);
__stack_chk_guard_init();

/* networking */
Expand Down
88 changes: 66 additions & 22 deletions src/runtime/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@
*
*/

#ifdef KERNEL
#include <kernel.h>
#define chacha20_lock() do { mutex_lock(chacha20inst.m); } while (0)
#define chacha20_unlock() do { mutex_unlock(chacha20inst.m); } while (0)
#else
#include <runtime.h>
#define chacha20_lock()
#define chacha20_unlock()
#endif
#include <crypto/chacha.h>

/*
Expand All @@ -39,29 +47,55 @@
#define CHACHA20_KEYBYTES 32
#define CHACHA20_BUFFER_SIZE 64

struct chacha20_s {
static struct chacha20_s {
#ifdef KERNEL
mutex m;
#endif
int numbytes;
u64 t_reseed;
u8 m_buffer[CHACHA20_BUFFER_SIZE];
struct chacha_ctx ctx;
};
} chacha20inst;

extern u64 random_seed();
/* entropy source mux - for any rng, not just chacha */
bytes (*preferred_get_seed)(void *seed, bytes len);

static bytes fallback_get_seed(void *seed, bytes len)
{
bytes n, remain;
u64 s;
remain = len;
while (remain > 0) {
n = MIN(sizeof(s), remain);
s = hw_get_seed();
runtime_memcpy(seed, &s, n);
seed += n;
remain -= n;
}
return len;
}

/* draw from preferred entropy source or resort to fallback */
void get_seed_complete(void *seed, bytes len)
{
while (len > 0) {
bytes s = preferred_get_seed ? preferred_get_seed(seed, len) : 0;
if (s == 0)
s = fallback_get_seed(seed, len);
seed += s;
assert(len >= s);
len -= s;
}
}

/*
* Mix up the current context.
*/
static void
chacha20_randomstir(struct chacha20_s *chacha20, timestamp t)
chacha20_randomstir_locked(struct chacha20_s *chacha20, timestamp t)
{
u8 key[CHACHA20_KEYBYTES];
u64 seed;
assert(sizeof(key) % sizeof(seed) == 0);
for (int i = 0; i < sizeof(key); i += sizeof(seed)) {
seed = random_seed();
*(u64 *) (key + i) = seed;
i += sizeof(seed);
}
get_seed_complete(key, CHACHA20_KEYBYTES);

u64 now_sec = sec_from_timestamp(t);
u64 now_usec = usec_from_timestamp(truncate_seconds(t));
Expand All @@ -73,26 +107,29 @@ chacha20_randomstir(struct chacha20_s *chacha20, timestamp t)
chacha20->numbytes = 0;
}

// should be per-CPU structure
static struct chacha20_s chacha20inst;

void init_random()
void init_random(heap h)
{
#ifdef KERNEL
chacha20inst.m = allocate_mutex(h, 2048);
assert(chacha20inst.m != INVALID_ADDRESS);
#endif
assert(CHACHA20_KEYBYTES*8 >= CHACHA_MINKEYLEN);
chacha20_randomstir(&chacha20inst, now(CLOCK_ID_MONOTONIC_RAW));
chacha20_lock();
chacha20_randomstir_locked(&chacha20inst, now(CLOCK_ID_MONOTONIC_RAW));
chacha20_unlock();
}

void
arc4rand(void *ptr, bytes len)
void arc4rand(void *ptr, bytes len)
{
struct chacha20_s *chacha20 = &chacha20inst;
bytes length;
u8 *p;

chacha20_lock();
timestamp t = now(CLOCK_ID_MONOTONIC_RAW);
u64 now_sec = sec_from_timestamp(t);
if ((chacha20->numbytes > CHACHA20_RESEED_BYTES) || (now_sec > chacha20->t_reseed))
chacha20_randomstir(chacha20, t);
chacha20_randomstir_locked(chacha20, t);

p = ptr;
while (len) {
Expand All @@ -102,15 +139,15 @@ arc4rand(void *ptr, bytes len)
len -= length;
chacha20->numbytes += length;
if (chacha20->numbytes > CHACHA20_RESEED_BYTES) {
chacha20_randomstir(chacha20, t);
chacha20_randomstir_locked(chacha20, t);
}
}
chacha20_unlock();
}

u64 random_u64()
u64 random_u64(void)
{
u64 retval;

arc4rand(&retval, sizeof(retval));
return retval;
}
Expand All @@ -120,3 +157,10 @@ u64 random_buffer(buffer b)
arc4rand(buffer_ref(b, 0), buffer_length(b));
return buffer_length(b);
}

void random_reseed(void)
{
chacha20_lock();
chacha20_randomstir_locked(&chacha20inst, now(CLOCK_ID_MONOTONIC_RAW));
chacha20_unlock();
}
11 changes: 8 additions & 3 deletions src/runtime/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,15 @@ parser tuple_parser(heap h, parse_finish c, parse_error err);
parser value_parser(heap h, parse_finish c, parse_error err);
parser parser_feed (parser p, buffer b);

// RNG
void init_random();
u64 random_u64();
/* RNG */
void init_random(heap h);
u64 hw_get_seed(void);
extern bytes (*preferred_get_seed)(void *seed, bytes len);
void get_seed_complete(void *seed, bytes len);

u64 random_u64(void);
u64 random_buffer(buffer b);
void random_reseed(void);

typedef struct signature {
u64 s[4];
Expand Down
38 changes: 34 additions & 4 deletions src/unix/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -1051,10 +1051,31 @@ sysreturn creat(const char *pathname, int mode)
return rv;
}

sysreturn getrandom(void *buf, u64 buflen, unsigned int flags)
/* small enough to not exhaust entropy resources without scheduling */
#define GETRANDOM_MAX_BUFLEN (1ull << 20)

static inline void fill_random(void *buf, u64 buflen)
{
buffer b;
random_buffer(alloca_wrap_buffer(buf, buflen));
}

closure_function(3, 0, void, getrandom_deferred,
void *, buf, u64, buflen, u64, written)
{
u64 len = MIN(GETRANDOM_MAX_BUFLEN, bound(buflen) - bound(written));
fill_random(bound(buf) + bound(written), len);
bound(written) += len;
if (bound(written) < bound(buflen)) {
assert(enqueue_irqsafe(runqueue, closure_self()));
kern_yield();
} else {
syscall_return(current, bound(written));
closure_finish();
}
}

sysreturn getrandom(void *buf, u64 buflen, unsigned int flags)
{
if (!buflen)
return set_syscall_error(current, EINVAL);

Expand All @@ -1064,8 +1085,17 @@ sysreturn getrandom(void *buf, u64 buflen, unsigned int flags)
if (flags & ~(GRND_NONBLOCK | GRND_RANDOM))
return set_syscall_error(current, EINVAL);

b = alloca_wrap_buffer(buf, buflen);
return random_buffer(b);
u64 n = MIN(GETRANDOM_MAX_BUFLEN, buflen);
fill_random(buf, n);

if (n < buflen) {
thunk t = contextual_closure(getrandom_deferred, buf, buflen, n);
assert(t != INVALID_ADDRESS);
assert(enqueue_irqsafe(runqueue, t));
/* not really sleeping */
thread_maybe_sleep_uninterruptible(current);
}
return buflen;
}

static int try_write_dirent(void *dirp, boolean dirent64, char *p,
Expand Down
4 changes: 2 additions & 2 deletions src/unix_process/unix_process_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ clock_now platform_monotonic_now;
void *malloc(size_t size);
void free(void *ptr);

u64 random_seed()
u64 hw_get_seed()
{
return random();
}
Expand Down Expand Up @@ -178,7 +178,7 @@ heap init_process_runtime()
{
heap h = malloc_allocator();
platform_monotonic_now = closure(h, unix_now);
init_random();
init_random(h);
init_runtime(h, h);
init_tuples(allocate_tagged_region(h, tag_table_tuple));
init_symbols(allocate_tagged_region(h, tag_symbol), h);
Expand Down
1 change: 1 addition & 0 deletions src/virtio/virtio.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
void init_virtio_balloon(kernel_heaps kh);
void init_virtio_blk(kernel_heaps kh, storage_attach a);
void init_virtio_network(kernel_heaps kh);
void init_virtio_rng(kernel_heaps kh);
void init_virtio_scsi(kernel_heaps kh, storage_attach a);

void virtio_mmio_parse(kernel_heaps kh, const char *str, int len);
Loading

0 comments on commit 6092477

Please sign in to comment.