diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-01-11 19:37:37 +0100 |
|---|---|---|
| committer | Aleksandr Nogikh <wp32pw@gmail.com> | 2023-02-10 14:34:44 +0100 |
| commit | 4c1f201b6fc2cc30625d3c706b1f45cc68ef0223 (patch) | |
| tree | 57f61c8bbb0c9792e58f0b368539b390725edb4b /pkg/subsystem/linux | |
| parent | 2246b3b0ea578783224118ca3af660ecb0ebd2b6 (diff) | |
pkg/subsystem/linux: add the basic subsystem extraction code
Diffstat (limited to 'pkg/subsystem/linux')
| -rw-r--r-- | pkg/subsystem/linux/rules.go | 13 | ||||
| -rw-r--r-- | pkg/subsystem/linux/subsystems.go | 124 | ||||
| -rw-r--r-- | pkg/subsystem/linux/subsystems_test.go | 132 |
3 files changed, 269 insertions, 0 deletions
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: */ + +` +) |
