diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-01-30 18:35:46 +0100 |
|---|---|---|
| committer | Aleksandr Nogikh <wp32pw@gmail.com> | 2023-02-10 14:34:44 +0100 |
| commit | 9a724c34f06b47f05b36c5292b107e9b06048667 (patch) | |
| tree | aab5d5ea54fa9fdd6cdcd8bc5b841e922a7b6bbb /tools/syz-query-subsystems | |
| parent | 5ca014a29eab3dcdecd58d34c0a332fa78958872 (diff) | |
tools: add a tool to generate a subsystem list
syz-query-subsystems will be used to query and generate the list of
subsystems.
For now such lists will be stored in the repository as auto generated
.go files. This lets us not implement the code for saving them into the
database on the dashboard side and for querying them. Also this should
facilitate the manual review of each subsystem list update.
Diffstat (limited to 'tools/syz-query-subsystems')
| -rw-r--r-- | tools/syz-query-subsystems/generator.go | 155 | ||||
| -rw-r--r-- | tools/syz-query-subsystems/query_subsystems.go | 63 |
2 files changed, 218 insertions, 0 deletions
diff --git a/tools/syz-query-subsystems/generator.go b/tools/syz-query-subsystems/generator.go new file mode 100644 index 000000000..e81002fe6 --- /dev/null +++ b/tools/syz-query-subsystems/generator.go @@ -0,0 +1,155 @@ +// Copyright 2023 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 ( + "bytes" + "fmt" + "go/format" + "regexp" + "sort" + "strings" + "text/template" + + "github.com/google/syzkaller/pkg/serializer" + "github.com/google/syzkaller/pkg/subsystem/entity" +) + +func generateSubsystemsFile(name string, list []*entity.Subsystem) ([]byte, error) { + // Set names first -- we'll need them for filling in the Parents array. + objToName := map[*entity.Subsystem]string{} + for _, entry := range list { + varName := makeVarRegexp.ReplaceAllString(strings.ToLower(entry.Name), "") + if varName == "" { + return nil, fmt.Errorf("failed to get a var name for %#v", entry.Name) + } + objToName[entry] = varName + } + + // Prepare the template data. + vars := &templateVars{ + Name: name, + Hierarchy: hierarchyList(list), + } + for _, entry := range list { + varName := objToName[entry] + // The serializer does not understand parent references and just prints all the + // nested structures. + // Therefore we call it separately for the fields it can understand. + parents := []string{} + for _, p := range entry.Parents { + parents = append(parents, objToName[p]) + } + sort.Strings(parents) + subsystem := &templateSubsystem{ + VarName: varName, + Name: serializer.WriteString(entry.Name), + PathRules: serializer.WriteString(entry.PathRules), + Parents: parents, + } + // Some of the records are mostly empty. + if len(entry.Maintainers) > 0 { + sort.Strings(entry.Maintainers) + subsystem.Maintainers = serializer.WriteString(entry.Maintainers) + } + if len(entry.Syscalls) > 0 { + subsystem.Syscalls = serializer.WriteString(entry.Syscalls) + } + if len(entry.Lists) > 0 { + subsystem.Lists = serializer.WriteString(entry.Lists) + } + vars.List = append(vars.List, subsystem) + } + tmpl, err := template.New("source").Parse(fileTemplate) + if err != nil { + return nil, err + } + var b bytes.Buffer + if err = tmpl.Execute(&b, vars); err != nil { + return nil, err + } + return format.Source(b.Bytes()) +} + +func hierarchyList(list []*entity.Subsystem) []string { + children := map[*entity.Subsystem][]*entity.Subsystem{} + for _, entry := range list { + for _, p := range entry.Parents { + children[p] = append(children[p], entry) + } + } + ret := []string{} + var dfs func(*entity.Subsystem, string) + dfs = func(entry *entity.Subsystem, prefix string) { + ret = append(ret, fmt.Sprintf("%s- %s", prefix, entry.Name)) + for _, child := range children[entry] { + dfs(child, prefix+" ") + } + } + for _, entity := range list { + if len(entity.Parents) == 0 { + dfs(entity, "") + } + } + return ret +} + +var makeVarRegexp = regexp.MustCompile(`[^\w]|^([^a-z]+)`) + +type templateSubsystem struct { + VarName string + Name string + Syscalls string + PathRules string + Lists string + Maintainers string + Parents []string +} + +type templateVars struct { + Name string + List []*templateSubsystem + Hierarchy []string +} + +const fileTemplate = `// Code generated by the syz-query-subsystem tool. DO NOT EDIT. + +package lists + +import . "github.com/google/syzkaller/pkg/subsystem/entity" +import "github.com/google/syzkaller/pkg/subsystem" + +func init() { + subsystem.RegisterList("{{.Name}}", subsystems) +} + +// The subsystem list: +{{- range .Hierarchy}} +// {{.}} +{{- end}} + +var subsystems = []*Subsystem{ +{{range .List}} {{.VarName}}, {{- end}} +} + +// Subsystem info. +{{range .List}} +var {{.VarName}} = &Subsystem{ + Name: {{.Name}}, +{{- if .Syscalls}} + Syscalls: {{.Syscalls}}, +{{- end}} +{{- if .Lists}} + Lists: {{.Lists}}, +{{- end}} +{{- if .Maintainers}} + Maintainers: {{.Maintainers}}, +{{- end}} +{{- if .Parents}} + Parents: []*Subsystem{ {{range .Parents}} {{.}}, {{end}} }, +{{- end}} + PathRules: {{.PathRules}}, +} +{{end}} +` diff --git a/tools/syz-query-subsystems/query_subsystems.go b/tools/syz-query-subsystems/query_subsystems.go new file mode 100644 index 000000000..12431d831 --- /dev/null +++ b/tools/syz-query-subsystems/query_subsystems.go @@ -0,0 +1,63 @@ +// Copyright 2023 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. + +// syz-query-subsystems generates and saves the subsystem lists in the format syzkaller understands. +// An example how to generate the linux subsystem list (for the upstream kernel). +// `./syz-query-subsystems -os linux -kernel ~/linux -syzkaller ~/syzkaller -name linux`. + +package main + +import ( + "flag" + "path/filepath" + "regexp" + "strings" + + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/pkg/subsystem/linux" + "github.com/google/syzkaller/pkg/tool" +) + +var ( + flagOS = flag.String("os", "", "target OS type") + flagKernelRepo = flag.String("kernel", "", "path to the OS kernel source directory") + flagSyzkallerRepo = flag.String("syzkaller", "", "path to the syzkaller repo") + flagName = flag.String("name", "", "the name under which the list should be saved") +) + +var nameRe = regexp.MustCompile(`^[a-z]\w*$`) + +func main() { + defer tool.Init()() + // Validate the input. + if strings.ToLower(*flagOS) != "linux" { + tool.Failf("only Linux is supported at the moment") + } + if !osutil.IsExist(*flagKernelRepo) { + tool.Failf("the specified kernel repo does not exist") + } + if !osutil.IsExist(*flagSyzkallerRepo) { + tool.Failf("the specified syzkaller repo does not exist") + } + if !nameRe.MatchString(*flagName) { + tool.Failf("the name is not acceptable") + } + // Query the subsystems. + list, err := linux.ListFromRepo(*flagKernelRepo) + if err != nil { + tool.Failf("failed to query subsystems: %v", err) + } + // Save the list. + folder := filepath.Join(*flagSyzkallerRepo, "pkg", "subsystem", "lists") + if err = osutil.MkdirAll(folder); err != nil { + tool.Failf("failed to create %s: %v", folder, err) + } + code, err := generateSubsystemsFile(*flagName, list) + if err != nil { + tool.Failf("failed to generate code: %s", err) + } + err = osutil.WriteFile(filepath.Join(folder, *flagName+".go"), code) + if err != nil { + tool.Failf("failed to save the code: %s", err) + } +} |
