Index: lib/libc/gen/arc4random.h =================================================================== --- lib/libc/gen/arc4random.h +++ lib/libc/gen/arc4random.h @@ -24,10 +24,18 @@ /* * Stub functions for portability. */ +#include #include +#include /* for sys/vdso.h only. */ +#include +#include +#include +#include #include +static uint64_t *fxrng_root_generationp; + static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; #define _ARC4_LOCK() \ do { \ @@ -54,6 +62,9 @@ struct _rs rs; struct _rsx rsx; } *p; +#ifndef __ILP32__ + int error; +#endif if ((p = mmap(NULL, sizeof(*p), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) @@ -62,13 +73,44 @@ munmap(p, sizeof(*p)); return (-1); } +#ifndef __ILP32__ + error = _elf_aux_info(AT_FXRNG, &fxrng_root_generationp, + sizeof(fxrng_root_generationp)); + /* XXX: Not rolled out to all arch yet. */ + if (error == 0) { + /* + * Need to grab it here on allocation because _rs_init -> + * _rs_allocate is invoked *after* getentropy(3) in _rs_stir(). + */ + p->rsx.rs_seed_generation = + atomic_load_acq_64(fxrng_root_generationp); + } +#endif *rsp = &p->rs; *rsxp = &p->rsx; return (0); } +/* + * This isn't detecting fork; we're just using the existing callback from + * _rs_stir_if_needed() to force arc4random(3) to reseed if the fenestrasX root + * seed version has changed. (That is, the root random(4) has reseeded from + * pooled entropy.) + */ static inline void _rs_forkdetect(void) { +#ifndef __ILP32__ + if (__predict_false(rs == NULL || rsx == NULL)) + return; + if (fxrng_root_generationp == NULL) + return; + if (__predict_true(rsx->rs_seed_generation == + atomic_load_acq_64(fxrng_root_generationp))) + return; + + /* Invalidate rs_buf to force "stir" (reseed). */ + memset(rs, 0, sizeof(*rs)); +#endif } Index: lib/libc/gen/arc4random.c =================================================================== --- lib/libc/gen/arc4random.c +++ lib/libc/gen/arc4random.c @@ -68,6 +68,9 @@ static struct _rsx { chacha_ctx rs_chacha; /* chacha context for random keystream */ u_char rs_buf[RSBUFSZ]; /* keystream blocks */ +#ifdef __FreeBSD__ + uint64_t rs_seed_generation; /* FXRNG seed version */ +#endif } *rsx; static inline int _rs_allocate(struct _rs **, struct _rsx **); @@ -96,6 +99,13 @@ { u_char rnd[KEYSZ + IVSZ]; +#if defined(__FreeBSD__) && !defined(__ILP32__) + if (rsx != NULL && fxrng_root_generationp != NULL) { + rsx->rs_seed_generation = + atomic_load_acq_64(fxrng_root_generationp); + } +#endif + if (getentropy(rnd, sizeof rnd) == -1) _getentropy_fail(); Index: lib/libc/gen/auxv.c =================================================================== --- lib/libc/gen/auxv.c +++ lib/libc/gen/auxv.c @@ -72,6 +72,7 @@ static char *canary, *pagesizes, *execpath; static void *timekeep; static u_long hwcap, hwcap2; +static void *fxrng_seed_version; static void init_aux(void) @@ -125,6 +126,10 @@ case AT_TIMEKEEP: timekeep = aux->a_un.a_ptr; break; + + case AT_FXRNG: + fxrng_seed_version = aux->a_un.a_ptr; + break; } } } @@ -224,6 +229,16 @@ } else res = EINVAL; break; + case AT_FXRNG: + if (buflen == sizeof(void *)) { + if (fxrng_seed_version != NULL) { + *(void **)buf = fxrng_seed_version; + res = 0; + } else + res = ENOENT; + } else + res = EINVAL; + break; default: res = ENOENT; break; Index: sys/amd64/amd64/elf_machdep.c =================================================================== --- sys/amd64/amd64/elf_machdep.c +++ sys/amd64/amd64/elf_machdep.c @@ -74,7 +74,7 @@ .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_ASLR | SV_LP64 | SV_SHP | - SV_TIMEKEEP, + SV_TIMEKEEP | SV_RNG_SEED_VER, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, Index: sys/dev/random/fenestrasX/fx_brng.c =================================================================== --- sys/dev/random/fenestrasX/fx_brng.c +++ sys/dev/random/fenestrasX/fx_brng.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -108,6 +109,8 @@ */ rng->brng_generation++; atomic_store_rel_64(&fxrng_root_generation, rng->brng_generation); + /* Update VDSO version. */ + fxrng_push_seed_generation(rng->brng_generation); FXRNG_BRNG_UNLOCK(rng); } @@ -129,9 +132,26 @@ rng->brng_generation++; atomic_store_rel_64(&fxrng_root_generation, rng->brng_generation); + /* Update VDSO version. */ + fxrng_push_seed_generation(rng->brng_generation); FXRNG_BRNG_UNLOCK(rng); } +/* + * Sysentvec and VDSO are initialized much later than SI_SUB_RANDOM. When + * they're online, go ahead and push an initial root seed version. + * INIT_SYSENTVEC runs at SI_SUB_EXEC:SI_ORDER_ANY, and SI_ORDER_ANY is the + * maximum value, so we must run at SI_SUB_EXEC+1. + */ +static void +fxrng_vdso_sysinit(void *dummy __unused) +{ + FXRNG_BRNG_LOCK(&fxrng_root); + fxrng_push_seed_generation(fxrng_root.brng_generation); + FXRNG_BRNG_UNLOCK(&fxrng_root); +} +SYSINIT(fxrng_vdso, SI_SUB_EXEC + 1, SI_ORDER_ANY, fxrng_vdso_sysinit, NULL); + /* * Grab some bytes off an initialized, current generation RNG. * Index: sys/dev/random/fenestrasX/fx_main.c =================================================================== --- sys/dev/random/fenestrasX/fx_main.c +++ sys/dev/random/fenestrasX/fx_main.c @@ -88,7 +88,8 @@ * a while). * * Not yet implemented, not in scope, or todo: - * - Userspace portions -- shared page, like timehands vdso? + * - Various initial seeding sources we don't have yet + * - In particular, VM migration/copy detection */ #include Index: sys/kern/imgact_elf.c =================================================================== --- sys/kern/imgact_elf.c +++ sys/kern/imgact_elf.c @@ -1368,6 +1368,8 @@ AUXARGS_ENTRY(pos, AT_HWCAP, *imgp->sysent->sv_hwcap); if (imgp->sysent->sv_hwcap2 != NULL) AUXARGS_ENTRY(pos, AT_HWCAP2, *imgp->sysent->sv_hwcap2); + if (imgp->sysent->sv_fxrng_gen_base != 0) + AUXARGS_ENTRY(pos, AT_FXRNG, imgp->sysent->sv_fxrng_gen_base); AUXARGS_ENTRY(pos, AT_NULL, 0); free(imgp->auxargs, M_TEMP); Index: sys/kern/kern_sharedpage.c =================================================================== --- sys/kern/kern_sharedpage.c +++ sys/kern/kern_sharedpage.c @@ -60,6 +60,10 @@ static int shared_page_free; char *shared_page_mapping; +#ifdef RANDOM_FENESTRASX +static uint64_t *fxrng_spage_mapping; +#endif + void shared_page_write(int base, int size, const void *data) { @@ -255,6 +259,36 @@ } #endif +#ifdef RANDOM_FENESTRASX +void +fxrng_push_seed_generation(uint64_t gen) +{ + if (fxrng_spage_mapping != NULL) + atomic_store_rel_64(fxrng_spage_mapping, gen); +} + +static int +alloc_sv_fxrng_generation(void) +{ + static const uint64_t zero; + int base; + + /* + * Allocate a full cache line for the fxrng root generation (64-bit + * counter). It is important that the line is not shared with + * frequently dirtied data, and the shared page allocator lacks a + * __read_mostly mechanism. However, PAGE_SIZE is typically large + * relative to the amount of stuff we've got in it so far, so maybe the + * possible waste isn't an issue. + */ + base = shared_page_alloc(CACHE_LINE_SIZE, CACHE_LINE_SIZE); + KASSERT(base != -1, ("%s: base allocation failed", __func__)); + shared_page_write(base, sizeof(uint64_t), &zero); + fxrng_spage_mapping = (void *)(shared_page_mapping + base); + return (base); +} +#endif + void exec_sysvec_init(void *param) { @@ -286,4 +320,10 @@ } #endif } +#ifdef RANDOM_FENESTRASX + if ((sv->sv_flags & SV_RNG_SEED_VER) != 0) { + sv->sv_fxrng_gen_base = sv->sv_shared_page_base + + alloc_sv_fxrng_generation(); + } +#endif } Index: sys/sys/elf_common.h =================================================================== --- sys/sys/elf_common.h +++ sys/sys/elf_common.h @@ -962,8 +962,9 @@ #define AT_EHDRFLAGS 24 /* e_flags field from elf hdr */ #define AT_HWCAP 25 /* CPU feature flags. */ #define AT_HWCAP2 26 /* CPU feature flags 2. */ +#define AT_FXRNG 27 /* Pointer to root RNG seed version. */ -#define AT_COUNT 27 /* Count of defined aux entry types. */ +#define AT_COUNT 28 /* Count of defined aux entry types. */ /* * Relocation types. Index: sys/sys/sysent.h =================================================================== --- sys/sys/sysent.h +++ sys/sys/sysent.h @@ -136,6 +136,7 @@ int (*sv_trap)(struct thread *); u_long *sv_hwcap; /* Value passed in AT_HWCAP. */ u_long *sv_hwcap2; /* Value passed in AT_HWCAP2. */ + vm_offset_t sv_fxrng_gen_base; }; #define SV_ILP32 0x000100 /* 32-bit executable. */ @@ -146,6 +147,7 @@ #define SV_CAPSICUM 0x020000 /* Force cap_enter() on startup. */ #define SV_TIMEKEEP 0x040000 /* Shared page timehands. */ #define SV_ASLR 0x080000 /* ASLR allowed. */ +#define SV_RNG_SEED_VER 0x100000 /* random(4) reseed generation. */ #define SV_ABI_MASK 0xff #define SV_ABI_ERRNO(p, e) ((p)->p_sysent->sv_errsize <= 0 ? e : \ Index: sys/sys/vdso.h =================================================================== --- sys/sys/vdso.h +++ sys/sys/vdso.h @@ -82,6 +82,9 @@ uint32_t sv_timekeep_gen; }; +#ifdef RANDOM_FENESTRASX +void fxrng_push_seed_generation(uint64_t gen); +#endif void timekeep_push_vdso(void); uint32_t tc_fill_vdso_timehands(struct vdso_timehands *vdso_th);