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 }