diff options
| -rw-r--r-- | pkg/serializer/serializer.go | 6 | ||||
| -rw-r--r-- | tools/syz-query-subsystems/generator.go | 155 | ||||
| -rw-r--r-- | tools/syz-query-subsystems/query_subsystems.go | 63 |
3 files changed, 224 insertions, 0 deletions
diff --git a/pkg/serializer/serializer.go b/pkg/serializer/serializer.go index 05c00a3bd..3f7770009 100644 --- a/pkg/serializer/serializer.go +++ b/pkg/serializer/serializer.go @@ -27,6 +27,12 @@ func Write(ww io.Writer, i interface{}) { w.do(v, false) } +func WriteString(i interface{}) string { + var sb strings.Builder + Write(&sb, i) + return sb.String() +} + type writer struct { w io.Writer } 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) + } +} |
