github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/cover/backend/modules.go (about)

     1  // Copyright 2021 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 backend
     5  
     6  import (
     7  	"bytes"
     8  	"debug/elf"
     9  	"fmt"
    10  	"io/fs"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/google/syzkaller/pkg/log"
    15  	"github.com/google/syzkaller/pkg/vminfo"
    16  	"github.com/google/syzkaller/sys/targets"
    17  )
    18  
    19  func DiscoverModules(target *targets.Target, objDir string, moduleObj []string) (
    20  	[]*vminfo.KernelModule, error) {
    21  	module := &vminfo.KernelModule{
    22  		Path: filepath.Join(objDir, target.KernelObject),
    23  	}
    24  	textRange, err := elfReadTextSecRange(module)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	modules := []*vminfo.KernelModule{
    29  		// A dummy module representing the kernel itself.
    30  		{
    31  			Path: module.Path,
    32  			Size: textRange.End - textRange.Start,
    33  		},
    34  	}
    35  	if target.OS == targets.Linux {
    36  		modules1, err := discoverModulesLinux(append([]string{objDir}, moduleObj...))
    37  		if err != nil {
    38  			return nil, err
    39  		}
    40  		modules = append(modules, modules1...)
    41  	} else if len(modules) != 1 {
    42  		return nil, fmt.Errorf("%v coverage does not support modules", target.OS)
    43  	}
    44  	return modules, nil
    45  }
    46  
    47  func discoverModulesLinux(dirs []string) ([]*vminfo.KernelModule, error) {
    48  	paths, err := locateModules(dirs)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	var modules []*vminfo.KernelModule
    53  	for name, path := range paths {
    54  		if path == "" {
    55  			continue
    56  		}
    57  		log.Logf(2, "module %v -> %v", name, path)
    58  		module := &vminfo.KernelModule{
    59  			Name: name,
    60  			Path: path,
    61  		}
    62  		textRange, err := elfReadTextSecRange(module)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		module.Size = textRange.End - textRange.Start
    67  		modules = append(modules, module)
    68  	}
    69  	return modules, nil
    70  }
    71  
    72  func locateModules(dirs []string) (map[string]string, error) {
    73  	paths := make(map[string]string)
    74  	for _, dir := range dirs {
    75  		err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
    76  			if err != nil || filepath.Ext(path) != ".ko" {
    77  				return err
    78  			}
    79  			name, err := getModuleName(path)
    80  			if err != nil {
    81  				// Extracting module name involves parsing ELF and binary data,
    82  				// let's not fail on it, we still have the file name,
    83  				// which is usually the right module name.
    84  				log.Logf(0, "failed to get %v module name: %v", path, err)
    85  				name = strings.TrimSuffix(filepath.Base(path), "."+filepath.Ext(path))
    86  			}
    87  			// Order of dirs determine priority, so don't overwrite already discovered names.
    88  			if name != "" && paths[name] == "" {
    89  				paths[name] = path
    90  			}
    91  			return nil
    92  		})
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  	}
    97  	return paths, nil
    98  }
    99  
   100  func getModuleName(path string) (string, error) {
   101  	file, err := elf.Open(path)
   102  	if err != nil {
   103  		return "", err
   104  	}
   105  	defer file.Close()
   106  	section := file.Section(".modinfo")
   107  	if section == nil {
   108  		return "", fmt.Errorf("no .modinfo section")
   109  	}
   110  	data, err := section.Data()
   111  	if err != nil {
   112  		return "", fmt.Errorf("failed to read .modinfo: %w", err)
   113  	}
   114  	if name := searchModuleName(data); name != "" {
   115  		return name, nil
   116  	}
   117  	section = file.Section(".gnu.linkonce.this_module")
   118  	if section == nil {
   119  		return "", fmt.Errorf("no .gnu.linkonce.this_module section")
   120  	}
   121  	data, err = section.Data()
   122  	if err != nil {
   123  		return "", fmt.Errorf("failed to read .gnu.linkonce.this_module: %w", err)
   124  	}
   125  	return string(data), nil
   126  }
   127  
   128  func searchModuleName(data []byte) string {
   129  	data = append([]byte{0}, data...)
   130  	key := []byte("\x00name=")
   131  	pos := bytes.Index(data, key)
   132  	if pos == -1 {
   133  		return ""
   134  	}
   135  	end := bytes.IndexByte(data[pos+len(key):], 0)
   136  	if end == -1 {
   137  		return ""
   138  	}
   139  	end = pos + len(key) + end
   140  	if end > len(data) {
   141  		return ""
   142  	}
   143  	return string(data[pos+len(key) : end])
   144  }
   145  
   146  func getKaslrOffset(modules []*vminfo.KernelModule, pcBase uint64) uint64 {
   147  	for _, mod := range modules {
   148  		if mod.Name == "" {
   149  			return mod.Addr - pcBase
   150  		}
   151  	}
   152  	return 0
   153  }
   154  
   155  // when CONFIG_RANDOMIZE_BASE=y, pc from kcov already removed kaslr_offset.
   156  func FixModules(localModules, modules []*vminfo.KernelModule, pcBase uint64) []*vminfo.KernelModule {
   157  	kaslrOffset := getKaslrOffset(modules, pcBase)
   158  	var modules1 []*vminfo.KernelModule
   159  	for _, mod := range modules {
   160  		size := uint64(0)
   161  		path := ""
   162  		for _, modA := range localModules {
   163  			if modA.Name == mod.Name {
   164  				size = modA.Size
   165  				path = modA.Path
   166  				break
   167  			}
   168  		}
   169  		if path == "" {
   170  			continue
   171  		}
   172  		addr := mod.Addr - kaslrOffset
   173  		modules1 = append(modules1, &vminfo.KernelModule{
   174  			Name: mod.Name,
   175  			Size: size,
   176  			Addr: addr,
   177  			Path: path,
   178  		})
   179  	}
   180  	return modules1
   181  }