github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/csource/options_test.go (about)

     1  // Copyright 2017 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 csource
     5  
     6  import (
     7  	"fmt"
     8  	"math"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/google/syzkaller/sys/targets"
    13  )
    14  
    15  func TestParseOptions(t *testing.T) {
    16  	for _, opts := range allOptionsSingle(targets.Linux) {
    17  		data := opts.Serialize()
    18  		got, err := DeserializeOptions(data)
    19  		if err != nil {
    20  			t.Fatalf("failed to deserialize %q: %v", data, err)
    21  		}
    22  		if !reflect.DeepEqual(got, opts) {
    23  			t.Fatalf("opts changed, got:\n%+v\nwant:\n%+v", got, opts)
    24  		}
    25  	}
    26  }
    27  
    28  func TestParseOptionsCanned(t *testing.T) {
    29  	// Dashboard stores csource options with syzkaller reproducers,
    30  	// so we need to be able to parse old formats.
    31  	// nolint: lll, dupl
    32  	canned := map[string]Options{
    33  		`{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"namespace",
    34  		"fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true,
    35  		"netdev":true,"resetnet":true,
    36  		"segv":true,"waitrepeat":true,"debug":true,"repro":true}`: {
    37  			Threaded:     true,
    38  			Repeat:       true,
    39  			Procs:        10,
    40  			Slowdown:     1,
    41  			Sandbox:      "namespace",
    42  			NetInjection: true,
    43  			NetDevices:   true,
    44  			NetReset:     true,
    45  			Cgroups:      true,
    46  			BinfmtMisc:   false,
    47  			CloseFDs:     true,
    48  			UseTmpDir:    true,
    49  			HandleSegv:   true,
    50  			LegacyOptions: LegacyOptions{
    51  				Collide:   true,
    52  				Fault:     true,
    53  				FaultCall: 1,
    54  				FaultNth:  2,
    55  			},
    56  		},
    57  		`{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"android",
    58  		"fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true,
    59  		"netdev":true,"resetnet":true,
    60  		"segv":true,"waitrepeat":true,"debug":true,"repro":true}`: {
    61  			Threaded:     true,
    62  			Repeat:       true,
    63  			Procs:        10,
    64  			Slowdown:     1,
    65  			Sandbox:      "android",
    66  			NetInjection: true,
    67  			NetDevices:   true,
    68  			NetReset:     true,
    69  			Cgroups:      true,
    70  			BinfmtMisc:   false,
    71  			CloseFDs:     true,
    72  			UseTmpDir:    true,
    73  			HandleSegv:   true,
    74  			LegacyOptions: LegacyOptions{
    75  				Collide:   true,
    76  				Fault:     true,
    77  				FaultCall: 1,
    78  				FaultNth:  2,
    79  			},
    80  		},
    81  		`{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"android",
    82  		"sandbox_arg":9,"fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true,
    83  		"netdev":true,"resetnet":true,
    84  		"segv":true,"waitrepeat":true,"debug":true,"repro":true}`: {
    85  			Threaded:     true,
    86  			Repeat:       true,
    87  			Procs:        10,
    88  			Slowdown:     1,
    89  			Sandbox:      "android",
    90  			SandboxArg:   9,
    91  			NetInjection: true,
    92  			NetDevices:   true,
    93  			NetReset:     true,
    94  			Cgroups:      true,
    95  			BinfmtMisc:   false,
    96  			CloseFDs:     true,
    97  			UseTmpDir:    true,
    98  			HandleSegv:   true,
    99  			LegacyOptions: LegacyOptions{
   100  				Collide:   true,
   101  				Fault:     true,
   102  				FaultCall: 1,
   103  				FaultNth:  2,
   104  			},
   105  		},
   106  		"{Threaded:true Collide:true Repeat:true Procs:1 Sandbox:none Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
   107  			Threaded:     true,
   108  			Repeat:       true,
   109  			Procs:        1,
   110  			Slowdown:     1,
   111  			Sandbox:      "none",
   112  			NetInjection: true,
   113  			Cgroups:      false,
   114  			BinfmtMisc:   false,
   115  			CloseFDs:     true,
   116  			UseTmpDir:    true,
   117  			HandleSegv:   true,
   118  			LegacyOptions: LegacyOptions{
   119  				Collide:   true,
   120  				Fault:     false,
   121  				FaultCall: -1,
   122  				FaultNth:  0,
   123  			},
   124  		},
   125  		"{Threaded:true Collide:true Repeat:true Procs:1 Sandbox: Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
   126  			Threaded:     true,
   127  			Repeat:       true,
   128  			Procs:        1,
   129  			Slowdown:     1,
   130  			Sandbox:      "",
   131  			NetInjection: true,
   132  			Cgroups:      false,
   133  			BinfmtMisc:   false,
   134  			CloseFDs:     true,
   135  			UseTmpDir:    true,
   136  			HandleSegv:   true,
   137  			LegacyOptions: LegacyOptions{
   138  				Collide:   true,
   139  				Fault:     false,
   140  				FaultCall: -1,
   141  				FaultNth:  0,
   142  			},
   143  		},
   144  		"{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
   145  			Threaded:     false,
   146  			Repeat:       true,
   147  			Procs:        1,
   148  			Slowdown:     1,
   149  			Sandbox:      "namespace",
   150  			NetInjection: true,
   151  			Cgroups:      true,
   152  			BinfmtMisc:   false,
   153  			CloseFDs:     true,
   154  			UseTmpDir:    true,
   155  			HandleSegv:   true,
   156  			LegacyOptions: LegacyOptions{
   157  				Collide:   true,
   158  				Fault:     false,
   159  				FaultCall: -1,
   160  				FaultNth:  0,
   161  			},
   162  		},
   163  		"{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace SandboxArg:-234 Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
   164  			Threaded:     false,
   165  			Repeat:       true,
   166  			Procs:        1,
   167  			Slowdown:     1,
   168  			Sandbox:      "namespace",
   169  			SandboxArg:   -234,
   170  			NetInjection: true,
   171  			Cgroups:      true,
   172  			BinfmtMisc:   false,
   173  			CloseFDs:     true,
   174  			UseTmpDir:    true,
   175  			HandleSegv:   true,
   176  			LegacyOptions: LegacyOptions{
   177  				Collide:   true,
   178  				Fault:     false,
   179  				FaultCall: -1,
   180  				FaultNth:  0,
   181  			},
   182  		},
   183  	}
   184  	for data, want := range canned {
   185  		got, err := DeserializeOptions([]byte(data))
   186  		if err != nil {
   187  			t.Fatalf("failed to deserialize %q: %v", data, err)
   188  		}
   189  		if !reflect.DeepEqual(got, want) {
   190  			t.Fatalf("deserialize %q\ngot:\n%+v\nwant:\n%+v", data, got, want)
   191  		}
   192  	}
   193  }
   194  
   195  func allOptionsSingle(OS string) []Options {
   196  	var opts []Options
   197  	fields := reflect.TypeOf(Options{}).NumField()
   198  	for i := 0; i < fields; i++ {
   199  		// Because of constraints on options, we need some defaults
   200  		// (e.g. no collide without threaded).
   201  		opt := Options{
   202  			Threaded:  true,
   203  			Repeat:    true,
   204  			Sandbox:   "none",
   205  			UseTmpDir: true,
   206  			Slowdown:  1,
   207  		}
   208  		opts = append(opts, enumerateField(OS, opt, i)...)
   209  	}
   210  	return dedup(opts)
   211  }
   212  
   213  func allOptionsPermutations(OS string) []Options {
   214  	opts := []Options{{}}
   215  	fields := reflect.TypeOf(Options{}).NumField()
   216  	for i := 0; i < fields; i++ {
   217  		var newOpts []Options
   218  		for _, opt := range opts {
   219  			newOpts = append(newOpts, enumerateField(OS, opt, i)...)
   220  		}
   221  		opts = newOpts
   222  	}
   223  	return dedup(opts)
   224  }
   225  
   226  func dedup(opts []Options) []Options {
   227  	pos := 0
   228  	dedup := make(map[Options]bool)
   229  	for _, opt := range opts {
   230  		if dedup[opt] {
   231  			continue
   232  		}
   233  		dedup[opt] = true
   234  		opts[pos] = opt
   235  		pos++
   236  	}
   237  	return opts[:pos]
   238  }
   239  
   240  func enumerateField(OS string, opt Options, field int) []Options {
   241  	var opts []Options
   242  	s := reflect.ValueOf(&opt).Elem()
   243  	fldName := s.Type().Field(field).Name
   244  	fld := s.Field(field)
   245  	if fldName == "Sandbox" {
   246  		for _, sandbox := range []string{"", "none", "setuid", "namespace", "android"} {
   247  			fld.SetString(sandbox)
   248  			opts = append(opts, opt)
   249  		}
   250  	} else if fldName == "SandboxArg" {
   251  		for _, sandboxArg := range []int64{math.MinInt, math.MaxInt} {
   252  			fld.SetInt(sandboxArg)
   253  			opts = append(opts, opt)
   254  		}
   255  	} else if fldName == "Procs" {
   256  		for _, procs := range []int64{1, 4} {
   257  			fld.SetInt(procs)
   258  			opts = append(opts, opt)
   259  		}
   260  	} else if fldName == "RepeatTimes" {
   261  		for _, times := range []int64{0, 10} {
   262  			fld.SetInt(times)
   263  			opts = append(opts, opt)
   264  		}
   265  	} else if fldName == "Slowdown" {
   266  		for _, val := range []int64{1, 10} {
   267  			fld.SetInt(val)
   268  			opts = append(opts, opt)
   269  		}
   270  	} else if fldName == "LegacyOptions" {
   271  		opts = append(opts, opt)
   272  	} else if fld.Kind() == reflect.Bool {
   273  		for _, v := range []bool{false, true} {
   274  			fld.SetBool(v)
   275  			opts = append(opts, opt)
   276  		}
   277  	} else {
   278  		panic(fmt.Sprintf("field '%v' is not boolean", fldName))
   279  	}
   280  	var checked []Options
   281  	for _, opt := range opts {
   282  		if err := opt.Check(OS); err == nil {
   283  			checked = append(checked, opt)
   284  		}
   285  	}
   286  	return checked
   287  }
   288  
   289  func TestParseFeaturesFlags(t *testing.T) {
   290  	tests := []struct {
   291  		Enable   string
   292  		Disable  string
   293  		Default  bool
   294  		Features map[string]bool
   295  	}{
   296  		{"none", "none", true, map[string]bool{
   297  			"tun":         true,
   298  			"net_dev":     true,
   299  			"net_reset":   true,
   300  			"cgroups":     true,
   301  			"binfmt_misc": true,
   302  			"close_fds":   true,
   303  			"devlink_pci": true,
   304  			"nic_vf":      true,
   305  			"usb":         true,
   306  			"vhci":        true,
   307  			"wifi":        true,
   308  			"ieee802154":  true,
   309  			"sysctl":      true,
   310  			"swap":        true,
   311  		}},
   312  		{"none", "none", false, map[string]bool{}},
   313  		{"all", "none", true, map[string]bool{
   314  			"tun":         true,
   315  			"net_dev":     true,
   316  			"net_reset":   true,
   317  			"cgroups":     true,
   318  			"binfmt_misc": true,
   319  			"close_fds":   true,
   320  			"devlink_pci": true,
   321  			"nic_vf":      true,
   322  			"usb":         true,
   323  			"vhci":        true,
   324  			"wifi":        true,
   325  			"ieee802154":  true,
   326  			"sysctl":      true,
   327  			"swap":        true,
   328  		}},
   329  		{"", "none", true, map[string]bool{}},
   330  		{"none", "all", true, map[string]bool{}},
   331  		{"none", "", true, map[string]bool{
   332  			"tun":         true,
   333  			"net_dev":     true,
   334  			"net_reset":   true,
   335  			"cgroups":     true,
   336  			"binfmt_misc": true,
   337  			"close_fds":   true,
   338  			"devlink_pci": true,
   339  			"nic_vf":      true,
   340  			"usb":         true,
   341  			"vhci":        true,
   342  			"wifi":        true,
   343  			"ieee802154":  true,
   344  			"sysctl":      true,
   345  			"swap":        true,
   346  		}},
   347  		{"tun,net_dev", "none", true, map[string]bool{
   348  			"tun":     true,
   349  			"net_dev": true,
   350  		}},
   351  		{"none", "cgroups,net_dev", true, map[string]bool{
   352  			"tun":         true,
   353  			"net_reset":   true,
   354  			"binfmt_misc": true,
   355  			"close_fds":   true,
   356  			"devlink_pci": true,
   357  			"nic_vf":      true,
   358  			"usb":         true,
   359  			"vhci":        true,
   360  			"wifi":        true,
   361  			"ieee802154":  true,
   362  			"sysctl":      true,
   363  			"swap":        true,
   364  		}},
   365  		{"close_fds", "none", true, map[string]bool{
   366  			"close_fds": true,
   367  		}},
   368  		{"swap", "none", true, map[string]bool{
   369  			"swap": true,
   370  		}},
   371  	}
   372  	for i, test := range tests {
   373  		features, err := ParseFeaturesFlags(test.Enable, test.Disable, test.Default)
   374  		if err != nil {
   375  			t.Fatalf("failed to parse features flags: %v", err)
   376  		}
   377  		for name, feature := range features {
   378  			if feature.Enabled != test.Features[name] {
   379  				t.Fatalf("test #%v: invalid value for feature flag %s: got %v, want %v",
   380  					i, name, feature.Enabled, test.Features[name])
   381  			}
   382  		}
   383  	}
   384  }