github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/vminfo/vminfo.go (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 // Package vminfo extracts information about the target VM. 5 // The package itself runs on the host, which may be a different OS/arch. 6 // User of the package first requests set of files that needs to be fetched from the VM 7 // and set of test programs that needs to be executed in the VM (Checker.RequiredThings), 8 // then fetches these files and executes test programs, and calls Checker.MachineInfo 9 // to parse the files and extract information about the VM, and optionally calls 10 // Checker.Check to obtain list of enabled/disabled syscalls. 11 // The information includes information about kernel modules and OS-specific info 12 // (for Linux that includes things like parsed /proc/cpuinfo). 13 package vminfo 14 15 import ( 16 "bytes" 17 "context" 18 "errors" 19 "fmt" 20 "io" 21 "io/fs" 22 "os" 23 "strings" 24 25 "github.com/google/syzkaller/pkg/flatrpc" 26 "github.com/google/syzkaller/pkg/fuzzer/queue" 27 "github.com/google/syzkaller/prog" 28 "github.com/google/syzkaller/sys/targets" 29 ) 30 31 type KernelModule struct { 32 Name string 33 Addr uint64 34 Size uint64 35 Path string 36 } 37 38 type Checker struct { 39 checker 40 cfg *Config 41 source queue.Source 42 executor queue.Executor 43 } 44 45 type Config struct { 46 Target *prog.Target 47 VMType string 48 // Set of features to check, missing features won't be checked/enabled after Run. 49 Features flatrpc.Feature 50 // Set of syscalls to check. 51 Syscalls []int 52 Debug bool 53 Cover bool 54 Sandbox flatrpc.ExecEnv 55 SandboxArg int64 56 } 57 58 func New(cfg *Config) *Checker { 59 var impl checker 60 switch cfg.Target.OS { 61 case targets.Linux: 62 impl = &linux{vmType: cfg.VMType} 63 case targets.NetBSD: 64 impl = new(netbsd) 65 case targets.OpenBSD: 66 impl = new(openbsd) 67 default: 68 impl = new(nopChecker) 69 } 70 executor := queue.Plain() 71 return &Checker{ 72 cfg: cfg, 73 checker: impl, 74 executor: executor, 75 source: queue.Deduplicate(executor), 76 } 77 } 78 79 func (checker *Checker) MachineInfo(fileInfos []*flatrpc.FileInfo) ([]*KernelModule, []byte, error) { 80 files := createVirtualFilesystem(fileInfos) 81 modules, err := checker.parseModules(files) 82 if err != nil { 83 return nil, nil, err 84 } 85 info := new(bytes.Buffer) 86 tmp := new(bytes.Buffer) 87 for _, fn := range checker.machineInfos() { 88 tmp.Reset() 89 name, err := fn(files, tmp) 90 if err != nil { 91 if !os.IsNotExist(err) { 92 return nil, nil, err 93 } 94 continue 95 } 96 if tmp.Len() == 0 { 97 continue 98 } 99 fmt.Fprintf(info, "[%v]\n%s\n%v\n\n", name, tmp.Bytes(), strings.Repeat("-", 80)) 100 } 101 return modules, info.Bytes(), nil 102 } 103 104 var ErrAborted = errors.New("aborted through the context") 105 106 func (checker *Checker) Run(ctx context.Context, files []*flatrpc.FileInfo, featureInfos []*flatrpc.FeatureInfo) ( 107 map[*prog.Syscall]bool, map[*prog.Syscall]string, Features, error) { 108 cc := newCheckContext(ctx, checker.cfg, checker.checker, checker.executor) 109 enabled, disabled, features, err := cc.do(files, featureInfos) 110 if ctx.Err() != nil { 111 return nil, nil, nil, ErrAborted 112 } 113 return enabled, disabled, features, err 114 } 115 116 // Implementation of the queue.Source interface. 117 func (checker *Checker) Next() *queue.Request { 118 return checker.source.Next() 119 } 120 121 var _ queue.Source = &Checker{} 122 123 type machineInfoFunc func(files filesystem, w io.Writer) (string, error) 124 125 type checker interface { 126 RequiredFiles() []string 127 CheckFiles() []string 128 parseModules(files filesystem) ([]*KernelModule, error) 129 machineInfos() []machineInfoFunc 130 syscallCheck(*checkContext, *prog.Syscall) string 131 } 132 133 type filesystem map[string]*flatrpc.FileInfo 134 135 func createVirtualFilesystem(fileInfos []*flatrpc.FileInfo) filesystem { 136 files := make(filesystem) 137 for _, file := range fileInfos { 138 if file.Exists { 139 files[file.Name] = file 140 } 141 } 142 return files 143 } 144 145 func (files filesystem) ReadFile(name string) ([]byte, error) { 146 file, ok := files[name] 147 if !ok { 148 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} 149 } 150 if file.Error != "" { 151 return nil, errors.New(file.Error) 152 } 153 return file.Data, nil 154 } 155 156 func (files filesystem) ReadDir(dir string) []string { 157 var res []string 158 dedup := make(map[string]bool) 159 for _, file := range files { 160 if len(file.Name) < len(dir)+2 || 161 !strings.HasPrefix(file.Name, dir) || 162 file.Name[len(dir)] != '/' { 163 continue 164 } 165 name := file.Name[len(dir)+1:] 166 if slash := strings.Index(name, "/"); slash != -1 { 167 name = name[:slash] 168 } 169 if dedup[name] { 170 continue 171 } 172 dedup[name] = true 173 res = append(res, name) 174 } 175 return res 176 } 177 178 type nopChecker int 179 180 func (nopChecker) RequiredFiles() []string { 181 return nil 182 } 183 184 func (nopChecker) CheckFiles() []string { 185 return nil 186 } 187 188 func (nopChecker) parseModules(files filesystem) ([]*KernelModule, error) { 189 return nil, nil 190 } 191 192 func (nopChecker) machineInfos() []machineInfoFunc { 193 return nil 194 } 195 196 func (nopChecker) syscallCheck(*checkContext, *prog.Syscall) string { 197 return "" 198 }