github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/sys_random.go (about)

     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 linux
    16  
    17  import (
    18  	"io"
    19  	"math"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    22  	"github.com/SagerNet/gvisor/pkg/hostarch"
    23  	"github.com/SagerNet/gvisor/pkg/rand"
    24  	"github.com/SagerNet/gvisor/pkg/safemem"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    27  	"github.com/SagerNet/gvisor/pkg/syserror"
    28  	"github.com/SagerNet/gvisor/pkg/usermem"
    29  )
    30  
    31  const (
    32  	_GRND_NONBLOCK = 0x1
    33  	_GRND_RANDOM   = 0x2
    34  )
    35  
    36  // GetRandom implements the linux syscall getrandom(2).
    37  //
    38  // In a multi-tenant/shared environment, the only valid implementation is to
    39  // fetch data from the urandom pool, otherwise starvation attacks become
    40  // possible. The urandom pool is also expected to have plenty of entropy, thus
    41  // the GRND_RANDOM flag is ignored. The GRND_NONBLOCK flag does not apply, as
    42  // the pool will already be initialized.
    43  func GetRandom(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    44  	addr := args[0].Pointer()
    45  	length := args[1].SizeT()
    46  	flags := args[2].Int()
    47  
    48  	// Flags are checked for validity but otherwise ignored. See above.
    49  	if flags & ^(_GRND_NONBLOCK|_GRND_RANDOM) != 0 {
    50  		return 0, nil, linuxerr.EINVAL
    51  	}
    52  
    53  	if length > math.MaxInt32 {
    54  		length = math.MaxInt32
    55  	}
    56  	ar, ok := addr.ToRange(uint64(length))
    57  	if !ok {
    58  		return 0, nil, syserror.EFAULT
    59  	}
    60  
    61  	// "If the urandom source has been initialized, reads of up to 256 bytes
    62  	// will always return as many bytes as requested and will not be
    63  	// interrupted by signals. No such guarantees apply for larger buffer
    64  	// sizes." - getrandom(2)
    65  	min := int(length)
    66  	if min > 256 {
    67  		min = 256
    68  	}
    69  	n, err := t.MemoryManager().CopyOutFrom(t, hostarch.AddrRangeSeqOf(ar), safemem.FromIOReader{&randReader{-1, min}}, usermem.IOOpts{
    70  		AddressSpaceActive: true,
    71  	})
    72  	if n >= int64(min) {
    73  		return uintptr(n), nil, nil
    74  	}
    75  	return 0, nil, err
    76  }
    77  
    78  // randReader is a io.Reader that handles partial reads from rand.Reader.
    79  type randReader struct {
    80  	done int
    81  	min  int
    82  }
    83  
    84  // Read implements io.Reader.Read.
    85  func (r *randReader) Read(dst []byte) (int, error) {
    86  	if r.done >= r.min {
    87  		return rand.Reader.Read(dst)
    88  	}
    89  	min := r.min - r.done
    90  	if min > len(dst) {
    91  		min = len(dst)
    92  	}
    93  	return io.ReadAtLeast(rand.Reader, dst, min)
    94  }