• Jason A. Donenfeld's avatar
    random: introduce generic vDSO getrandom() implementation · 4ad10a5f
    Jason A. Donenfeld authored
    Provide a generic C vDSO getrandom() implementation, which operates on
    an opaque state returned by vgetrandom_alloc() and produces random bytes
    the same way as getrandom(). This has the following API signature:
    
      ssize_t vgetrandom(void *buffer, size_t len, unsigned int flags,
                         void *opaque_state, size_t opaque_len);
    
    The return value and the first three arguments are the same as ordinary
    getrandom(), while the last two arguments are a pointer to the opaque
    allocated state and its size. Were all five arguments passed to the
    getrandom() syscall, nothing different would happen, and the functions
    would have the exact same behavior.
    
    The actual vDSO RNG algorithm implemented is the same one implemented by
    drivers/char/random.c, using the same fast-erasure techniques as that.
    Should the in-kernel implementation change, so too will the vDSO one.
    
    It requires an implementation of ChaCha20 that does not use any stack,
    in order to maintain forward secrecy if a multi-threaded program forks
    (though this does not account for a similar issue with SA_SIGINFO
    copying registers to the stack), so this is left as an
    architecture-specific fill-in. Stack-less ChaCha20 is an easy algorithm
    to implement on a variety of architectures, so this shouldn't be too
    onerous.
    
    Initially, the state is keyless, and so the first call makes a
    getrandom() syscall to generate that key, and then uses it for
    subsequent calls. By keeping track of a generation counter, it knows
    when its key is invalidated and it should fetch a new one using the
    syscall. Later, more than just a generation counter might be used.
    
    Since MADV_WIPEONFORK is set on the opaque state, the key and related
    state is wiped during a fork(), so secrets don't roll over into new
    processes, and the same state doesn't accidentally generate the same
    random stream. The generation counter, as well, is always >0, so that
    the 0 counter is a useful indication of a fork() or otherwise
    uninitialized state.
    
    If the kernel RNG is not yet initialized, then the vDSO always calls the
    syscall, because that behavior cannot be emulated in userspace, but
    fortunately that state is short lived and only during early boot. If it
    has been initialized, then there is no need to inspect the `flags`
    argument, because the behavior does not change post-initialization
    regardless of the `flags` value.
    
    Since the opaque state passed to it is mutated, vDSO getrandom() is not
    reentrant, when used with the same opaque state, which libc should be
    mindful of.
    
    The function works over an opaque per-thread state of a particular size,
    which must be marked VM_WIPEONFORK, VM_DONTDUMP, VM_NORESERVE, and
    VM_DROPPABLE for proper operation. Over time, the nuances of these
    allocations may change or grow or even differ based on architectural
    features.
    
    The opaque state passed to vDSO getrandom() must be allocated using the
    mmap_flags and mmap_prot parameters provided by the vgetrandom_opaque_params
    struct, which also contains the size of each state. That struct can be
    obtained with a call to vgetrandom(NULL, 0, 0, &params, ~0UL). Then,
    libc can call mmap(2) and slice up the returned array into a state per
    each thread, while ensuring that no single state straddles a page
    boundary. Libc is expected to allocate a chunk of these on first use,
    and then dole them out to threads as they're created, allocating more
    when needed.
    
    vDSO getrandom() provides the ability for userspace to generate random
    bytes quickly and safely, and is intended to be integrated into libc's
    thread management. As an illustrative example, the introduced code in
    the vdso_test_getrandom self test later in this series might be used to
    do the same outside of libc. In a libc the various pthread-isms are
    expected to be elided into libc internals.
    Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
    Signed-off-by: default avatarJason A. Donenfeld <Jason@zx2c4.com>
    4ad10a5f
Kconfig 965 Bytes