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  }