github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/syz-hub/state/state_test.go (about)

     1  // Copyright 2016 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 state
     5  
     6  import (
     7  	"sort"
     8  	"testing"
     9  
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/google/syzkaller/pkg/rpctype"
    12  )
    13  
    14  type TestState struct {
    15  	t     *testing.T
    16  	dir   string
    17  	state *State
    18  }
    19  
    20  func MakeTestState(t *testing.T) *TestState {
    21  	t.Parallel()
    22  	dir := t.TempDir()
    23  	state, err := Make(dir)
    24  	if err != nil {
    25  		t.Fatalf("failed to make state: %v", err)
    26  	}
    27  	return &TestState{t, dir, state}
    28  }
    29  
    30  func (ts *TestState) Reload() {
    31  	ts.state.Flush()
    32  	state, err := Make(ts.dir)
    33  	if err != nil {
    34  		ts.t.Fatalf("failed to make state: %v", err)
    35  	}
    36  	ts.state = state
    37  }
    38  
    39  func (ts *TestState) Connect(name, domain string, fresh bool, calls []string, corpus [][]byte) {
    40  	ts.t.Helper()
    41  	if err := ts.state.Connect(name, domain, fresh, calls, corpus); err != nil {
    42  		ts.t.Fatalf("Connect failed: %v", err)
    43  	}
    44  }
    45  
    46  func (ts *TestState) Sync(name string, add [][]byte, del []string) (string, []rpctype.HubInput, int) {
    47  	ts.t.Helper()
    48  	domain, inputs, pending, err := ts.state.Sync(name, add, del)
    49  	if err != nil {
    50  		ts.t.Fatalf("Sync failed: %v", err)
    51  	}
    52  	sort.Slice(inputs, func(i, j int) bool {
    53  		if inputs[i].Domain != inputs[j].Domain {
    54  			return inputs[i].Domain < inputs[j].Domain
    55  		}
    56  		return string(inputs[i].Prog) < string(inputs[j].Prog)
    57  	})
    58  	return domain, inputs, pending
    59  }
    60  
    61  func (ts *TestState) AddRepro(name string, repro []byte) {
    62  	ts.t.Helper()
    63  	if err := ts.state.AddRepro(name, repro); err != nil {
    64  		ts.t.Fatalf("AddRepro failed: %v", err)
    65  	}
    66  }
    67  
    68  func (ts *TestState) PendingRepro(name string) []byte {
    69  	ts.t.Helper()
    70  	repro, err := ts.state.PendingRepro(name)
    71  	if err != nil {
    72  		ts.t.Fatalf("PendingRepro failed: %v", err)
    73  	}
    74  	return repro
    75  }
    76  
    77  func TestBasic(t *testing.T) {
    78  	st := MakeTestState(t)
    79  
    80  	if _, _, _, err := st.state.Sync("foo", nil, nil); err == nil {
    81  		t.Fatalf("synced with unconnected manager")
    82  	}
    83  	calls := []string{"read", "write"}
    84  	st.Connect("foo", "", false, calls, nil)
    85  	st.Sync("foo", nil, nil)
    86  }
    87  
    88  func TestRepro(t *testing.T) {
    89  	st := MakeTestState(t)
    90  
    91  	st.Connect("foo", "", false, []string{"open", "read", "write"}, nil)
    92  	st.Connect("bar", "", false, []string{"open", "read", "close"}, nil)
    93  
    94  	expectPendingRepro := func(name, result string) {
    95  		t.Helper()
    96  		repro := st.PendingRepro(name)
    97  		if string(repro) != result {
    98  			t.Fatalf("got %q, want %q", string(repro), result)
    99  		}
   100  	}
   101  	expectPendingRepro("foo", "")
   102  	expectPendingRepro("bar", "")
   103  	st.AddRepro("foo", []byte("open()"))
   104  	expectPendingRepro("foo", "")
   105  	expectPendingRepro("bar", "open()")
   106  	expectPendingRepro("bar", "")
   107  
   108  	// This repro is already present.
   109  	st.AddRepro("bar", []byte("open()"))
   110  	st.AddRepro("bar", []byte("read()"))
   111  	st.AddRepro("bar", []byte("open()\nread()"))
   112  	// This does not satisfy foo's call set.
   113  	st.AddRepro("bar", []byte("close()"))
   114  	expectPendingRepro("bar", "")
   115  
   116  	// Check how persistence works.
   117  	st.Reload()
   118  	st.Connect("foo", "", false, []string{"open", "read", "write"}, nil)
   119  	st.Connect("bar", "", false, []string{"open", "read", "close"}, nil)
   120  	expectPendingRepro("bar", "")
   121  	expectPendingRepro("foo", "read()")
   122  	expectPendingRepro("foo", "open()\nread()")
   123  	expectPendingRepro("foo", "")
   124  }
   125  
   126  func TestDomain(t *testing.T) {
   127  	st := MakeTestState(t)
   128  
   129  	st.Connect("client0", "", false, []string{"open"}, nil)
   130  	st.Connect("client1", "domain1", false, []string{"open"}, nil)
   131  	st.Connect("client2", "domain2", false, []string{"open"}, nil)
   132  	st.Connect("client3", "domain3", false, []string{"open"}, nil)
   133  	{
   134  		domain, inputs, pending := st.Sync("client0", [][]byte{[]byte("open(0x0)")}, nil)
   135  		if domain != "" || len(inputs) != 0 || pending != 0 {
   136  			t.Fatalf("bad sync result: %v, %v, %v", domain, inputs, pending)
   137  		}
   138  	}
   139  	{
   140  		domain, inputs, pending := st.Sync("client0", [][]byte{[]byte("open(0x1)")}, nil)
   141  		if domain != "" || len(inputs) != 0 || pending != 0 {
   142  			t.Fatalf("bad sync result: %v, %v, %v", domain, inputs, pending)
   143  		}
   144  	}
   145  	{
   146  		domain, inputs, pending := st.Sync("client1", [][]byte{[]byte("open(0x2)"), []byte("open(0x1)")}, nil)
   147  		if domain != "domain1" || pending != 0 {
   148  			t.Fatalf("bad sync result: %v, %v, %v", domain, inputs, pending)
   149  		}
   150  		if diff := cmp.Diff(inputs, []rpctype.HubInput{
   151  			{Domain: "", Prog: []byte("open(0x0)")},
   152  		}); diff != "" {
   153  			t.Fatal(diff)
   154  		}
   155  	}
   156  	{
   157  		_, inputs, _ := st.Sync("client2", [][]byte{[]byte("open(0x3)")}, nil)
   158  		if diff := cmp.Diff(inputs, []rpctype.HubInput{
   159  			{Domain: "", Prog: []byte("open(0x0)")},
   160  			{Domain: "domain1", Prog: []byte("open(0x1)")},
   161  			{Domain: "domain1", Prog: []byte("open(0x2)")},
   162  		}); diff != "" {
   163  			t.Fatal(diff)
   164  		}
   165  	}
   166  	{
   167  		_, inputs, _ := st.Sync("client0", nil, nil)
   168  		if diff := cmp.Diff(inputs, []rpctype.HubInput{
   169  			{Domain: "domain1", Prog: []byte("open(0x2)")},
   170  			{Domain: "domain2", Prog: []byte("open(0x3)")},
   171  		}); diff != "" {
   172  			t.Fatal(diff)
   173  		}
   174  	}
   175  	st.Reload()
   176  	st.Connect("client3", "domain3", false, []string{"open"}, nil)
   177  	{
   178  		_, inputs, _ := st.Sync("client3", nil, nil)
   179  		if diff := cmp.Diff(inputs, []rpctype.HubInput{
   180  			{Domain: "", Prog: []byte("open(0x0)")},
   181  			{Domain: "domain1", Prog: []byte("open(0x1)")},
   182  			{Domain: "domain1", Prog: []byte("open(0x2)")},
   183  			{Domain: "domain2", Prog: []byte("open(0x3)")},
   184  		}); diff != "" {
   185  			t.Fatal(diff)
   186  		}
   187  	}
   188  }