Home | History | Annotate | Download | only in csource
      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 	"reflect"
      9 	"testing"
     10 )
     11 
     12 func TestParseOptions(t *testing.T) {
     13 	for _, opts := range allOptionsSingle("linux") {
     14 		data := opts.Serialize()
     15 		got, err := DeserializeOptions(data)
     16 		if err != nil {
     17 			t.Fatalf("failed to deserialize %q: %v", data, err)
     18 		}
     19 		if !reflect.DeepEqual(got, opts) {
     20 			t.Fatalf("opts changed, got:\n%+v\nwant:\n%+v", got, opts)
     21 		}
     22 	}
     23 }
     24 
     25 func TestParseOptionsCanned(t *testing.T) {
     26 	// Dashboard stores csource options with syzkaller reproducers,
     27 	// so we need to be able to parse old formats.
     28 	// nolint: lll
     29 	canned := map[string]Options{
     30 		`{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"namespace",
     31 		"fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true,
     32 		"netdev":true,"resetnet":true,
     33 		"segv":true,"waitrepeat":true,"debug":true,"repro":true}`: {
     34 			Threaded:      true,
     35 			Collide:       true,
     36 			Repeat:        true,
     37 			Procs:         10,
     38 			Sandbox:       "namespace",
     39 			Fault:         true,
     40 			FaultCall:     1,
     41 			FaultNth:      2,
     42 			EnableTun:     true,
     43 			UseTmpDir:     true,
     44 			EnableCgroups: true,
     45 			EnableNetdev:  true,
     46 			ResetNet:      true,
     47 			HandleSegv:    true,
     48 			Repro:         true,
     49 		},
     50 		"{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}": {
     51 			Threaded:      true,
     52 			Collide:       true,
     53 			Repeat:        true,
     54 			Procs:         1,
     55 			Sandbox:       "none",
     56 			Fault:         false,
     57 			FaultCall:     -1,
     58 			FaultNth:      0,
     59 			EnableTun:     true,
     60 			UseTmpDir:     true,
     61 			EnableCgroups: false,
     62 			HandleSegv:    true,
     63 			Repro:         false,
     64 		},
     65 		"{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}": {
     66 			Threaded:      true,
     67 			Collide:       true,
     68 			Repeat:        true,
     69 			Procs:         1,
     70 			Sandbox:       "",
     71 			Fault:         false,
     72 			FaultCall:     -1,
     73 			FaultNth:      0,
     74 			EnableTun:     true,
     75 			UseTmpDir:     true,
     76 			EnableCgroups: false,
     77 			HandleSegv:    true,
     78 			Repro:         false,
     79 		},
     80 		"{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}": {
     81 			Threaded:      false,
     82 			Collide:       true,
     83 			Repeat:        true,
     84 			Procs:         1,
     85 			Sandbox:       "namespace",
     86 			Fault:         false,
     87 			FaultCall:     -1,
     88 			FaultNth:      0,
     89 			EnableTun:     true,
     90 			UseTmpDir:     true,
     91 			EnableCgroups: true,
     92 			HandleSegv:    true,
     93 			Repro:         false,
     94 		},
     95 	}
     96 	for data, want := range canned {
     97 		got, err := DeserializeOptions([]byte(data))
     98 		if err != nil {
     99 			t.Fatalf("failed to deserialize %q: %v", data, err)
    100 		}
    101 		if !reflect.DeepEqual(got, want) {
    102 			t.Fatalf("deserialize %q\ngot:\n%+v\nwant:\n%+v", data, got, want)
    103 		}
    104 	}
    105 }
    106 
    107 func allOptionsSingle(OS string) []Options {
    108 	var opts []Options
    109 	fields := reflect.TypeOf(Options{}).NumField()
    110 	for i := 0; i < fields; i++ {
    111 		// Because of constraints on options, we need some defaults
    112 		// (e.g. no collide without threaded).
    113 		opt := Options{
    114 			Threaded:  true,
    115 			Repeat:    true,
    116 			Sandbox:   "none",
    117 			UseTmpDir: true,
    118 		}
    119 		opts = append(opts, enumerateField(OS, opt, i)...)
    120 	}
    121 	return opts
    122 }
    123 
    124 func allOptionsPermutations(OS string) []Options {
    125 	opts := []Options{{}}
    126 	fields := reflect.TypeOf(Options{}).NumField()
    127 	for i := 0; i < fields; i++ {
    128 		var newOpts []Options
    129 		for _, opt := range opts {
    130 			newOpts = append(newOpts, enumerateField(OS, opt, i)...)
    131 		}
    132 		opts = newOpts
    133 	}
    134 	return opts
    135 }
    136 
    137 func enumerateField(OS string, opt Options, field int) []Options {
    138 	var opts []Options
    139 	s := reflect.ValueOf(&opt).Elem()
    140 	fldName := s.Type().Field(field).Name
    141 	fld := s.Field(field)
    142 	if fldName == "Sandbox" {
    143 		for _, sandbox := range []string{"", "none", "setuid", "namespace"} {
    144 			fld.SetString(sandbox)
    145 			opts = append(opts, opt)
    146 		}
    147 	} else if fldName == "Procs" {
    148 		for _, procs := range []int64{1, 4} {
    149 			fld.SetInt(procs)
    150 			opts = append(opts, opt)
    151 		}
    152 	} else if fldName == "RepeatTimes" {
    153 		for _, times := range []int64{0, 10} {
    154 			fld.SetInt(times)
    155 			opts = append(opts, opt)
    156 		}
    157 	} else if fldName == "FaultCall" {
    158 		opts = append(opts, opt)
    159 	} else if fldName == "FaultNth" {
    160 		opts = append(opts, opt)
    161 	} else if fld.Kind() == reflect.Bool {
    162 		for _, v := range []bool{false, true} {
    163 			fld.SetBool(v)
    164 			opts = append(opts, opt)
    165 		}
    166 	} else {
    167 		panic(fmt.Sprintf("field '%v' is not boolean", fldName))
    168 	}
    169 	var checked []Options
    170 	for _, opt := range opts {
    171 		if err := opt.Check(OS); err == nil {
    172 			checked = append(checked, opt)
    173 		}
    174 	}
    175 	return checked
    176 }
    177