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
This commit is contained in:
Ayush Ranjan
2023-10-04 15:46:28 -07:00
committed by gVisor bot
parent c39ecc4eb4
commit e0bdd0d576
2 changed files with 12 additions and 32 deletions
+10 -3
View File
@@ -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.
+2 -29
View File
@@ -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)
}