github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/utils/utils_linux.go (about) 1 // Copyright (c) 2018 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package utils 7 8 import ( 9 "bufio" 10 "crypto/rand" 11 "fmt" 12 "io" 13 "math/big" 14 "os" 15 "strings" 16 "syscall" 17 "unsafe" 18 19 "golang.org/x/sys/unix" 20 ) 21 22 var ioctlFunc = Ioctl 23 24 // maxUInt represents the maximum valid value for the context ID. 25 // The upper 32 bits of the CID are reserved and zeroed. 26 // See http://stefanha.github.io/virtio/ 27 var maxUInt uint64 = 1<<32 - 1 28 29 func Ioctl(fd uintptr, request, data uintptr) error { 30 if _, _, errno := unix.Syscall(unix.SYS_IOCTL, fd, request, data); errno != 0 { 31 //uintptr(request) 32 //uintptr(unsafe.Pointer(&arg1)), 33 //); errno != 0 { 34 return os.NewSyscallError("ioctl", fmt.Errorf("%d", int(errno))) 35 } 36 37 return nil 38 } 39 40 // FindContextID finds a unique context ID by generating a random number between 3 and max unsigned int (maxUint). 41 // Using the ioctl VHOST_VSOCK_SET_GUEST_CID, findContextID asks to the kernel if the given 42 // context ID (N) is available, when the context ID is not available, incrementing by 1 findContextID 43 // iterates from N to maxUint until an available context ID is found, otherwise decrementing by 1 44 // findContextID iterates from N to 3 until an available context ID is found, this is the last chance 45 // to find a context ID available. 46 // On success vhost file and a context ID greater or equal than 3 are returned, otherwise 0 and an error are returned. 47 // vhost file can be used to send vhost file decriptor to QEMU. It's the caller's responsibility to 48 // close vhost file descriptor. 49 // 50 // Benefits of using random context IDs: 51 // - Reduce the probability of a *DoS attack*, since other processes don't know whatis the initial context ID 52 // used by findContextID to find a context ID available 53 // 54 func FindContextID() (*os.File, uint64, error) { 55 // context IDs 0x0, 0x1 and 0x2 are reserved, 0x3 is the first context ID usable. 56 var firstContextID uint64 = 0x3 57 var contextID = firstContextID 58 59 // Generate a random number 60 n, err := rand.Int(rand.Reader, big.NewInt(int64(maxUInt))) 61 if err == nil && n.Int64() >= int64(firstContextID) { 62 contextID = uint64(n.Int64()) 63 } 64 65 // Open vhost-vsock device to check what context ID is available. 66 // This file descriptor holds/locks the context ID and it should be 67 // inherited by QEMU process. 68 vsockFd, err := os.OpenFile(VHostVSockDevicePath, syscall.O_RDWR, 0666) 69 if err != nil { 70 return nil, 0, err 71 } 72 73 // Looking for the first available context ID. 74 for cid := contextID; cid <= maxUInt; cid++ { 75 if err = ioctlFunc(vsockFd.Fd(), ioctlVhostVsockSetGuestCid, uintptr(unsafe.Pointer(&cid))); err == nil { 76 return vsockFd, cid, nil 77 } 78 } 79 80 ioctlVhostVsockSetGuestCid := getIoctlVhostVsockGuestCid() 81 // Last chance to get a free context ID. 82 for cid := contextID - 1; cid >= firstContextID; cid-- { 83 if err = ioctlFunc(vsockFd.Fd(), ioctlVhostVsockSetGuestCid, uintptr(unsafe.Pointer(&cid))); err == nil { 84 return vsockFd, cid, nil 85 } 86 } 87 88 vsockFd.Close() 89 return nil, 0, fmt.Errorf("Could not get a unique context ID for the vsock : %s", err) 90 } 91 92 const ( 93 procMountsFile = "/proc/mounts" 94 95 fieldsPerLine = 6 96 ) 97 98 const ( 99 procDeviceIndex = iota 100 procPathIndex 101 procTypeIndex 102 procOptionIndex 103 ) 104 105 // GetDevicePathAndFsTypeOptions gets the device for the mount point, the file system type 106 // and mount options 107 func GetDevicePathAndFsTypeOptions(mountPoint string) (devicePath, fsType string, fsOptions []string, err error) { 108 if mountPoint == "" { 109 err = fmt.Errorf("Mount point cannot be empty") 110 return 111 } 112 113 var file *os.File 114 115 file, err = os.Open(procMountsFile) 116 if err != nil { 117 return 118 } 119 120 defer file.Close() 121 122 reader := bufio.NewReader(file) 123 for { 124 var line string 125 126 line, err = reader.ReadString('\n') 127 if err == io.EOF { 128 err = fmt.Errorf("Mount %s not found", mountPoint) 129 return 130 } 131 132 fields := strings.Fields(line) 133 if len(fields) != fieldsPerLine { 134 err = fmt.Errorf("Incorrect no of fields (expected %d, got %d)) :%s", fieldsPerLine, len(fields), line) 135 return 136 } 137 138 if mountPoint == fields[procPathIndex] { 139 devicePath = fields[procDeviceIndex] 140 fsType = fields[procTypeIndex] 141 fsOptions = strings.Split(fields[procOptionIndex], ",") 142 return 143 } 144 } 145 }