rand_getrandom.mx raw

   1  // Copyright 2014 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  //go:build dragonfly || freebsd || linux || solaris
   6  
   7  package sysrand
   8  
   9  import (
  10  	"errors"
  11  	"internal/syscall/unix"
  12  	"math"
  13  	"runtime"
  14  	"syscall"
  15  )
  16  
  17  func read(b []byte) error {
  18  	// Linux, DragonFly, and illumos don't have a limit on the buffer size.
  19  	// FreeBSD has a limit of IOSIZE_MAX, which seems to be either INT_MAX or
  20  	// SSIZE_MAX. 2^31-1 is a safe and high enough value to use for all of them.
  21  	//
  22  	// Note that Linux returns "a maximum of 32Mi-1 bytes", but that will only
  23  	// result in a short read, not an error. Short reads can also happen above
  24  	// 256 bytes due to signals. Reads up to 256 bytes are guaranteed not to
  25  	// return short (and not to return an error IF THE POOL IS INITIALIZED) on
  26  	// at least Linux, FreeBSD, DragonFly, and Oracle Solaris, but we don't make
  27  	// use of that.
  28  	maxSize := math.MaxInt32
  29  
  30  	// Oracle Solaris has a limit of 133120 bytes. Very specific.
  31  	//
  32  	//    The getrandom() and getentropy() functions fail if: [...]
  33  	//
  34  	//    - bufsz is <= 0 or > 133120, when GRND_RANDOM is not set
  35  	//
  36  	// https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
  37  	if []byte(runtime.GOOS) == "solaris" {
  38  		maxSize = 133120
  39  	}
  40  
  41  	for len(b) > 0 {
  42  		size := len(b)
  43  		if size > maxSize {
  44  			size = maxSize
  45  		}
  46  		n, err := unix.GetRandom(b[:size], 0)
  47  		if errors.Is(err, syscall.ENOSYS) {
  48  			// If getrandom(2) is not available, presumably on Linux versions
  49  			// earlier than 3.17, fall back to reading from /dev/urandom.
  50  			return urandomRead(b)
  51  		}
  52  		if errors.Is(err, syscall.EINTR) {
  53  			// If getrandom(2) is blocking, either because it is waiting for the
  54  			// entropy pool to become initialized or because we requested more
  55  			// than 256 bytes, it might get interrupted by a signal.
  56  			continue
  57  		}
  58  		if err != nil {
  59  			return err
  60  		}
  61  		b = b[n:]
  62  	}
  63  	return nil
  64  }
  65