From 560bf7dcd4369d4633568a2b9912c71bb9928322 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 7 May 2024 17:20:52 +0200 Subject: pkg/vminfo: deduplicate syscall test programs Properly dedup syscall tests. This reduces number of test programs for linux from 4349 to 641. --- pkg/vminfo/linux_syscalls.go | 14 ++++++++++++- pkg/vminfo/linux_test.go | 24 ++++++++-------------- pkg/vminfo/vminfo_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 17 deletions(-) (limited to 'pkg/vminfo') diff --git a/pkg/vminfo/linux_syscalls.go b/pkg/vminfo/linux_syscalls.go index a11eb06b4..529fed3bd 100644 --- a/pkg/vminfo/linux_syscalls.go +++ b/pkg/vminfo/linux_syscalls.go @@ -20,7 +20,19 @@ func (linux) syscallCheck(ctx *checkContext, call *prog.Syscall) string { check := linuxSyscallChecks[call.CallName] if check == nil { check = func(ctx *checkContext, call *prog.Syscall) string { - return ctx.supportedSyscalls([]string{call.Name}) + // Execute plain syscall (rather than a variation with $) to make test program + // deduplication effective. However, if the plain syscall does not exist take + // the first variant for this syscall, this still allows to dedup all variants. + // This works b/c in syscall test we only check for ENOSYS result. + name := call.CallName + if ctx.target.SyscallMap[name] == nil { + for _, call1 := range ctx.target.Syscalls { + if name == call1.CallName { + name = call1.Name + } + } + } + return ctx.supportedSyscalls([]string{name}) } } if reason := check(ctx, call); reason != "" { diff --git a/pkg/vminfo/linux_test.go b/pkg/vminfo/linux_test.go index 702167606..be157b858 100644 --- a/pkg/vminfo/linux_test.go +++ b/pkg/vminfo/linux_test.go @@ -14,8 +14,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/syzkaller/pkg/flatrpc" - "github.com/google/syzkaller/pkg/ipc" - "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/sys/targets" "github.com/stretchr/testify/assert" ) @@ -24,6 +22,13 @@ func TestLinuxSyscalls(t *testing.T) { cfg := testConfig(t, targets.Linux, targets.AMD64) checker := New(cfg) _, checkProgs := checker.StartCheck() + t.Logf("got %v test programs", len(checkProgs)) + if len(checkProgs) > 1000 { + // This is just a sanity check that we don't do something stupid accidentally. + // If it grows above the limit intentionally, the limit can be increased. + // Currently we have 641 (when we failed to properly dedup syscall tests, it was 4349). + t.Fatal("too many test programs") + } filesystems := []string{ "", "9p", "esdfs", "incremental-fs", "cgroup", "cgroup2", "pvfs2", "nfs", "nfs4", "fuse", "fuseblk", "afs", "pipefs", @@ -47,20 +52,7 @@ func TestLinuxSyscalls(t *testing.T) { Data: []byte(strings.Join(filesystems, "\nnodev\t")), }, } - var results []rpctype.ExecutionResult - for _, req := range checkProgs { - p, err := cfg.Target.DeserializeExec(req.ProgData, nil) - if err != nil { - t.Fatal(err) - } - res := rpctype.ExecutionResult{ - ID: req.ID, - Info: ipc.ProgInfo{ - Calls: make([]ipc.CallInfo, len(p.Calls)), - }, - } - results = append(results, res) - } + results := createSuccessfulResults(t, cfg.Target, checkProgs) enabled, disabled, err := checker.FinishCheck(files, results) if err != nil { t.Fatal(err) diff --git a/pkg/vminfo/vminfo_test.go b/pkg/vminfo/vminfo_test.go index 7e7781a73..de41b48fe 100644 --- a/pkg/vminfo/vminfo_test.go +++ b/pkg/vminfo/vminfo_test.go @@ -12,6 +12,7 @@ import ( "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/mgrconfig" + "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" ) @@ -42,6 +43,54 @@ func TestHostMachineInfo(t *testing.T) { } } +func TestSyscalls(t *testing.T) { + t.Parallel() + for _, arches := range targets.List { + for _, target := range arches { + target := target + if target.OS == targets.Linux { + continue // linux has own TestLinuxSyscalls test + } + t.Run(target.OS+"/"+target.Arch, func(t *testing.T) { + t.Parallel() + cfg := testConfig(t, target.OS, target.Arch) + checker := New(cfg) + _, checkProgs := checker.StartCheck() + results := createSuccessfulResults(t, cfg.Target, checkProgs) + enabled, disabled, err := checker.FinishCheck(nil, results) + if err != nil { + t.Fatal(err) + } + for call, reason := range disabled { + t.Errorf("disabled call %v: %v", call.Name, reason) + } + if len(enabled) != len(cfg.Syscalls) { + t.Errorf("enabled only %v calls out of %v", len(enabled), len(cfg.Syscalls)) + } + }) + } + } +} + +func createSuccessfulResults(t *testing.T, target *prog.Target, + progs []rpctype.ExecutionRequest) []rpctype.ExecutionResult { + var results []rpctype.ExecutionResult + for _, req := range progs { + p, err := target.DeserializeExec(req.ProgData, nil) + if err != nil { + t.Fatal(err) + } + res := rpctype.ExecutionResult{ + ID: req.ID, + Info: ipc.ProgInfo{ + Calls: make([]ipc.CallInfo, len(p.Calls)), + }, + } + results = append(results, res) + } + return results +} + func hostChecker(t *testing.T) (*Checker, []flatrpc.FileInfo) { cfg := testConfig(t, runtime.GOOS, runtime.GOARCH) checker := New(cfg) -- cgit mrf-deployment