rand_linux.go raw

   1  // Copyright 2018 The gVisor Authors.
   2  //
   3  // Licensed under the Apache License, Version 2.0 (the "License");
   4  // you may not use this file except in compliance with the License.
   5  // You may obtain a copy of the License at
   6  //
   7  //     http://www.apache.org/licenses/LICENSE-2.0
   8  //
   9  // Unless required by applicable law or agreed to in writing, software
  10  // distributed under the License is distributed on an "AS IS" BASIS,
  11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12  // See the License for the specific language governing permissions and
  13  // limitations under the License.
  14  
  15  package rand
  16  
  17  import (
  18  	"bufio"
  19  	"crypto/rand"
  20  	"io"
  21  
  22  	"golang.org/x/sys/unix"
  23  	"gvisor.dev/gvisor/pkg/sync"
  24  )
  25  
  26  // reader implements an io.Reader that returns pseudorandom bytes.
  27  type reader struct {
  28  	once         sync.Once
  29  	useGetrandom bool
  30  }
  31  
  32  // Read implements io.Reader.Read.
  33  func (r *reader) Read(p []byte) (int, error) {
  34  	r.once.Do(func() {
  35  		_, err := unix.Getrandom(p, 0)
  36  		if err != unix.ENOSYS {
  37  			r.useGetrandom = true
  38  		}
  39  	})
  40  
  41  	if r.useGetrandom {
  42  		return unix.Getrandom(p, 0)
  43  	}
  44  	return rand.Read(p)
  45  }
  46  
  47  // bufferedReader implements a threadsafe buffered io.Reader.
  48  type bufferedReader struct {
  49  	mu sync.Mutex
  50  	r  *bufio.Reader
  51  }
  52  
  53  // Read implements io.Reader.Read.
  54  func (b *bufferedReader) Read(p []byte) (int, error) {
  55  	// In Linux, reads of up to page size bytes will always complete fully.
  56  	// See drivers/char/random.c:get_random_bytes_user().
  57  	// NOTE(gvisor.dev/issue/9445): Some applications rely on this behavior.
  58  	const pageSize = 4096
  59  	min := len(p)
  60  	if min > pageSize {
  61  		min = pageSize
  62  	}
  63  	b.mu.Lock()
  64  	defer b.mu.Unlock()
  65  	return io.ReadAtLeast(b.r, p, min)
  66  }
  67  
  68  // Reader is the default reader.
  69  var Reader io.Reader = &bufferedReader{r: bufio.NewReader(&reader{})}
  70  
  71  // Read reads from the default reader.
  72  func Read(b []byte) (int, error) {
  73  	return io.ReadFull(Reader, b)
  74  }
  75  
  76  // Init can be called to make sure /dev/urandom is pre-opened on kernels that
  77  // do not support getrandom(2).
  78  func Init() error {
  79  	p := make([]byte, 1)
  80  	_, err := Read(p)
  81  	return err
  82  }
  83