aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/subsystem
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-01-11 19:37:37 +0100
committerAleksandr Nogikh <wp32pw@gmail.com>2023-02-10 14:34:44 +0100
commit4c1f201b6fc2cc30625d3c706b1f45cc68ef0223 (patch)
tree57f61c8bbb0c9792e58f0b368539b390725edb4b /pkg/subsystem
parent2246b3b0ea578783224118ca3af660ecb0ebd2b6 (diff)
pkg/subsystem/linux: add the basic subsystem extraction code
Diffstat (limited to 'pkg/subsystem')
-rw-r--r--pkg/subsystem/entity/entities.go10
-rw-r--r--pkg/subsystem/linux/rules.go13
-rw-r--r--pkg/subsystem/linux/subsystems.go124
-rw-r--r--pkg/subsystem/linux/subsystems_test.go132
4 files changed, 278 insertions, 1 deletions
diff --git a/pkg/subsystem/entity/entities.go b/pkg/subsystem/entity/entities.go
index 6d028e391..3bcdf8e93 100644
--- a/pkg/subsystem/entity/entities.go
+++ b/pkg/subsystem/entity/entities.go
@@ -4,7 +4,11 @@
package entity
type Subsystem struct {
- PathRules []PathRule
+ Name string
+ PathRules []PathRule
+ Syscalls []string
+ Lists []string
+ Maintainers []string
}
// PathRule describes the part of the directory tree belonging to a single subsystem.
@@ -13,3 +17,7 @@ type PathRule struct {
// ExcludeRegexps are tested before IncludeRegexp.
ExcludeRegexp string
}
+
+func (pr *PathRule) IsEmpty() bool {
+ return pr.IncludeRegexp == "" && pr.ExcludeRegexp == ""
+}
diff --git a/pkg/subsystem/linux/rules.go b/pkg/subsystem/linux/rules.go
new file mode 100644
index 000000000..2df641c5e
--- /dev/null
+++ b/pkg/subsystem/linux/rules.go
@@ -0,0 +1,13 @@
+// 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 linux
+
+type customRules struct {
+ // The mapping between a Linux subsystem name and its system calls.
+ subsystemCalls map[string][]string
+}
+
+var (
+ linuxSubsystemRules = &customRules{}
+)
diff --git a/pkg/subsystem/linux/subsystems.go b/pkg/subsystem/linux/subsystems.go
new file mode 100644
index 000000000..b5ffa924c
--- /dev/null
+++ b/pkg/subsystem/linux/subsystems.go
@@ -0,0 +1,124 @@
+// 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 linux
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "sort"
+
+ "github.com/google/syzkaller/pkg/subsystem/entity"
+)
+
+func ListFromRepo(repo string) ([]*entity.Subsystem, error) {
+ return listFromRepoInner(os.DirFS(repo), linuxSubsystemRules)
+}
+
+// listFromRepoInner allows for better testing.
+func listFromRepoInner(root fs.FS, rules *customRules) ([]*entity.Subsystem, error) {
+ records, err := getMaintainers(root)
+ if err != nil {
+ return nil, err
+ }
+ ctx := &linuxCtx{
+ root: root,
+ rawRecords: records,
+ extraRules: rules,
+ }
+ ctx.groupByList()
+ list, err := ctx.getSubsystems()
+ if err != nil {
+ return nil, err
+ }
+ // Sort subsystems by name to keep output consistent.
+ sort.Slice(list, func(i, j int) bool { return list[i].Name < list[j].Name })
+ // Sort path rules to keep output consistent.
+ for _, entity := range list {
+ sort.Slice(entity.PathRules, func(i, j int) bool {
+ a, b := entity.PathRules[i], entity.PathRules[j]
+ if a.IncludeRegexp != b.IncludeRegexp {
+ return a.IncludeRegexp < b.IncludeRegexp
+ }
+ return a.ExcludeRegexp < b.ExcludeRegexp
+ })
+ }
+ return list, nil
+}
+
+type linuxCtx struct {
+ root fs.FS
+ rawRecords []*maintainersRecord
+ extraRules *customRules
+}
+
+type subsystemCandidate struct {
+ records []*maintainersRecord
+ commonEmail string
+}
+
+func (ctx *linuxCtx) groupByList() []*subsystemCandidate {
+ perList := make(map[string][]*maintainersRecord)
+ for _, record := range ctx.rawRecords {
+ for _, list := range record.lists {
+ perList[list] = append(perList[list], record)
+ }
+ }
+ ret := []*subsystemCandidate{}
+ for email, list := range perList {
+ ret = append(ret, &subsystemCandidate{
+ commonEmail: email,
+ records: list,
+ })
+ }
+ return ret
+}
+
+func (ctx *linuxCtx) getSubsystems() ([]*entity.Subsystem, error) {
+ ret := []*entity.Subsystem{}
+ for _, raw := range ctx.groupByList() {
+ s := &entity.Subsystem{}
+ mergeRawRecords(s, raw.records)
+ ret = append(ret, s)
+ }
+ return ret, nil
+}
+
+func mergeRawRecords(subsystem *entity.Subsystem, records []*maintainersRecord) {
+ unique := func(list []string) []string {
+ m := make(map[string]struct{})
+ for _, s := range list {
+ m[s] = struct{}{}
+ }
+ ret := []string{}
+ for s := range m {
+ ret = append(ret, s)
+ }
+ return ret
+ }
+ var lists, maintainers []string
+ for _, record := range records {
+ rule := record.ToPathRule()
+ if !rule.IsEmpty() {
+ subsystem.PathRules = append(subsystem.PathRules, rule)
+ }
+ lists = append(lists, record.lists...)
+ maintainers = append(maintainers, record.maintainers...)
+ }
+ subsystem.Lists = unique(lists)
+ // But there's a risk that we collect too many unrelated maintainers, so
+ // let's only merge them if there are no lists.
+ if len(records) <= 1 {
+ subsystem.Maintainers = unique(maintainers)
+ }
+}
+
+func getMaintainers(root fs.FS) ([]*maintainersRecord, error) {
+ f, err := root.Open("MAINTAINERS")
+ if err != nil {
+ return nil, fmt.Errorf("failed to open the MAINTAINERS file: %w", err)
+ }
+ defer f.Close()
+ return parseLinuxMaintainers(f)
+}
diff --git a/pkg/subsystem/linux/subsystems_test.go b/pkg/subsystem/linux/subsystems_test.go
new file mode 100644
index 000000000..f5699f26e
--- /dev/null
+++ b/pkg/subsystem/linux/subsystems_test.go
@@ -0,0 +1,132 @@
+// 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 linux
+
+import (
+ "io/fs"
+ "testing"
+ "testing/fstest"
+
+ "github.com/google/syzkaller/pkg/subsystem/entity"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGroupLinuxSubsystems(t *testing.T) {
+ subsystems, err := listFromRepoInner(
+ prepareTestLinuxRepo(t, []byte(testMaintainers)),
+ nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The regexps used for matching rules may change later, so let's not compare them here.
+ for _, s := range subsystems {
+ s.PathRules = nil
+ }
+ expected := []*entity.Subsystem{
+ {
+ Lists: []string{"linux-fsdevel@vger.kernel.org"},
+ Maintainers: []string{"email_vfs@email.com"},
+ },
+ {
+ Lists: []string{"linux-ext4@vger.kernel.org"},
+ Maintainers: []string{"email_ext4@email.com", "email_ext4_2@email.com"},
+ },
+ {
+ Lists: []string{"linux-mm@kvack.org"},
+ },
+ {
+ Lists: []string{"linux-kernel@vger.kernel.org"},
+ Maintainers: []string{"email_rest@email.com"},
+ },
+ }
+ assert.ElementsMatch(t, subsystems, expected)
+}
+
+func prepareTestLinuxRepo(t *testing.T, maintainers []byte) fs.FS {
+ return fstest.MapFS{
+ `fs/ext4/fsync.c`: {},
+ `fs/ext4/mmp.c`: {},
+ `fs/freevxfs/vxfs_olt.c`: {},
+ `fs/file.c`: {},
+ `fs/internal.h`: {},
+ `include/linux/fs.h`: {},
+ `include/linux/mm.h`: {},
+ `include/net/ah.h`: {},
+ `mm/memory.c`: {},
+ `mm/shmem.c`: {},
+ `MAINTAINERS`: {Data: maintainers},
+ }
+}
+
+var (
+ testMaintainers = `
+Maintainers List
+----------------
+
+.. note:: When reading this list, please look for the most precise areas
+ first. When adding to this list, please keep the entries in
+ alphabetical order.
+
+FILESYSTEMS (VFS and infrastructure)
+M: Developer <email_vfs@email.com>
+L: linux-fsdevel@vger.kernel.org
+S: Maintained
+F: fs/*
+F: include/linux/fs.h
+F: include/linux/fs_types.h
+F: include/uapi/linux/fs.h
+F: include/uapi/linux/openat2.h
+
+EXT4 FILE SYSTEM
+M: Developer <email_ext4@email.com>
+M: Developer <email_ext4_2@email.com>
+L: linux-ext4@vger.kernel.org
+S: Maintained
+W: http://ext4.wiki.kernel.org
+Q: http://patchwork.ozlabs.org/project/linux-ext4/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git
+F: Documentation/filesystems/ext4/
+F: fs/ext4/
+F: include/trace/events/ext4.h
+
+FREEVXFS FILESYSTEM
+M: Developer <email_vxfs@email.com>
+S: Maintained
+W: ftp://ftp.openlinux.org/pub/people/hch/vxfs
+F: fs/freevxfs/
+
+MEMORY MANAGEMENT
+M: Developer <email_mm@email.com>
+L: linux-mm@kvack.org
+S: Maintained
+W: http://www.linux-mm.org
+T: git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+T: quilt git://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new
+F: include/linux/gfp.h
+F: include/linux/gfp_types.h
+F: include/linux/memory_hotplug.h
+F: include/linux/mm.h
+F: include/linux/mmzone.h
+F: include/linux/pagewalk.h
+F: include/linux/vmalloc.h
+F: mm/
+F: tools/testing/selftests/vm/
+
+TMPFS (SHMEM FILESYSTEM)
+M: Developer <email_shmem@email.com>
+L: linux-mm@kvack.org
+S: Maintained
+F: include/linux/shmem_fs.h
+F: mm/shmem.c
+
+THE REST
+M: Developer <email_rest@email.com>
+L: linux-kernel@vger.kernel.org
+S: Buried alive in reporters
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+F: *
+F: */
+
+`
+)