github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/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/cover" 26 "github.com/google/syzkaller/pkg/flatrpc" 27 "github.com/google/syzkaller/pkg/fuzzer/queue" 28 "github.com/google/syzkaller/pkg/mgrconfig" 29 "github.com/google/syzkaller/prog" 30 "github.com/google/syzkaller/sys/targets" 31 ) 32 33 type Checker struct { 34 checker 35 source queue.Source 36 checkContext *checkContext 37 } 38 39 func New(cfg *mgrconfig.Config) *Checker { 40 var impl checker 41 switch cfg.TargetOS { 42 case targets.Linux: 43 impl = new(linux) 44 case targets.NetBSD: 45 impl = new(netbsd) 46 case targets.OpenBSD: 47 impl = new(openbsd) 48 default: 49 impl = new(stub) 50 } 51 ctx := context.Background() 52 executor := queue.Plain() 53 return &Checker{ 54 checker: impl, 55 source: queue.Deduplicate(ctx, executor), 56 checkContext: newCheckContext(ctx, cfg, impl, executor), 57 } 58 } 59 60 func (checker *Checker) MachineInfo(fileInfos []*flatrpc.FileInfo) ([]cover.KernelModule, []byte, error) { 61 files := createVirtualFilesystem(fileInfos) 62 modules, err := checker.parseModules(files) 63 if err != nil { 64 return nil, nil, err 65 } 66 info := new(bytes.Buffer) 67 tmp := new(bytes.Buffer) 68 for _, fn := range checker.machineInfos() { 69 tmp.Reset() 70 name, err := fn(files, tmp) 71 if err != nil { 72 if !os.IsNotExist(err) { 73 return nil, nil, err 74 } 75 continue 76 } 77 if tmp.Len() == 0 { 78 continue 79 } 80 fmt.Fprintf(info, "[%v]\n%s\n%v\n\n", name, tmp.Bytes(), strings.Repeat("-", 80)) 81 } 82 return modules, info.Bytes(), nil 83 } 84 85 func (checker *Checker) CheckFiles() []string { 86 return checker.checkFiles() 87 } 88 89 func (checker *Checker) Run(files []*flatrpc.FileInfo, featureInfos []*flatrpc.FeatureInfo) ( 90 map[*prog.Syscall]bool, map[*prog.Syscall]string, Features, error) { 91 ctx := checker.checkContext 92 checker.checkContext = nil 93 ctx.start(files) 94 return ctx.wait(featureInfos) 95 } 96 97 // Implementation of the queue.Source interface. 98 func (checker *Checker) Next() *queue.Request { 99 return checker.source.Next() 100 } 101 102 var _ queue.Source = &Checker{} 103 104 type machineInfoFunc func(files filesystem, w io.Writer) (string, error) 105 106 type checker interface { 107 RequiredFiles() []string 108 checkFiles() []string 109 parseModules(files filesystem) ([]cover.KernelModule, error) 110 machineInfos() []machineInfoFunc 111 syscallCheck(*checkContext, *prog.Syscall) string 112 } 113 114 type filesystem map[string]*flatrpc.FileInfo 115 116 func createVirtualFilesystem(fileInfos []*flatrpc.FileInfo) filesystem { 117 files := make(filesystem) 118 for _, file := range fileInfos { 119 if file.Exists { 120 files[file.Name] = file 121 } 122 } 123 return files 124 } 125 126 func (files filesystem) ReadFile(name string) ([]byte, error) { 127 file, ok := files[name] 128 if !ok { 129 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} 130 } 131 if file.Error != "" { 132 return nil, errors.New(file.Error) 133 } 134 return file.Data, nil 135 } 136 137 func (files filesystem) ReadDir(dir string) []string { 138 var res []string 139 dedup := make(map[string]bool) 140 for _, file := range files { 141 if len(file.Name) < len(dir)+2 || 142 !strings.HasPrefix(file.Name, dir) || 143 file.Name[len(dir)] != '/' { 144 continue 145 } 146 name := file.Name[len(dir)+1:] 147 if slash := strings.Index(name, "/"); slash != -1 { 148 name = name[:slash] 149 } 150 if dedup[name] { 151 continue 152 } 153 dedup[name] = true 154 res = append(res, name) 155 } 156 return res 157 } 158 159 type stub int 160 161 func (stub) RequiredFiles() []string { 162 return nil 163 } 164 165 func (stub) checkFiles() []string { 166 return nil 167 } 168 169 func (stub) parseModules(files filesystem) ([]cover.KernelModule, error) { 170 return nil, nil 171 } 172 173 func (stub) machineInfos() []machineInfoFunc { 174 return nil 175 } 176 177 func (stub) syscallCheck(*checkContext, *prog.Syscall) string { 178 return "" 179 }