aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/serializer/serializer.go6
-rw-r--r--tools/syz-query-subsystems/generator.go155
-rw-r--r--tools/syz-query-subsystems/query_subsystems.go63
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)
+ }
+}