github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/vminfo/vminfo_test.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
     5  
     6  import (
     7  	"context"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/google/syzkaller/pkg/flatrpc"
    16  	"github.com/google/syzkaller/pkg/fuzzer/queue"
    17  	"github.com/google/syzkaller/prog"
    18  	_ "github.com/google/syzkaller/sys"
    19  	"github.com/google/syzkaller/sys/targets"
    20  )
    21  
    22  func TestHostMachineInfo(t *testing.T) {
    23  	checker, files := hostChecker(t)
    24  	dups := make(map[string]bool)
    25  	for _, file := range files {
    26  		if file.Name[0] != '/' || file.Name[len(file.Name)-1] == '/' || strings.Contains(file.Name, "\\") {
    27  			t.Errorf("malformed file %q", file.Name)
    28  		}
    29  		// Reading duplicate files leads to duplicate work.
    30  		if dups[file.Name] {
    31  			t.Errorf("duplicate file %q", file.Name)
    32  		}
    33  		dups[file.Name] = true
    34  		if file.Error != "" {
    35  			t.Logf("failed to read %q: %s", file.Name, file.Error)
    36  		}
    37  	}
    38  	modules, info, err := checker.MachineInfo(files)
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	t.Logf("machine info:\n%s", info)
    43  	for _, module := range modules {
    44  		t.Logf("module %q: addr 0x%x size %v", module.Name, module.Addr, module.Size)
    45  	}
    46  }
    47  
    48  func TestSyscalls(t *testing.T) {
    49  	t.Parallel()
    50  	for _, arches := range targets.List {
    51  		for _, target := range arches {
    52  			if target.OS == targets.Linux {
    53  				continue // linux has own TestLinuxSyscalls test
    54  			}
    55  			t.Run(target.OS+"/"+target.Arch, func(t *testing.T) {
    56  				t.Parallel()
    57  				cfg := testConfig(t, target.OS, target.Arch)
    58  				checker := New(cfg)
    59  				stop := make(chan struct{})
    60  				go createSuccessfulResults(checker, stop)
    61  				enabled, disabled, _, err := checker.Run(context.Background(), nil, allFeatures())
    62  				close(stop)
    63  				if err != nil {
    64  					t.Fatal(err)
    65  				}
    66  				for call, reason := range disabled {
    67  					t.Errorf("disabled call %v: %v", call.Name, reason)
    68  				}
    69  				if len(enabled) != len(cfg.Syscalls) {
    70  					t.Errorf("enabled only %v calls out of %v", len(enabled), len(cfg.Syscalls))
    71  				}
    72  			})
    73  		}
    74  	}
    75  }
    76  
    77  func allFeatures() []*flatrpc.FeatureInfo {
    78  	var features []*flatrpc.FeatureInfo
    79  	for feat := range flatrpc.EnumNamesFeature {
    80  		features = append(features, &flatrpc.FeatureInfo{
    81  			Id: feat,
    82  		})
    83  	}
    84  	return features
    85  }
    86  
    87  func createSuccessfulResults(source queue.Source, stop chan struct{}) {
    88  	var count int
    89  	for {
    90  		select {
    91  		case <-stop:
    92  			return
    93  		case <-time.After(time.Millisecond):
    94  		}
    95  		req := source.Next()
    96  		if req == nil {
    97  			continue
    98  		}
    99  		count++
   100  		if count > 1000 {
   101  			// This is just a sanity check that we don't do something stupid accidentally.
   102  			// If it grows above the limit intentionally, the limit can be increased.
   103  			// Currently we have 641 (when we failed to properly dedup syscall tests, it was 4349).
   104  			panic("too many test programs")
   105  		}
   106  		res := &queue.Result{
   107  			Status: queue.Success,
   108  		}
   109  		switch req.Type {
   110  		case flatrpc.RequestTypeProgram:
   111  			res.Info = &flatrpc.ProgInfo{}
   112  			for range req.Prog.Calls {
   113  				res.Info.Calls = append(res.Info.Calls, &flatrpc.CallInfo{
   114  					Cover:  []uint64{1},
   115  					Signal: []uint64{1},
   116  					Comps:  []*flatrpc.Comparison{{Op1: 1, Op2: 2}},
   117  				})
   118  			}
   119  		case flatrpc.RequestTypeGlob:
   120  			res.Output = []byte("/some/file\n")
   121  		}
   122  		req.Done(res)
   123  	}
   124  }
   125  
   126  func hostChecker(t *testing.T) (*Checker, []*flatrpc.FileInfo) {
   127  	cfg := testConfig(t, runtime.GOOS, runtime.GOARCH)
   128  	checker := New(cfg)
   129  	files := readFiles(checker.RequiredFiles())
   130  	return checker, files
   131  }
   132  
   133  func testConfig(t *testing.T, OS, arch string) *Config {
   134  	target, err := prog.GetTarget(OS, arch)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  	var syscalls []int
   139  	for id := range target.Syscalls {
   140  		if !target.Syscalls[id].Attrs.Disabled {
   141  			syscalls = append(syscalls, id)
   142  		}
   143  	}
   144  	return &Config{
   145  		Target:   target,
   146  		Features: flatrpc.AllFeatures,
   147  		Sandbox:  flatrpc.ExecEnvSandboxNone,
   148  		Syscalls: syscalls,
   149  	}
   150  }
   151  
   152  func readFiles(files []string) []*flatrpc.FileInfo {
   153  	var res []*flatrpc.FileInfo
   154  	for _, glob := range files {
   155  		glob = filepath.FromSlash(glob)
   156  		if !strings.Contains(glob, "*") {
   157  			res = append(res, readFile(glob))
   158  			continue
   159  		}
   160  		matches, err := filepath.Glob(glob)
   161  		if err != nil {
   162  			res = append(res, &flatrpc.FileInfo{
   163  				Name:  glob,
   164  				Error: err.Error(),
   165  			})
   166  			continue
   167  		}
   168  		for _, file := range matches {
   169  			res = append(res, readFile(file))
   170  		}
   171  	}
   172  	return res
   173  }
   174  
   175  func readFile(file string) *flatrpc.FileInfo {
   176  	data, err := os.ReadFile(file)
   177  	exists, errStr := true, ""
   178  	if err != nil {
   179  		exists, errStr = !os.IsNotExist(err), err.Error()
   180  	}
   181  	return &flatrpc.FileInfo{
   182  		Name:   file,
   183  		Exists: exists,
   184  		Error:  errStr,
   185  		Data:   data,
   186  	}
   187  }