From 01622de2d0ec3b6cc18aef5bcbd5e76dd634116e Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 12 Sep 2020 17:12:12 +0200 Subject: pkg/host: move machine info functionality from syz-fuzzer It's better to keep functionality in packages rather than in main. It makes it reusable and better organized. Move machine info functionality to pkg/host and do some cosmetic refactoring. --- pkg/host/machine_info.go | 34 +++++++++ pkg/host/machine_info_linux.go | 122 ++++++++++++++++++++++++++++++++ pkg/host/machine_info_test.go | 149 ++++++++++++++++++++++++++++++++++++++++ syz-fuzzer/fuzzer.go | 2 +- syz-fuzzer/machine_info.go | 149 ---------------------------------------- syz-fuzzer/machine_info_test.go | 145 -------------------------------------- 6 files changed, 306 insertions(+), 295 deletions(-) create mode 100644 pkg/host/machine_info.go create mode 100644 pkg/host/machine_info_linux.go create mode 100644 pkg/host/machine_info_test.go delete mode 100644 syz-fuzzer/machine_info.go delete mode 100644 syz-fuzzer/machine_info_test.go diff --git a/pkg/host/machine_info.go b/pkg/host/machine_info.go new file mode 100644 index 000000000..7a7cd2612 --- /dev/null +++ b/pkg/host/machine_info.go @@ -0,0 +1,34 @@ +// Copyright 2020 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package host + +import ( + "bytes" + "fmt" + "os" + "strings" +) + +func CollectMachineInfo() ([]byte, error) { + buf := new(bytes.Buffer) + for _, pair := range machineInfoFuncs { + fmt.Fprintf(buf, "[%s]\n", pair.name) + err := pair.fn(buf) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + fmt.Fprintf(buf, "%v\n", err) + } + fmt.Fprintf(buf, "%v\n\n", strings.Repeat("-", 80)) + } + return buf.Bytes(), nil +} + +var machineInfoFuncs []machineInfoFunc + +type machineInfoFunc struct { + name string + fn func(*bytes.Buffer) error +} diff --git a/pkg/host/machine_info_linux.go b/pkg/host/machine_info_linux.go new file mode 100644 index 000000000..da1ee481a --- /dev/null +++ b/pkg/host/machine_info_linux.go @@ -0,0 +1,122 @@ +// Copyright 2020 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package host + +import ( + "bufio" + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +func init() { + machineInfoFuncs = []machineInfoFunc{ + {"CPU Info", readCPUInfo}, + {"KVM", readKVMInfo}, + } +} + +func readCPUInfo(buffer *bytes.Buffer) error { + file, err := os.Open("/proc/cpuinfo") + if err != nil { + return err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + scanCPUInfo(buffer, scanner) + return nil +} + +func scanCPUInfo(buffer *bytes.Buffer, scanner *bufio.Scanner) { + keyIndices := make(map[string]int) + type keyValues struct { + key string + values []string + } + var info []keyValues + + for scanner.Scan() { + splitted := strings.Split(scanner.Text(), ":") + if len(splitted) != 2 { + continue + } + key := strings.TrimSpace(splitted[0]) + val := strings.TrimSpace(splitted[1]) + + if idx, ok := keyIndices[key]; !ok { + idx = len(keyIndices) + keyIndices[key] = idx + info = append(info, keyValues{key, []string{val}}) + } else { + info[idx].values = append(info[idx].values, val) + } + } + + for _, kv := range info { + // It is guaranteed that len(vals) >= 1 + key := kv.key + vals := kv.values + if allEqual(vals) { + fmt.Fprintf(buffer, "%-20s: %s\n", key, vals[0]) + } else { + fmt.Fprintf(buffer, "%-20s: %s\n", key, strings.Join(vals, ", ")) + } + } +} + +func allEqual(slice []string) bool { + if len(slice) == 0 { + return true + } + for i := 1; i < len(slice); i++ { + if slice[i] != slice[0] { + return false + } + } + return true +} + +func readKVMInfo(buffer *bytes.Buffer) error { + files, err := ioutil.ReadDir("/sys/module/") + if err != nil { + return err + } + + for _, file := range files { + name := file.Name() + if !strings.HasPrefix(name, "kvm") { + continue + } + + paramPath := filepath.Join("/sys", "module", name, "parameters") + params, err := ioutil.ReadDir(paramPath) + if err != nil { + if os.IsNotExist(err) { + continue + } + return err + } + + if len(params) == 0 { + continue + } + + fmt.Fprintf(buffer, "/sys/module/%s:\n", name) + for _, key := range params { + keyName := key.Name() + data, err := ioutil.ReadFile(filepath.Join(paramPath, keyName)) + if err != nil { + return err + } + fmt.Fprintf(buffer, "\t%s: ", keyName) + buffer.Write(data) + } + buffer.WriteByte('\n') + } + return nil +} diff --git a/pkg/host/machine_info_test.go b/pkg/host/machine_info_test.go new file mode 100644 index 000000000..49ef93fe0 --- /dev/null +++ b/pkg/host/machine_info_test.go @@ -0,0 +1,149 @@ +// Copyright 2020 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package host + +import ( + "bufio" + "bytes" + "runtime" + "strings" + "testing" +) + +func TestMachineInfoLinux(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip() + } + result, err := CollectMachineInfo() + if err != nil { + t.Fatal(err) + } + + scanner := bufio.NewScanner(bytes.NewReader(result)) + + for scanner.Scan() { + line := scanner.Text() + + if line == "[CPU Info]" { + checkCPUInfo(t, scanner) + } + if line == "[KVM]" { + checkKVMInfo(t, scanner) + } + } +} + +func checkCPUInfo(t *testing.T, scanner *bufio.Scanner) { + keys := make(map[string]bool) + for scanner.Scan() { + line := scanner.Text() + // End of CPU Info section. + if strings.HasPrefix(line, "-----") { + break + } + splitted := strings.Split(line, ":") + if len(splitted) != 2 { + t.Fatalf("the format of line \"%s\" is not correct", line) + } + key := strings.TrimSpace(splitted[0]) + keys[key] = true + } + + importantKeys := [][]string{ + {"vendor", "vendor_id", "CPU implementer"}, + {"model", "CPU part", "cpu model"}, + {"flags", "features", "Features", "ASEs implemented", "type"}, + } + for _, possibleNames := range importantKeys { + exists := false + for _, name := range possibleNames { + if keys[name] { + exists = true + break + } + } + if !exists { + t.Fatalf("one of {%s} should exists in the output, but not found", + strings.Join(possibleNames, ", ")) + } + } +} + +func checkKVMInfo(t *testing.T, scanner *bufio.Scanner) { + for scanner.Scan() { + line := scanner.Text() + if line == "" { + continue + } + if strings.HasPrefix(line, "-----") { + break + } + splitted := strings.Split(line, ":") + if len(splitted) != 2 { + t.Fatalf("the format of line \"%s\" is not correct", line) + } + key := strings.TrimSpace(splitted[0]) + if key == "" { + t.Fatalf("empty key") + } + if key[0] != '/' { + continue + } + + if !strings.HasPrefix(key, "/sys/module/kvm") { + t.Fatalf("the directory does not match /sys/module/kvm*") + } + } +} + +func TestScanCPUInfo(t *testing.T) { + input := `A: a +B: b + +C: c1 +D: d +C: c1 +D: d +C: c2 +D: d +` + + output := []struct { + key, val string + }{ + {"A", "a"}, + {"B", "b"}, + {"C", "c1, c1, c2"}, + {"D", "d"}, + } + scanner := bufio.NewScanner(strings.NewReader(input)) + buffer := new(bytes.Buffer) + scanCPUInfo(buffer, scanner) + result := bufio.NewScanner(buffer) + + idx := 0 + for result.Scan() { + line := result.Text() + splitted := strings.Split(line, ":") + if len(splitted) != 2 { + t.Fatalf("the format of line \"%s\" is not correct", line) + } + key := strings.TrimSpace(splitted[0]) + val := strings.TrimSpace(splitted[1]) + if idx >= len(output) { + t.Fatalf("additional line \"%s: %s\"", key, val) + } + expected := output[idx] + if key != expected.key || val != expected.val { + t.Fatalf("expected \"%s: %s\", got \"%s: %s\"", + expected.key, expected.val, key, val) + } + idx++ + } + if idx < len(output) { + expected := output[idx] + t.Fatalf("expected \"%s: %s\", got end of output", + expected.key, expected.val) + } +} diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index 3218d9b88..ae9a5af59 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -181,7 +181,7 @@ func main() { runtime.MemProfileRate = 0 } - machineInfo, err := CollectMachineInfo() + machineInfo, err := host.CollectMachineInfo() if err != nil { log.Fatalf("failed to collect machine information: %v", err) } diff --git a/syz-fuzzer/machine_info.go b/syz-fuzzer/machine_info.go deleted file mode 100644 index 040655e62..000000000 --- a/syz-fuzzer/machine_info.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2020 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package main - -import ( - "bufio" - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "strings" -) - -func CollectMachineInfo() ([]byte, error) { - if runtime.GOOS != "linux" { - return nil, nil - } - - type machineInfoFunc struct { - name string - fn func(*bytes.Buffer) error - } - - allMachineInfo := []machineInfoFunc{ - {"CPU Info", readCPUInfo}, - {"KVM", readKVMInfo}, - } - - buffer := new(bytes.Buffer) - - for _, pair := range allMachineInfo { - fmt.Fprintf(buffer, "[%s]\n", pair.name) - err := pair.fn(buffer) - if err != nil { - if os.IsNotExist(err) { - buffer.WriteString(err.Error() + "\n") - } else { - return nil, err - } - } - fmt.Fprintf(buffer, "-----------------------------------\n\n") - } - - return buffer.Bytes(), nil -} - -func readCPUInfo(buffer *bytes.Buffer) error { - file, err := os.Open("/proc/cpuinfo") - if err != nil { - return err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - scanCPUInfo(buffer, scanner) - return nil -} - -func scanCPUInfo(buffer *bytes.Buffer, scanner *bufio.Scanner) { - keyIndices := make(map[string]int) - type keyValues struct { - key string - values []string - } - var info []keyValues - - for scanner.Scan() { - splitted := strings.Split(scanner.Text(), ":") - if len(splitted) != 2 { - continue - } - key := strings.TrimSpace(splitted[0]) - val := strings.TrimSpace(splitted[1]) - - if idx, ok := keyIndices[key]; !ok { - idx = len(keyIndices) - keyIndices[key] = idx - info = append(info, keyValues{key, []string{val}}) - } else { - info[idx].values = append(info[idx].values, val) - } - } - - for _, kv := range info { - // It is guaranteed that len(vals) >= 1 - key := kv.key - vals := kv.values - if allEqual(vals) { - fmt.Fprintf(buffer, "%-20s: %s\n", key, vals[0]) - } else { - fmt.Fprintf(buffer, "%-20s: %s\n", key, strings.Join(vals, ", ")) - } - } -} - -func allEqual(slice []string) bool { - if len(slice) == 0 { - return true - } - for i := 1; i < len(slice); i++ { - if slice[i] != slice[0] { - return false - } - } - return true -} - -func readKVMInfo(buffer *bytes.Buffer) error { - files, err := ioutil.ReadDir("/sys/module/") - if err != nil { - return err - } - - for _, file := range files { - name := file.Name() - if !strings.HasPrefix(name, "kvm") { - continue - } - - paramPath := filepath.Join("/sys", "module", name, "parameters") - params, err := ioutil.ReadDir(paramPath) - if err != nil { - if os.IsNotExist(err) { - continue - } - return err - } - - if len(params) == 0 { - continue - } - - fmt.Fprintf(buffer, "/sys/module/%s:\n", name) - for _, key := range params { - keyName := key.Name() - data, err := ioutil.ReadFile(filepath.Join(paramPath, keyName)) - if err != nil { - return err - } - fmt.Fprintf(buffer, "\t%s: ", keyName) - buffer.Write(data) - } - buffer.WriteString("\n") - } - return nil -} diff --git a/syz-fuzzer/machine_info_test.go b/syz-fuzzer/machine_info_test.go deleted file mode 100644 index 71633ca5b..000000000 --- a/syz-fuzzer/machine_info_test.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2020 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package main - -import ( - "bufio" - "bytes" - "strings" - "testing" -) - -func TestMachineInfoLinux(t *testing.T) { - result, err := CollectMachineInfo() - if err != nil { - t.Fatal(err) - } - - scanner := bufio.NewScanner(bytes.NewReader(result)) - - for scanner.Scan() { - line := scanner.Text() - - if line == "[CPU Info]" { - checkCPUInfo(t, scanner) - } - if line == "[KVM]" { - checkKVMInfo(t, scanner) - } - } -} - -func checkCPUInfo(t *testing.T, scanner *bufio.Scanner) { - keys := make(map[string]bool) - for scanner.Scan() { - line := scanner.Text() - // End of CPU Info section. - if strings.HasPrefix(line, "-----") { - break - } - splitted := strings.Split(line, ":") - if len(splitted) != 2 { - t.Fatalf("the format of line \"%s\" is not correct", line) - } - key := strings.TrimSpace(splitted[0]) - keys[key] = true - } - - importantKeys := [][]string{ - {"vendor", "vendor_id", "CPU implementer"}, - {"model", "CPU part", "cpu model"}, - {"flags", "features", "Features", "ASEs implemented", "type"}, - } - for _, possibleNames := range importantKeys { - exists := false - for _, name := range possibleNames { - if keys[name] { - exists = true - break - } - } - if !exists { - t.Fatalf("one of {%s} should exists in the output, but not found", - strings.Join(possibleNames, ", ")) - } - } -} - -func checkKVMInfo(t *testing.T, scanner *bufio.Scanner) { - for scanner.Scan() { - line := scanner.Text() - if line == "" { - continue - } - if strings.HasPrefix(line, "-----") { - break - } - splitted := strings.Split(line, ":") - if len(splitted) != 2 { - t.Fatalf("the format of line \"%s\" is not correct", line) - } - key := strings.TrimSpace(splitted[0]) - if key == "" { - t.Fatalf("empty key") - } - if key[0] != '/' { - continue - } - - if !strings.HasPrefix(key, "/sys/module/kvm") { - t.Fatalf("the directory does not match /sys/module/kvm*") - } - } -} - -func TestScanCPUInfo(t *testing.T) { - input := `A: a -B: b - -C: c1 -D: d -C: c1 -D: d -C: c2 -D: d -` - - output := []struct { - key, val string - }{ - {"A", "a"}, - {"B", "b"}, - {"C", "c1, c1, c2"}, - {"D", "d"}, - } - scanner := bufio.NewScanner(strings.NewReader(input)) - buffer := new(bytes.Buffer) - scanCPUInfo(buffer, scanner) - result := bufio.NewScanner(strings.NewReader(buffer.String())) - - idx := 0 - for result.Scan() { - line := result.Text() - splitted := strings.Split(line, ":") - if len(splitted) != 2 { - t.Fatalf("the format of line \"%s\" is not correct", line) - } - key := strings.TrimSpace(splitted[0]) - val := strings.TrimSpace(splitted[1]) - if idx >= len(output) { - t.Fatalf("additional line \"%s: %s\"", key, val) - } - expected := output[idx] - if key != expected.key || val != expected.val { - t.Fatalf("expected \"%s: %s\", got \"%s: %s\"", - expected.key, expected.val, key, val) - } - idx++ - } - if idx < len(output) { - expected := output[idx] - t.Fatalf("expected \"%s: %s\", got end of output", - expected.key, expected.val) - } -} -- cgit mrf-deployment