Skip to content

Commit 4da006f

Browse files
committed
pkg/aflow/action/crash/test: implement patch testing
1 parent b1fba58 commit 4da006f

File tree

3 files changed

+160
-77
lines changed

3 files changed

+160
-77
lines changed

pkg/aflow/action/crash/reproduce.go

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
// If the reproducer does not trigger a crash, action fails.
2424
var Reproduce = aflow.NewFuncAction("crash-reproducer", reproduce)
2525

26-
type reproduceArgs struct {
26+
type ReproduceArgs struct {
2727
Syzkaller string
2828
Image string
2929
Type string
@@ -42,11 +42,55 @@ type reproduceResult struct {
4242
CrashReport string
4343
}
4444

45-
func reproduce(ctx *aflow.Context, args reproduceArgs) (reproduceResult, error) {
45+
func ReproduceCrash(args ReproduceArgs, workdir string) (string, string, error) {
4646
if args.Type != "qemu" {
47-
// Since we use injected kernel boot, and don't build full disk image.
48-
return reproduceResult{}, errors.New("only qemu VM type is supported")
47+
return "", "", errors.New("only qemu VM type is supported")
4948
}
49+
50+
var vmConfig map[string]any
51+
if err := json.Unmarshal(args.VM, &vmConfig); err != nil {
52+
return "", "", fmt.Errorf("failed to parse VM config: %w", err)
53+
}
54+
vmConfig["kernel"] = filepath.Join(args.KernelObj, filepath.FromSlash(build.LinuxKernelImage(targets.AMD64)))
55+
vmCfg, err := json.Marshal(vmConfig)
56+
if err != nil {
57+
return "", "", fmt.Errorf("failed to serialize VM config: %w", err)
58+
}
59+
60+
cfg := mgrconfig.DefaultValues()
61+
cfg.RawTarget = "linux/amd64"
62+
cfg.Workdir = workdir
63+
cfg.Syzkaller = args.Syzkaller
64+
cfg.KernelObj = args.KernelObj
65+
cfg.KernelSrc = args.KernelSrc
66+
cfg.Image = args.Image
67+
cfg.Type = args.Type
68+
cfg.VM = vmCfg
69+
if err := mgrconfig.SetTargets(cfg); err != nil {
70+
return "", "", err
71+
}
72+
if err := mgrconfig.Complete(cfg); err != nil {
73+
return "", "", err
74+
}
75+
env, err := instance.NewEnv(cfg, nil, nil)
76+
if err != nil {
77+
return "", "", err
78+
}
79+
results, err := env.Test(1, nil, nil, []byte(args.ReproC))
80+
if err != nil {
81+
return "", "", err
82+
}
83+
if results[0].Error != nil {
84+
if crashErr := new(instance.CrashError); errors.As(results[0].Error, &crashErr) {
85+
return string(crashErr.Report.Report), "", nil
86+
} else {
87+
return "", results[0].Error.Error(), nil
88+
}
89+
}
90+
return "", "", nil
91+
}
92+
93+
func reproduce(ctx *aflow.Context, args ReproduceArgs) (reproduceResult, error) {
5094
imageData, err := os.ReadFile(args.Image)
5195
if err != nil {
5296
return reproduceResult{}, err
@@ -61,50 +105,12 @@ func reproduce(ctx *aflow.Context, args reproduceArgs) (reproduceResult, error)
61105
}
62106
cached, err := aflow.CacheObject(ctx, "repro", desc, func() (Cached, error) {
63107
var res Cached
64-
var vmConfig map[string]any
65-
if err := json.Unmarshal(args.VM, &vmConfig); err != nil {
66-
return res, fmt.Errorf("failed to parse VM config: %w", err)
67-
}
68-
vmConfig["kernel"] = filepath.Join(args.KernelObj, filepath.FromSlash(build.LinuxKernelImage(targets.AMD64)))
69-
vmCfg, err := json.Marshal(vmConfig)
70-
if err != nil {
71-
return res, fmt.Errorf("failed to serialize VM config: %w", err)
72-
}
73108
workdir, err := ctx.TempDir()
74109
if err != nil {
75110
return res, err
76111
}
77-
cfg := mgrconfig.DefaultValues()
78-
cfg.RawTarget = "linux/amd64"
79-
cfg.Workdir = workdir
80-
cfg.Syzkaller = args.Syzkaller
81-
cfg.KernelObj = args.KernelObj
82-
cfg.KernelSrc = args.KernelSrc
83-
cfg.Image = args.Image
84-
cfg.Type = args.Type
85-
cfg.VM = vmCfg
86-
if err := mgrconfig.SetTargets(cfg); err != nil {
87-
return res, err
88-
}
89-
if err := mgrconfig.Complete(cfg); err != nil {
90-
return res, err
91-
}
92-
env, err := instance.NewEnv(cfg, nil, nil)
93-
if err != nil {
94-
return res, err
95-
}
96-
results, err := env.Test(1, nil, nil, []byte(args.ReproC))
97-
if err != nil {
98-
return res, err
99-
}
100-
if results[0].Error != nil {
101-
if crashErr := new(instance.CrashError); errors.As(results[0].Error, &crashErr) {
102-
res.Report = string(crashErr.Report.Report)
103-
} else {
104-
res.Error = results[0].Error.Error()
105-
}
106-
}
107-
return res, nil
112+
res.Error, res.Report, err = ReproduceCrash(args, workdir)
113+
return res, err
108114
})
109115
if err != nil {
110116
return reproduceResult{}, err

pkg/aflow/action/crash/test.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ package crash
55

66
import (
77
"encoding/json"
8+
"fmt"
9+
"time"
810

911
"github.com/google/syzkaller/pkg/aflow"
12+
"github.com/google/syzkaller/pkg/aflow/action/kernel"
13+
"github.com/google/syzkaller/pkg/osutil"
1014
)
1115

1216
// TestPatch action does an in-tree kernel build in KernelScratchSrc dir,
@@ -36,5 +40,71 @@ type testResult struct {
3640
}
3741

3842
func testPatch(ctx *aflow.Context, args testArgs) (testResult, error) {
39-
return testResult{}, nil
43+
res := testResult{}
44+
defer undoChanges(args.KernelScratchSrc)
45+
46+
diff, err := currentDiff(args.KernelScratchSrc)
47+
if err != nil {
48+
return res, err
49+
}
50+
res.PatchDiff = diff
51+
52+
if err := kernel.BuildKernel(args.KernelScratchSrc, args.KernelScratchSrc, args.KernelConfig, false); err != nil {
53+
res.TestError = fmt.Sprintf("Building the kernel failed with %v", err)
54+
return res, nil
55+
}
56+
57+
workdir, err := ctx.TempDir()
58+
if err != nil {
59+
return res, err
60+
}
61+
reproduceArgs := ReproduceArgs{
62+
Syzkaller: args.Syzkaller,
63+
Image: args.Image,
64+
Type: args.Type,
65+
VM: args.VM,
66+
ReproOpts: args.ReproOpts,
67+
ReproSyz: args.ReproSyz,
68+
ReproC: args.ReproC,
69+
SyzkallerCommit: args.SyzkallerCommit,
70+
KernelSrc: args.KernelScratchSrc,
71+
KernelObj: args.KernelScratchSrc,
72+
KernelCommit: args.KernelCommit,
73+
KernelConfig: args.KernelConfig,
74+
}
75+
errorLog, reportLog, err := ReproduceCrash(reproduceArgs, workdir)
76+
if errorLog != "" {
77+
res.TestError = errorLog
78+
} else {
79+
res.TestError = reportLog
80+
}
81+
return res, err
82+
}
83+
84+
func currentDiff(repo string) (string, error) {
85+
// Mark the "intent to add" on all files so git diff also shows currently untracked files.
86+
_, err := osutil.RunCmd(time.Minute, repo, "git", "add", "-N", ".")
87+
if err != nil {
88+
return "", err
89+
}
90+
diff, err := osutil.RunCmd(time.Minute, repo, "git", "diff")
91+
if err != nil {
92+
return "", err
93+
}
94+
return string(diff), nil
95+
}
96+
97+
func undoChanges(repo string) error {
98+
// Unset the "intent to add", otherwise git clean doesn't remove these files.
99+
_, err := osutil.RunCmd(time.Minute, repo, "git", "reset")
100+
if err != nil {
101+
return err
102+
}
103+
_, err = osutil.RunCmd(time.Minute, repo, "git", "checkout", "--", ".")
104+
if err != nil {
105+
return err
106+
}
107+
// We do not use -fdx to keep object files around and make the next tool call faster.
108+
_, err = osutil.RunCmd(time.Minute, repo, "git", "clean", "-fd")
109+
return err
40110
}

pkg/aflow/action/kernel/build.go

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,42 +34,49 @@ type buildResult struct {
3434
KernelObj string // Directory with build artifacts.
3535
}
3636

37-
func buildKernel(ctx *aflow.Context, args buildArgs) (buildResult, error) {
38-
desc := fmt.Sprintf("kernel commit %v, kernel config hash %v",
39-
args.KernelCommit, hash.String(args.KernelConfig))
40-
dir, err := ctx.Cache("build", desc, func(dir string) error {
41-
if err := osutil.WriteFile(filepath.Join(dir, ".config"), []byte(args.KernelConfig)); err != nil {
37+
func BuildKernel(buildDir, srcDir, cfg string, cleanup bool) error {
38+
if err := osutil.WriteFile(filepath.Join(buildDir, ".config"), []byte(cfg)); err != nil {
39+
return err
40+
}
41+
target := targets.List[targets.Linux][targets.AMD64]
42+
image := filepath.FromSlash(build.LinuxKernelImage(targets.AMD64))
43+
makeArgs := build.LinuxMakeArgs(target, targets.DefaultLLVMCompiler, targets.DefaultLLVMLinker,
44+
"ccache", buildDir, runtime.NumCPU())
45+
const compileCommands = "compile_commands.json"
46+
makeArgs = append(makeArgs, "-s", path.Base(image), compileCommands)
47+
if out, err := osutil.RunCmd(time.Hour, srcDir, "make", makeArgs...); err != nil {
48+
return aflow.FlowError(fmt.Errorf("make failed: %w\n%s", err, out))
49+
}
50+
if !cleanup {
51+
return nil
52+
}
53+
// Remove main intermediate build files, we don't need them anymore
54+
// and they take lots of space. But keep generated source files.
55+
keepFiles := map[string]bool{
56+
image: true,
57+
target.KernelObject: true,
58+
compileCommands: true,
59+
}
60+
return filepath.WalkDir(buildDir, func(path string, d fs.DirEntry, err error) error {
61+
if err != nil {
4262
return err
4363
}
44-
target := targets.List[targets.Linux][targets.AMD64]
45-
image := filepath.FromSlash(build.LinuxKernelImage(targets.AMD64))
46-
makeArgs := build.LinuxMakeArgs(target, targets.DefaultLLVMCompiler, targets.DefaultLLVMLinker,
47-
"ccache", dir, runtime.NumCPU())
48-
const compileCommands = "compile_commands.json"
49-
makeArgs = append(makeArgs, "-s", path.Base(image), compileCommands)
50-
if out, err := osutil.RunCmd(time.Hour, args.KernelSrc, "make", makeArgs...); err != nil {
51-
return aflow.FlowError(fmt.Errorf("make failed: %w\n%s", err, out))
64+
relative, err := filepath.Rel(buildDir, path)
65+
if err != nil {
66+
return err
5267
}
53-
// Remove main intermediate build files, we don't need them anymore
54-
// and they take lots of space. But keep generated source files.
55-
keepFiles := map[string]bool{
56-
image: true,
57-
target.KernelObject: true,
58-
compileCommands: true,
68+
if d.IsDir() || keepFiles[relative] || codesearch.IsSourceFile(relative) {
69+
return nil
5970
}
60-
return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
61-
if err != nil {
62-
return err
63-
}
64-
relative, err := filepath.Rel(dir, path)
65-
if err != nil {
66-
return err
67-
}
68-
if d.IsDir() || keepFiles[relative] || codesearch.IsSourceFile(relative) {
69-
return nil
70-
}
71-
return os.Remove(path)
72-
})
71+
return os.Remove(path)
72+
})
73+
}
74+
75+
func buildKernel(ctx *aflow.Context, args buildArgs) (buildResult, error) {
76+
desc := fmt.Sprintf("kernel commit %v, kernel config hash %v",
77+
args.KernelCommit, hash.String(args.KernelConfig))
78+
dir, err := ctx.Cache("build", desc, func(dir string) error {
79+
return BuildKernel(dir, args.KernelSrc, args.KernelConfig, true)
7380
})
7481
return buildResult{KernelObj: dir}, err
7582
}

0 commit comments

Comments
 (0)