github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/vminfo/linux.go (about) 1 // Copyright 2024 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 vminfo 5 6 import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "io" 11 "path" 12 "regexp" 13 "strconv" 14 "strings" 15 16 "github.com/google/syzkaller/pkg/cover" 17 ) 18 19 type linux int 20 21 func (linux) RequiredFiles() []string { 22 return []string{ 23 "/proc/cpuinfo", 24 "/proc/modules", 25 "/sys/module/*/sections/.text", 26 "/sys/module/kvm*/parameters/*", 27 } 28 } 29 30 func (linux) checkFiles() []string { 31 return []string{ 32 "/proc/version", 33 "/proc/filesystems", 34 "/sys/kernel/security/lsm", 35 "/dev/raw-gadget", 36 } 37 } 38 39 func (linux) machineInfos() []machineInfoFunc { 40 return []machineInfoFunc{ 41 linuxReadCPUInfo, 42 linuxReadKVMInfo, 43 } 44 } 45 46 func (linux) parseModules(files filesystem) ([]cover.KernelModule, error) { 47 var modules []cover.KernelModule 48 re := regexp.MustCompile(`(\w+) ([0-9]+) .*(0[x|X][a-fA-F0-9]+)[^\n]*`) 49 modulesText, _ := files.ReadFile("/proc/modules") 50 for _, match := range re.FindAllSubmatch(modulesText, -1) { 51 name := string(match[1]) 52 modAddr, err := strconv.ParseUint(string(match[3]), 0, 64) 53 if err != nil { 54 // /proc/modules is broken, bail out. 55 return nil, fmt.Errorf("module %v address parsing error: %w", name, err) 56 } 57 textAddr, err := linuxModuleTextAddr(files, name) 58 if err != nil { 59 // Module address unavailable, .text is probably 0. Skip this module. 60 continue 61 } 62 modSize, err := strconv.ParseUint(string(match[2]), 0, 64) 63 if err != nil { 64 // /proc/modules is broken, bail out. 65 return nil, fmt.Errorf("module %v size parsing error: %w", name, err) 66 } 67 offset := modAddr - textAddr 68 modules = append(modules, cover.KernelModule{ 69 Name: name, 70 Addr: textAddr, 71 Size: modSize - offset, 72 }) 73 } 74 return modules, nil 75 } 76 77 func linuxModuleTextAddr(files filesystem, module string) (uint64, error) { 78 data, err := files.ReadFile("/sys/module/" + module + "/sections/.text") 79 if err != nil { 80 return 0, fmt.Errorf("could not read module %v .text address file: %w", module, err) 81 } 82 addrString := strings.TrimSpace(string(data)) 83 addr, err := strconv.ParseUint(addrString, 0, 64) 84 if err != nil { 85 return 0, fmt.Errorf("address parsing error in %v: %w", module, err) 86 } 87 return addr, nil 88 } 89 90 func linuxReadCPUInfo(files filesystem, w io.Writer) (string, error) { 91 data, err := files.ReadFile("/proc/cpuinfo") 92 if err != nil { 93 return "", fmt.Errorf("error reading CPU info:: %w", err) 94 } 95 96 keyIndices := make(map[string]int) 97 type keyValues struct { 98 key string 99 values []string 100 } 101 var info []keyValues 102 for s := bufio.NewScanner(bytes.NewReader(data)); s.Scan(); { 103 splitted := strings.Split(s.Text(), ":") 104 if len(splitted) != 2 { 105 continue 106 } 107 key := strings.TrimSpace(splitted[0]) 108 val := strings.TrimSpace(splitted[1]) 109 if idx, ok := keyIndices[key]; !ok { 110 idx = len(keyIndices) 111 keyIndices[key] = idx 112 info = append(info, keyValues{key, []string{val}}) 113 } else { 114 info[idx].values = append(info[idx].values, val) 115 } 116 } 117 118 for _, kv := range info { 119 // It is guaranteed that len(vals) >= 1 120 key := kv.key 121 vals := kv.values 122 if allEqual(vals) { 123 fmt.Fprintf(w, "%-20s: %s\n", key, vals[0]) 124 } else { 125 fmt.Fprintf(w, "%-20s: %s\n", key, strings.Join(vals, ", ")) 126 } 127 } 128 return "CPU Info", nil 129 } 130 131 func allEqual(slice []string) bool { 132 for i := 1; i < len(slice); i++ { 133 if slice[i] != slice[0] { 134 return false 135 } 136 } 137 return true 138 } 139 140 func linuxReadKVMInfo(files filesystem, w io.Writer) (string, error) { 141 for _, module := range files.ReadDir("/sys/module") { 142 if !strings.HasPrefix(module, "kvm") { 143 continue 144 } 145 paramPath := path.Join("/sys", "module", module, "parameters") 146 fmt.Fprintf(w, "/sys/module/%s:\n", module) 147 for _, param := range files.ReadDir(paramPath) { 148 data, err := files.ReadFile(path.Join(paramPath, param)) 149 if err != nil { 150 return "", fmt.Errorf("error reading KVM info: %w", err) 151 } 152 fmt.Fprintf(w, "\t%s: %s", param, data) 153 } 154 w.Write([]byte{'\n'}) 155 } 156 return "KVM", nil 157 }