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 }