From e0bdd0d57680d5aa5ac7c6aa1fc03954feb48011 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Wed, 4 Oct 2023 15:44:25 -0700 Subject: [PATCH] Ensure at least page size bytes are read from /dev/{u}random and getrandom(2). This undocumented behavior manifests in Linux and some apps depend on it. See drivers/char/random.c:get_random_bytes_user(). Fixes #9445 Fixes #4988 PiperOrigin-RevId: 570833447 --- pkg/rand/rand_linux.go | 13 ++++++++--- pkg/sentry/syscalls/linux/sys_random.go | 31 ++----------------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/pkg/rand/rand_linux.go b/pkg/rand/rand_linux.go index fa6a21026..fd5fa5d6a 100644 --- a/pkg/rand/rand_linux.go +++ b/pkg/rand/rand_linux.go @@ -54,10 +54,17 @@ type bufferedReader struct { // Read implements io.Reader.Read. func (b *bufferedReader) Read(p []byte) (int, error) { + // In Linux, reads of up to page size bytes will always complete fully. + // See drivers/char/random.c:get_random_bytes_user(). + // NOTE(gvisor.dev/issue/9445): Some applications rely on this behavior. + const pageSize = 4096 + min := len(p) + if min > pageSize { + min = pageSize + } b.mu.Lock() - n, err := b.r.Read(p) - b.mu.Unlock() - return n, err + defer b.mu.Unlock() + return io.ReadAtLeast(b.r, p, min) } // Reader is the default reader. diff --git a/pkg/sentry/syscalls/linux/sys_random.go b/pkg/sentry/syscalls/linux/sys_random.go index da5b527dd..1e6698ecf 100644 --- a/pkg/sentry/syscalls/linux/sys_random.go +++ b/pkg/sentry/syscalls/linux/sys_random.go @@ -15,7 +15,6 @@ package linux import ( - "io" "math" "gvisor.dev/gvisor/pkg/errors/linuxerr" @@ -57,37 +56,11 @@ func GetRandom(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintp return 0, nil, linuxerr.EFAULT } - // "If the urandom source has been initialized, reads of up to 256 bytes - // will always return as many bytes as requested and will not be - // interrupted by signals. No such guarantees apply for larger buffer - // sizes." - getrandom(2) - min := int(length) - if min > 256 { - min = 256 - } - n, err := t.MemoryManager().CopyOutFrom(t, hostarch.AddrRangeSeqOf(ar), safemem.FromIOReader{&randReader{-1, min}}, usermem.IOOpts{ + n, err := t.MemoryManager().CopyOutFrom(t, hostarch.AddrRangeSeqOf(ar), safemem.FromIOReader{rand.Reader}, usermem.IOOpts{ AddressSpaceActive: true, }) - if n >= int64(min) { + if n > 0 { return uintptr(n), nil, nil } return 0, nil, err } - -// randReader is a io.Reader that handles partial reads from rand.Reader. -type randReader struct { - done int - min int -} - -// Read implements io.Reader.Read. -func (r *randReader) Read(dst []byte) (int, error) { - if r.done >= r.min { - return rand.Reader.Read(dst) - } - min := r.min - r.done - if min > len(dst) { - min = len(dst) - } - return io.ReadAtLeast(rand.Reader, dst, min) -}