github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/rand_test.go (about)

     1  // Copyright 2018 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 prog
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"math/rand"
    10  	"sort"
    11  	"testing"
    12  )
    13  
    14  func TestNotEscaping(t *testing.T) {
    15  	target, err := GetTarget("test", "64")
    16  	if err != nil {
    17  		t.Fatal(err)
    18  	}
    19  	r := newRand(target, rand.NewSource(0))
    20  	s := &state{
    21  		files: map[string]bool{"./file0": true},
    22  	}
    23  	bound := 1000000
    24  	if testing.Short() {
    25  		bound = 1000
    26  	}
    27  	for i := 0; i < bound; i++ {
    28  		fn := r.filenameImpl(s)
    29  		if escapingFilename(fn) {
    30  			t.Errorf("sandbox escaping file name %q", fn)
    31  		}
    32  	}
    33  }
    34  
    35  func TestDeterminism(t *testing.T) {
    36  	target, rs, iters := initTest(t)
    37  	iters /= 10 // takes too long
    38  	ct := target.DefaultChoiceTable()
    39  	var corpus []*Prog
    40  	for i := 0; i < iters; i++ {
    41  		seed := rs.Int63()
    42  		rs1 := rand.NewSource(seed)
    43  		p1 := generateProg(t, target, rs1, ct, corpus)
    44  		rs2 := rand.NewSource(seed)
    45  		p2 := generateProg(t, target, rs2, ct, corpus)
    46  		ps1 := string(p1.Serialize())
    47  		ps2 := string(p2.Serialize())
    48  		r1 := rs1.Int63()
    49  		r2 := rs2.Int63()
    50  		if r1 != r2 || ps1 != ps2 {
    51  			t.Errorf("seed=%v\nprog 1 (%v):\n%v\nprog 2 (%v):\n%v", seed, r1, ps1, r2, ps2)
    52  		}
    53  		corpus = append(corpus, p1)
    54  	}
    55  }
    56  
    57  func generateProg(t *testing.T, target *Target, rs rand.Source, ct *ChoiceTable, corpus []*Prog) *Prog {
    58  	p := target.Generate(rs, 5, ct)
    59  	p.Mutate(rs, 10, ct, nil, corpus)
    60  	for i, c := range p.Calls {
    61  		comps := make(CompMap)
    62  		for v := range extractValues(c) {
    63  			comps.AddComp(v, v+1)
    64  			comps.AddComp(v, v+10)
    65  		}
    66  		// If unbounded, this code may take O(N^2) time to complete.
    67  		// Since large programs are not uncommon, let's limit the number of hint iterations.
    68  		limit := 100
    69  		p.MutateWithHints(i, comps, func(p1 *Prog) bool {
    70  			p = p1.Clone()
    71  			limit--
    72  			return limit > 0
    73  		})
    74  	}
    75  	for _, crash := range []bool{false, true} {
    76  		p, _ = Minimize(p, -1, crash, func(*Prog, int) bool {
    77  			return rs.Int63()%10 == 0
    78  		})
    79  	}
    80  	data := p.Serialize()
    81  	var err error
    82  	p, err = target.Deserialize(data, NonStrict)
    83  	if err != nil {
    84  		t.Fatal(err)
    85  	}
    86  	return p
    87  }
    88  
    89  // Checks that a generated program contains only enabled syscalls.
    90  func TestEnabledCalls(t *testing.T) {
    91  	target, rs, iters := initTest(t)
    92  	enabledCalls := make(map[string]bool)
    93  	enabled := make(map[*Syscall]bool)
    94  	rnd := rand.New(rs)
    95  	for i := 0; i < len(target.Syscalls)/10; i++ {
    96  		c := target.Syscalls[rnd.Intn(len(target.Syscalls))]
    97  		enabled[c] = true
    98  		enabledCalls[c.Name] = true
    99  	}
   100  	c := target.SyscallMap["clock_gettime"]
   101  	enabled[c] = true
   102  	enabledCalls[c.Name] = true
   103  
   104  	ct := target.BuildChoiceTable(nil, enabled)
   105  	const tries = 10
   106  	for i := 0; i < tries; i++ {
   107  		p := target.Generate(rs, 50, ct)
   108  		for it := 0; it < iters/tries; it++ {
   109  			p.Mutate(rs, 50, ct, nil, nil)
   110  		}
   111  		for _, c := range p.Calls {
   112  			if _, ok := enabledCalls[c.Meta.Name]; !ok {
   113  				t.Fatalf("program contains a syscall that is not enabled: %v", c.Meta.Name)
   114  			}
   115  		}
   116  	}
   117  }
   118  
   119  func TestSizeGenerateConstArg(t *testing.T) {
   120  	target, rs, iters := initRandomTargetTest(t, "test", "64")
   121  	r := newRand(target, rs)
   122  	ForeachType(target.Syscalls, func(typ Type, ctx *TypeCtx) {
   123  		if _, ok := typ.(*IntType); !ok {
   124  			return
   125  		}
   126  		bits := typ.TypeBitSize()
   127  		limit := uint64(1<<bits - 1)
   128  		for i := 0; i < iters; i++ {
   129  			newArg, _ := typ.generate(r, nil, ctx.Dir)
   130  			newVal := newArg.(*ConstArg).Val
   131  			if newVal > limit {
   132  				t.Fatalf("invalid generated value: %d. (arg bitsize: %d; max value: %d)", newVal, bits, limit)
   133  			}
   134  		}
   135  	})
   136  }
   137  
   138  func TestFlags(t *testing.T) {
   139  	// This test does not test anything, it just prints resulting
   140  	// distribution of values for different scenarios.
   141  	tests := []struct {
   142  		vv      []uint64
   143  		bitmask bool
   144  		old     uint64
   145  	}{
   146  		{[]uint64{0, 1, 2, 3}, false, 0},
   147  		{[]uint64{0, 1, 2, 3}, false, 2},
   148  		{[]uint64{1, 2, 3, 4}, false, 0},
   149  		{[]uint64{1, 2, 3, 4}, false, 2},
   150  		{[]uint64{1, 2, 4, 8}, true, 0},
   151  		{[]uint64{1, 2, 4, 8}, true, 2},
   152  		{[]uint64{7}, false, 0},
   153  		{[]uint64{7}, false, 7},
   154  		{[]uint64{1, 2}, true, 0},
   155  		{[]uint64{1, 2}, true, 2},
   156  	}
   157  	target, rs, _ := initRandomTargetTest(t, "test", "64")
   158  	r := newRand(target, rs)
   159  	for _, test := range tests {
   160  		results := make(map[uint64]uint64)
   161  		const throws = 1e4
   162  		for i := 0; i < throws; i++ {
   163  			var v uint64
   164  			for {
   165  				v = r.flags(test.vv, test.bitmask, test.old)
   166  				if test.old == 0 || test.old != v {
   167  					break
   168  				}
   169  			}
   170  			if v > 100 {
   171  				v = 999 // to not print all possible random values we generated
   172  			}
   173  			results[v]++
   174  		}
   175  		var sorted [][2]uint64
   176  		for v, c := range results {
   177  			sorted = append(sorted, [2]uint64{v, c})
   178  		}
   179  		sort.Slice(sorted, func(i, j int) bool {
   180  			return sorted[i][0] < sorted[j][0]
   181  		})
   182  		buf := new(bytes.Buffer)
   183  		for _, p := range sorted {
   184  			fmt.Fprintf(buf, "%v\t%v\n", p[0], p[1])
   185  		}
   186  		t.Logf("test: vv=%+v bitmask=%v old=%v\nvalue\ttimes (out of %v)\n%v",
   187  			test.vv, test.bitmask, test.old, throws, buf.String())
   188  	}
   189  }
   190  
   191  func TestTruncateToBitSize(t *testing.T) {
   192  	tests := []struct{ v, bits, res uint64 }{
   193  		{0, 1, 0},
   194  		{1, 1, 1},
   195  		{0x123, 4, 0x3},
   196  		{0xabc, 4, 0xc},
   197  		{0x123, 8, 0x23},
   198  		{0xabc, 8, 0xbc},
   199  		{0x12345678abcdabcd, 64, 0x12345678abcdabcd},
   200  		{0xf2345678abcdabcd, 64, 0xf2345678abcdabcd},
   201  	}
   202  	for i, test := range tests {
   203  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   204  			res := truncateToBitSize(test.v, test.bits)
   205  			if res != test.res {
   206  				t.Fatalf("truncateToBitSize(0x%x, %v)=0x%x, want 0x%x", test.v, test.bits, res, test.res)
   207  			}
   208  		})
   209  	}
   210  }
   211  
   212  // Checks that a generated program does not contain any "no_generate" syscalls.
   213  func TestNoGenerate(t *testing.T) {
   214  	target, rs, iters := initTest(t)
   215  
   216  	// Enable all "no_generate" syscalls and ~10% of all others.
   217  	enabled := make(map[*Syscall]bool)
   218  	rnd := newRand(target, rs)
   219  	for i := 0; i < len(target.Syscalls); i++ {
   220  		syscall := target.Syscalls[rnd.Intn(len(target.Syscalls))]
   221  		if syscall.Attrs.NoGenerate || rnd.oneOf(10) {
   222  			enabled[syscall] = true
   223  		}
   224  	}
   225  	c := target.SyscallMap["clock_gettime"]
   226  	enabled[c] = true
   227  	ct := target.BuildChoiceTable(nil, enabled)
   228  
   229  	const tries = 10
   230  	for i := 0; i < tries; i++ {
   231  		p := target.Generate(rs, 50, ct)
   232  		for it := 0; it < iters/tries; it++ {
   233  			p.Mutate(rs, 50, ct, nil, nil)
   234  		}
   235  		for _, c := range p.Calls {
   236  			if c.Meta.Attrs.NoGenerate {
   237  				t.Fatalf("program contains a no_generate syscall: %v", c.Meta.Name)
   238  			}
   239  		}
   240  	}
   241  }