Source file src/crypto/rand/rand_getrandom.go

     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 rand
     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 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  

View as plain text