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