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

     1  // Copyright 2023 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  // Tests the translation of coverage pcs between fuzzer instances with differing module offsets.
     5  
     6  package cover
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  	"strconv"
    12  	"testing"
    13  )
    14  
    15  type RPCServer struct {
    16  	canonicalModules   *Canonicalizer
    17  	modulesInitialized bool
    18  	fuzzers            map[string]*Fuzzer
    19  }
    20  
    21  type Fuzzer struct {
    22  	instModules *CanonicalizerInstance
    23  	cov         []uint32
    24  	goalCov     []uint32
    25  	bitmap      map[uint32]uint32
    26  	goalBitmap  map[uint32]uint32
    27  	sign        []uint32
    28  	goalSign    []uint32
    29  }
    30  
    31  type canonicalizeValue int
    32  
    33  const (
    34  	Canonicalize canonicalizeValue = iota
    35  	Decanonicalize
    36  )
    37  
    38  // Confirms there is no change to coverage if modules aren't instantiated.
    39  func TestNilModules(t *testing.T) {
    40  	serv := &RPCServer{
    41  		fuzzers: make(map[string]*Fuzzer),
    42  	}
    43  	serv.connect("f1", nil, true)
    44  	serv.connect("f2", nil, true)
    45  
    46  	serv.fuzzers["f1"].cov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000}
    47  	serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000}
    48  
    49  	serv.fuzzers["f2"].cov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000}
    50  	serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000}
    51  
    52  	serv.fuzzers["f1"].bitmap = map[uint32]uint32{
    53  		0x00010011: 1,
    54  		0x00020FFF: 2,
    55  		0x00030000: 3,
    56  		0x00040000: 4,
    57  	}
    58  	serv.fuzzers["f1"].goalBitmap = map[uint32]uint32{
    59  		0x00010011: 1,
    60  		0x00020FFF: 2,
    61  		0x00030000: 3,
    62  		0x00040000: 4,
    63  	}
    64  	serv.fuzzers["f2"].bitmap = map[uint32]uint32{
    65  		0x00010011: 1,
    66  		0x00020FFF: 2,
    67  		0x00030000: 3,
    68  		0x00040000: 4,
    69  	}
    70  	serv.fuzzers["f2"].goalBitmap = map[uint32]uint32{
    71  		0x00010011: 1,
    72  		0x00020FFF: 2,
    73  		0x00030000: 3,
    74  		0x00040000: 4,
    75  	}
    76  
    77  	if err := serv.runTest(Canonicalize); err != "" {
    78  		t.Fatalf("failed in canonicalization: %v", err)
    79  	}
    80  
    81  	serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000}
    82  	serv.fuzzers["f1"].goalSign = serv.fuzzers["f1"].goalCov
    83  	serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000}
    84  	serv.fuzzers["f2"].goalSign = serv.fuzzers["f2"].goalCov
    85  	if err := serv.runTest(Decanonicalize); err != "" {
    86  		t.Fatalf("failed in decanonicalization: %v", err)
    87  	}
    88  }
    89  
    90  // Confirms there is no change to PCs if coverage is disabled and fallback signals are used.
    91  func TestDisabledSignals(t *testing.T) {
    92  	serv := &RPCServer{
    93  		fuzzers: make(map[string]*Fuzzer),
    94  	}
    95  	// Create modules at the specified address offsets.
    96  	f1ModuleAddresses := []uint64{0x00015000, 0x00020000, 0x00030000, 0x00040000, 0x00045000}
    97  	f1ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000}
    98  	f1Modules := initModules(f1ModuleAddresses, f1ModuleSizes)
    99  	serv.connect("f1", f1Modules, false)
   100  
   101  	f2ModuleAddresses := []uint64{0x00015000, 0x00040000, 0x00045000, 0x00020000, 0x00030000}
   102  	f2ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000}
   103  	f2Modules := initModules(f2ModuleAddresses, f2ModuleSizes)
   104  	serv.connect("f2", f2Modules, false)
   105  
   106  	pcs := []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000}
   107  	serv.fuzzers["f1"].cov = pcs
   108  	serv.fuzzers["f1"].goalCov = pcs
   109  
   110  	serv.fuzzers["f2"].sign = pcs
   111  	serv.fuzzers["f2"].goalSign = pcs
   112  
   113  	if err := serv.runTest(Canonicalize); err != "" {
   114  		t.Fatalf("failed in canonicalization: %v", err)
   115  	}
   116  
   117  	serv.fuzzers["f1"].goalSign = pcs
   118  	serv.fuzzers["f2"].goalSign = pcs
   119  	if err := serv.runTest(Decanonicalize); err != "" {
   120  		t.Fatalf("failed in decanonicalization: %v", err)
   121  	}
   122  }
   123  
   124  // Tests coverage conversion when modules are instantiated.
   125  func TestModules(t *testing.T) {
   126  	serv := &RPCServer{
   127  		fuzzers: make(map[string]*Fuzzer),
   128  	}
   129  
   130  	// Create modules at the specified address offsets.
   131  	f1ModuleAddresses := []uint64{0x00015000, 0x00020000, 0x00030000, 0x00040000, 0x00045000}
   132  	f1ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000}
   133  	f1Modules := initModules(f1ModuleAddresses, f1ModuleSizes)
   134  	serv.connect("f1", f1Modules, true)
   135  
   136  	f2ModuleAddresses := []uint64{0x00015000, 0x00040000, 0x00045000, 0x00020000, 0x00030000}
   137  	f2ModuleSizes := []uint64{0x5000, 0x5000, 0x10000, 0x5000, 0x10000}
   138  	f2Modules := initModules(f2ModuleAddresses, f2ModuleSizes)
   139  	serv.connect("f2", f2Modules, true)
   140  
   141  	// f1 is the "canonical" fuzzer as it is first one instantiated.
   142  	// This means that all coverage output should be the same as the inputs.
   143  	serv.fuzzers["f1"].cov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000,
   144  		0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000}
   145  	serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000,
   146  		0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000}
   147  
   148  	// The modules addresss are inverted between: (2 and 4), (3 and 5),
   149  	// affecting the output canonical coverage values in these ranges.
   150  	serv.fuzzers["f2"].cov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000,
   151  		0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000}
   152  	serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00040000, 0x00025000, 0x00045000,
   153  		0x0004a000, 0x00020000, 0x00030000, 0x0003b000, 0x00055000}
   154  
   155  	serv.fuzzers["f1"].bitmap = map[uint32]uint32{
   156  		0x00010011: 1,
   157  		0x00020FFF: 2,
   158  		0x00030000: 3,
   159  		0x00040000: 4,
   160  	}
   161  	serv.fuzzers["f1"].goalBitmap = map[uint32]uint32{
   162  		0x00010011: 1,
   163  		0x00020FFF: 2,
   164  		0x00030000: 3,
   165  		0x00040000: 4,
   166  	}
   167  	serv.fuzzers["f2"].bitmap = map[uint32]uint32{
   168  		0x00010011: 1,
   169  		0x00020FFF: 2,
   170  		0x00030000: 3,
   171  		0x00040000: 4,
   172  	}
   173  	serv.fuzzers["f2"].goalBitmap = map[uint32]uint32{
   174  		0x00010011: 1,
   175  		0x00040FFF: 2,
   176  		0x00045000: 3,
   177  		0x00020000: 4,
   178  	}
   179  
   180  	if err := serv.runTest(Canonicalize); err != "" {
   181  		t.Fatalf("failed in canonicalization: %v", err)
   182  	}
   183  
   184  	serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000,
   185  		0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000}
   186  	serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000,
   187  		0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000}
   188  	if err := serv.runTest(Decanonicalize); err != "" {
   189  		t.Fatalf("failed in decanonicalization: %v", err)
   190  	}
   191  }
   192  
   193  // Tests coverage conversion when modules are added after initialization.
   194  func TestChangingModules(t *testing.T) {
   195  	serv := &RPCServer{
   196  		fuzzers: make(map[string]*Fuzzer),
   197  	}
   198  
   199  	// Create modules at the specified address offsets.
   200  	f1ModuleAddresses := []uint64{0x00015000}
   201  	f1ModuleSizes := []uint64{0x5000}
   202  	f1Modules := initModules(f1ModuleAddresses, f1ModuleSizes)
   203  	serv.connect("f1", f1Modules, true)
   204  
   205  	f2ModuleAddresses := []uint64{0x00015000, 0x00020000}
   206  	f2ModuleSizes := []uint64{0x5000, 0x5000}
   207  	f2Modules := initModules(f2ModuleAddresses, f2ModuleSizes)
   208  	serv.connect("f2", f2Modules, true)
   209  
   210  	// Module 2 is not present in the "canonical" fuzzer, so coverage values
   211  	// in this range should be deleted.
   212  	serv.fuzzers["f2"].cov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000}
   213  	serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00025000}
   214  
   215  	if err := serv.runTest(Canonicalize); err != "" {
   216  		t.Fatalf("failed in canonicalization: %v", err)
   217  	}
   218  
   219  	serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00025000}
   220  	if err := serv.runTest(Decanonicalize); err != "" {
   221  		t.Fatalf("failed in decanonicalization: %v", err)
   222  	}
   223  }
   224  
   225  func (serv *RPCServer) runTest(val canonicalizeValue) string {
   226  	var cov []uint32
   227  	for name, fuzzer := range serv.fuzzers {
   228  		if val == Canonicalize {
   229  			cov = fuzzer.instModules.Canonicalize(fuzzer.cov)
   230  		} else {
   231  			cov = fuzzer.instModules.Decanonicalize(fuzzer.cov)
   232  			instBitmap := fuzzer.instModules.DecanonicalizeFilter(fuzzer.bitmap)
   233  			if !reflect.DeepEqual(instBitmap, fuzzer.goalBitmap) {
   234  				return fmt.Sprintf("failed in bitmap conversion. Fuzzer %v.\nExpected: 0x%x.\nReturned: 0x%x",
   235  					name, fuzzer.goalBitmap, instBitmap)
   236  			}
   237  		}
   238  		if !reflect.DeepEqual(cov, fuzzer.goalCov) {
   239  			return fmt.Sprintf("failed in coverage conversion. Fuzzer %v.\nExpected: 0x%x.\nReturned: 0x%x",
   240  				name, fuzzer.goalCov, cov)
   241  		}
   242  		fuzzer.cov = cov
   243  	}
   244  	return ""
   245  }
   246  
   247  func (serv *RPCServer) connect(name string, modules []KernelModule, flagSignal bool) {
   248  	if !serv.modulesInitialized {
   249  		serv.canonicalModules = NewCanonicalizer(modules, flagSignal)
   250  		serv.modulesInitialized = true
   251  	}
   252  
   253  	serv.fuzzers[name] = &Fuzzer{
   254  		instModules: serv.canonicalModules.NewInstance(modules),
   255  	}
   256  }
   257  
   258  func initModules(addrs, sizes []uint64) []KernelModule {
   259  	var modules []KernelModule
   260  	for idx, addr := range addrs {
   261  		modules = append(modules, KernelModule{
   262  			Name: strconv.FormatInt(int64(idx), 10),
   263  			Addr: addr,
   264  			Size: sizes[idx],
   265  		})
   266  	}
   267  	return modules
   268  }