github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/cover/canonicalizer.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  package cover
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  
    10  	"github.com/google/syzkaller/pkg/log"
    11  )
    12  
    13  type Canonicalizer struct {
    14  	// Map of modules stored as module name:kernel module.
    15  	modules map[string]KernelModule
    16  
    17  	// Contains a sorted list of the canonical module addresses.
    18  	moduleKeys []uint32
    19  }
    20  
    21  type CanonicalizerInstance struct {
    22  	canonical Canonicalizer
    23  
    24  	// Contains the canonicalize and decanonicalize conversion maps.
    25  	canonicalize   *Convert
    26  	decanonicalize *Convert
    27  }
    28  
    29  // Contains the current conversion maps used.
    30  type Convert struct {
    31  	conversionHash map[uint32]*canonicalizerModule
    32  	moduleKeys     []uint32
    33  }
    34  
    35  type convertContext struct {
    36  	errCount int
    37  	errPC    uint32
    38  	convert  *Convert
    39  }
    40  
    41  // Contains the offset and final address of each module.
    42  type canonicalizerModule struct {
    43  	offset  int
    44  	endAddr uint32
    45  	// Discard coverage from current module.
    46  	// Set to true if module is not present in canonical.
    47  	discard bool
    48  }
    49  
    50  func NewCanonicalizer(modules []KernelModule, flagSignal bool) *Canonicalizer {
    51  	// Return if not using canonicalization.
    52  	if len(modules) == 0 || !flagSignal {
    53  		return &Canonicalizer{}
    54  	}
    55  	// Create a map of canonical module offsets by name.
    56  	canonicalModules := make(map[string]KernelModule)
    57  	for _, module := range modules {
    58  		canonicalModules[module.Name] = module
    59  	}
    60  
    61  	// Store sorted canonical address keys.
    62  	canonicalModuleKeys := make([]uint32, len(modules))
    63  	setModuleKeys(canonicalModuleKeys, modules)
    64  	return &Canonicalizer{
    65  		modules:    canonicalModules,
    66  		moduleKeys: canonicalModuleKeys,
    67  	}
    68  }
    69  
    70  func (can *Canonicalizer) NewInstance(modules []KernelModule) *CanonicalizerInstance {
    71  	if can.moduleKeys == nil {
    72  		return &CanonicalizerInstance{}
    73  	}
    74  	// Save sorted list of module offsets.
    75  	moduleKeys := make([]uint32, len(modules))
    76  	setModuleKeys(moduleKeys, modules)
    77  
    78  	// Create a hash between the "canonical" module addresses and each VM instance.
    79  	instToCanonicalMap := make(map[uint32]*canonicalizerModule)
    80  	canonicalToInstMap := make(map[uint32]*canonicalizerModule)
    81  	for _, module := range modules {
    82  		discard := false
    83  		canonicalAddr := uint32(0)
    84  		canonicalModule, found := can.modules[module.Name]
    85  		if !found || canonicalModule.Size != module.Size {
    86  			log.Errorf("kernel build has changed; instance module %v differs from canonical", module.Name)
    87  			discard = true
    88  		}
    89  		if found {
    90  			canonicalAddr = uint32(canonicalModule.Addr)
    91  		}
    92  
    93  		instAddr := uint32(module.Addr)
    94  
    95  		canonicalToInstMap[canonicalAddr] = &canonicalizerModule{
    96  			offset:  int(instAddr) - int(canonicalAddr),
    97  			endAddr: uint32(module.Size) + canonicalAddr,
    98  			discard: discard,
    99  		}
   100  
   101  		instToCanonicalMap[instAddr] = &canonicalizerModule{
   102  			offset:  int(canonicalAddr) - int(instAddr),
   103  			endAddr: uint32(module.Size) + instAddr,
   104  			discard: discard,
   105  		}
   106  	}
   107  
   108  	return &CanonicalizerInstance{
   109  		canonical: *can,
   110  		canonicalize: &Convert{
   111  			conversionHash: instToCanonicalMap,
   112  			moduleKeys:     moduleKeys,
   113  		},
   114  		decanonicalize: &Convert{
   115  			conversionHash: canonicalToInstMap,
   116  			moduleKeys:     can.moduleKeys,
   117  		},
   118  	}
   119  }
   120  
   121  func (ci *CanonicalizerInstance) Canonicalize(elems []uint32) []uint32 {
   122  	if ci.canonical.moduleKeys == nil {
   123  		return elems
   124  	}
   125  	return ci.canonicalize.convertPCs(elems)
   126  }
   127  
   128  func (ci *CanonicalizerInstance) Decanonicalize(elems []uint32) []uint32 {
   129  	if ci.canonical.moduleKeys == nil {
   130  		return elems
   131  	}
   132  	return ci.decanonicalize.convertPCs(elems)
   133  }
   134  
   135  func (ci *CanonicalizerInstance) DecanonicalizeFilter(bitmap map[uint32]uint32) map[uint32]uint32 {
   136  	// Skip conversion if modules or filter are not used.
   137  	if ci.canonical.moduleKeys == nil || len(bitmap) == 0 {
   138  		return bitmap
   139  	}
   140  	instBitmap := make(map[uint32]uint32)
   141  	convCtx := &convertContext{convert: ci.decanonicalize}
   142  	for pc, val := range bitmap {
   143  		if newPC, ok := ci.decanonicalize.convertPC(pc); ok {
   144  			instBitmap[newPC] = val
   145  		} else {
   146  			convCtx.discard(pc)
   147  		}
   148  	}
   149  	if msg := convCtx.discarded(); msg != "" {
   150  		log.Logf(4, "error in bitmap conversion: %v", msg)
   151  	}
   152  	return instBitmap
   153  }
   154  
   155  // Store sorted list of addresses. Used to binary search when converting PCs.
   156  func setModuleKeys(moduleKeys []uint32, modules []KernelModule) {
   157  	for idx, module := range modules {
   158  		// Truncate PCs to uint32, assuming that they fit into 32 bits.
   159  		// True for x86_64 and arm64 without KASLR.
   160  		moduleKeys[idx] = uint32(module.Addr)
   161  	}
   162  
   163  	// Sort modules by address.
   164  	sort.Slice(moduleKeys, func(i, j int) bool { return moduleKeys[i] < moduleKeys[j] })
   165  }
   166  
   167  func findModule(pc uint32, moduleKeys []uint32) (moduleIdx int) {
   168  	moduleIdx, _ = sort.Find(len(moduleKeys), func(moduleIdx int) int {
   169  		if pc < moduleKeys[moduleIdx] {
   170  			return -1
   171  		}
   172  		return +1
   173  	})
   174  	// Sort.Find returns the index above the correct module.
   175  	return moduleIdx - 1
   176  }
   177  
   178  func (convert *Convert) convertPCs(pcs []uint32) []uint32 {
   179  	// Convert coverage.
   180  	var ret []uint32
   181  	convCtx := &convertContext{convert: convert}
   182  	for _, pc := range pcs {
   183  		if newPC, ok := convert.convertPC(pc); ok {
   184  			ret = append(ret, newPC)
   185  		} else {
   186  			convCtx.discard(pc)
   187  		}
   188  	}
   189  	if msg := convCtx.discarded(); msg != "" {
   190  		log.Logf(4, "error in PC/signal conversion: %v", msg)
   191  	}
   192  	return ret
   193  }
   194  
   195  func (convert *Convert) convertPC(pc uint32) (uint32, bool) {
   196  	moduleIdx := findModule(pc, convert.moduleKeys)
   197  	// Check if address is above the first module offset.
   198  	if moduleIdx >= 0 {
   199  		module, found := convert.conversionHash[convert.moduleKeys[moduleIdx]]
   200  		if !found {
   201  			return pc, false
   202  		}
   203  		// If the address is within the found module add the offset.
   204  		if pc < module.endAddr {
   205  			if module.discard {
   206  				return pc, false
   207  			}
   208  			pc = uint32(int(pc) + module.offset)
   209  		}
   210  	}
   211  	return pc, true
   212  }
   213  
   214  func (cc *convertContext) discarded() string {
   215  	if cc.errCount == 0 {
   216  		return ""
   217  	}
   218  	errMsg := fmt.Sprintf("discarded 0x%x (and %v other PCs) during conversion", cc.errPC, cc.errCount)
   219  	return fmt.Sprintf("%v; not found in module map", errMsg)
   220  }
   221  
   222  func (cc *convertContext) discard(pc uint32) {
   223  	cc.errCount += 1
   224  	if cc.errPC == 0 {
   225  		cc.errPC = pc
   226  	}
   227  }