github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/fuzzer/job_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 fuzzer
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/google/syzkaller/pkg/cover"
    11  	"github.com/google/syzkaller/pkg/flatrpc"
    12  	"github.com/google/syzkaller/pkg/fuzzer/queue"
    13  	"github.com/google/syzkaller/pkg/signal"
    14  	"github.com/google/syzkaller/prog"
    15  	"github.com/google/syzkaller/sys/targets"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func TestDeflake(t *testing.T) {
    20  	type Test struct {
    21  		Info triageCall
    22  		Exec func(run uint64) (errno int32, signal []uint64, cover []uint64)
    23  		Runs uint64
    24  	}
    25  	tests := []Test{
    26  		{
    27  			Info: triageCall{
    28  				newSignal: signal.FromRaw([]uint64{0, 1, 2, 3, 4}, 0),
    29  				cover:     cover.FromRaw([]uint64{10, 20}),
    30  			},
    31  			Exec: func(run uint64) (int32, []uint64, []uint64) {
    32  				// For first, we return 1. For second, 2. And so on.
    33  				return 0, []uint64{run}, []uint64{10, 20}
    34  			},
    35  			Runs: 3,
    36  		},
    37  		{
    38  			Info: triageCall{
    39  				newSignal: signal.FromRaw([]uint64{0, 1, 2}, 0),
    40  				// Cover is a union of all coverages.
    41  				cover: cover.FromRaw([]uint64{10, 20, 30, 40, 100}),
    42  				// 0, 2, 6 were in three resuls.
    43  				stableSignal: signal.FromRaw([]uint64{0, 2, 6}, 0),
    44  				// 0, 2 were also in newSignal.
    45  				newStableSignal: signal.FromRaw([]uint64{0, 2, 6}, 0),
    46  			},
    47  			Exec: func(run uint64) (int32, []uint64, []uint64) {
    48  				switch run {
    49  				case 1:
    50  					return 0, []uint64{0, 2, 4, 6, 8}, []uint64{10, 20}
    51  				case 2:
    52  					// This one should be ignored -- it has a different errno.
    53  					return 1, []uint64{0, 1, 2}, []uint64{100}
    54  				case 3:
    55  					return 0, []uint64{0, 2, 4, 6, 8}, []uint64{20, 30}
    56  				case 4:
    57  					return 0, []uint64{0, 2, 6}, []uint64{30, 40}
    58  				}
    59  				panic("unrechable")
    60  			},
    61  			Runs: 4,
    62  		},
    63  		{
    64  			Info: triageCall{
    65  				newSignal:       signal.FromRaw([]uint64{0, 1, 2, 3, 4}, 3),
    66  				cover:           cover.FromRaw([]uint64{10, 20}),
    67  				stableSignal:    signal.FromRaw([]uint64{2}, 0),
    68  				newStableSignal: signal.FromRaw([]uint64{2}, 0),
    69  			},
    70  			Exec: func(run uint64) (int32, []uint64, []uint64) {
    71  				// For first, we return 0 and 1. For second, 1 and 2. And so on.
    72  				return 0, []uint64{run, run + 1}, []uint64{10, 20}
    73  			},
    74  			Runs: 2,
    75  		},
    76  	}
    77  
    78  	target, err := prog.GetTarget(targets.TestOS, targets.TestArch64Fuzz)
    79  	assert.NoError(t, err)
    80  	const anyTestProg = `syz_compare(&AUTO="00000000", 0x4, &AUTO=@conditional={0x0, @void, @void, @void}, AUTO)`
    81  	prog, err := target.Deserialize([]byte(anyTestProg), prog.NonStrict)
    82  	assert.NoError(t, err)
    83  
    84  	for i, test := range tests {
    85  		t.Run(fmt.Sprint(i), func(t *testing.T) {
    86  			info := test.Info
    87  			info.signals[0] = test.Info.newSignal.Copy()
    88  			info.cover = nil
    89  			info.stableSignal = nil
    90  			info.newStableSignal = nil
    91  			testJob := &triageJob{
    92  				p:     prog,
    93  				calls: map[int]*triageCall{0: &info},
    94  				fuzzer: &Fuzzer{
    95  					Cover:  newCover(),
    96  					Config: &Config{},
    97  				},
    98  				info: &JobInfo{},
    99  			}
   100  
   101  			var run uint64
   102  			stop := testJob.deflake(func(_ *queue.Request, _ ProgFlags) *queue.Result {
   103  				run++
   104  				errno, signal, cover := test.Exec(run)
   105  				return &queue.Result{
   106  					Info: &flatrpc.ProgInfo{
   107  						Calls: []*flatrpc.CallInfo{{
   108  							Error:  errno,
   109  							Signal: signal,
   110  							Cover:  cover,
   111  						}},
   112  					},
   113  				}
   114  			})
   115  
   116  			assert.False(t, stop)
   117  			assert.Equal(t, run, test.Runs)
   118  			assert.ElementsMatch(t, info.cover.Serialize(), test.Info.cover.Serialize())
   119  			assert.ElementsMatch(t, info.stableSignal.ToRaw(), test.Info.stableSignal.ToRaw())
   120  			assert.ElementsMatch(t, info.newStableSignal.ToRaw(), test.Info.newStableSignal.ToRaw())
   121  		})
   122  	}
   123  }