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