aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2024-08-23 14:30:37 +0200
committerAleksandr Nogikh <nogikh@google.com>2024-08-27 13:41:43 +0000
commit9085be7e30d32bb8b2464e04100f8974c04aaae3 (patch)
tree1887a3c871b78aa22bf0b44a21ee48d806d78bdb /pkg
parent77187656728a5d9458f5a7ba82fc4b9b2704e37f (diff)
pkg/repro: be strict about titles during opt simplifications
Ideally, we should be mindful of that during the whole repro process, but there's always a chance that different titles are the manifestations of the same problem. So let's stay tolerant to different titles during prog extraction and minimization, but carefully check them during opt simplifications and C repro extraction.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/repro/repro.go86
1 files changed, 48 insertions, 38 deletions
diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go
index db943a9b8..d2076ffbf 100644
--- a/pkg/repro/repro.go
+++ b/pkg/repro/repro.go
@@ -45,19 +45,20 @@ type Stats struct {
}
type reproContext struct {
- exec execInterface
- logf func(string, ...interface{})
- target *targets.Target
- crashTitle string
- crashType crash.Type
- crashStart int
- crashExecutor *report.ExecutorInfo
- entries []*prog.LogEntry
- testTimeouts []time.Duration
- startOpts csource.Options
- stats *Stats
- report *report.Report
- timeouts targets.Timeouts
+ exec execInterface
+ logf func(string, ...interface{})
+ target *targets.Target
+ crashTitle string
+ crashType crash.Type
+ crashStart int
+ crashExecutor *report.ExecutorInfo
+ entries []*prog.LogEntry
+ testTimeouts []time.Duration
+ startOpts csource.Options
+ stats *Stats
+ report *report.Report
+ timeouts targets.Timeouts
+ observedTitles map[string]bool
}
// execInterface describes the interfaces needed by pkg/repro.
@@ -126,11 +127,12 @@ func prepareCtx(crashLog []byte, cfg *mgrconfig.Config, features flatrpc.Feature
crashStart: crashStart,
crashExecutor: crashExecutor,
- entries: entries,
- testTimeouts: testTimeouts,
- startOpts: createStartOptions(cfg, features, crashType),
- stats: new(Stats),
- timeouts: cfg.Timeouts,
+ entries: entries,
+ testTimeouts: testTimeouts,
+ startOpts: createStartOptions(cfg, features, crashType),
+ stats: new(Stats),
+ timeouts: cfg.Timeouts,
+ observedTitles: map[string]bool{},
}
ctx.reproLogf(0, "%v programs, timeouts %v", len(entries), testTimeouts)
return ctx, nil
@@ -148,9 +150,9 @@ func (ctx *reproContext) run() (*Result, *Stats, error) {
for attempts := 0; ctx.report.Corrupted && attempts < 3; attempts++ {
ctx.reproLogf(3, "report is corrupted, running repro again")
if res.CRepro {
- _, err = ctx.testCProg(res.Prog, res.Duration, res.Opts)
+ _, err = ctx.testCProg(res.Prog, res.Duration, res.Opts, false)
} else {
- _, err = ctx.testProg(res.Prog, res.Duration, res.Opts)
+ _, err = ctx.testProg(res.Prog, res.Duration, res.Opts, false)
}
if err != nil {
return nil, nil, err
@@ -328,7 +330,7 @@ func (ctx *reproContext) extractProgSingle(entries []*prog.LogEntry, duration ti
opts := ctx.startOpts
for _, ent := range entries {
- ret, err := ctx.testProg(ent.P, duration, opts)
+ ret, err := ctx.testProg(ent.P, duration, opts, false)
if err != nil {
return nil, err
}
@@ -356,7 +358,7 @@ func (ctx *reproContext) extractProgBisect(entries []*prog.LogEntry, baseDuratio
}
// First check if replaying the log may crash the kernel at all.
- ret, err := ctx.testProgs(entries, duration(len(entries)), opts)
+ ret, err := ctx.testProgs(entries, duration(len(entries)), opts, false)
if !ret.Crashed {
ctx.reproLogf(3, "replaying the whole log did not cause a kernel crash")
return nil, nil
@@ -367,7 +369,7 @@ func (ctx *reproContext) extractProgBisect(entries []*prog.LogEntry, baseDuratio
// Bisect the log to find multiple guilty programs.
entries, err = ctx.bisectProgs(entries, func(progs []*prog.LogEntry) (bool, error) {
- ret, err := ctx.testProgs(progs, duration(len(progs)), opts)
+ ret, err := ctx.testProgs(progs, duration(len(progs)), opts, false)
return ret.Crashed, err
})
if err != nil {
@@ -410,7 +412,7 @@ func (ctx *reproContext) concatenateProgs(entries []*prog.LogEntry, dur time.Dur
if i+1 < len(entries) {
newEntries = append(newEntries, entries[i+1:]...)
}
- ret, err := ctx.testProgs(newEntries, dur, ctx.startOpts)
+ ret, err := ctx.testProgs(newEntries, dur, ctx.startOpts, false)
if err != nil {
ctx.reproLogf(0, "concatenation step failed with %v", err)
return false
@@ -430,7 +432,7 @@ func (ctx *reproContext) concatenateProgs(entries []*prog.LogEntry, dur time.Dur
ctx.reproLogf(2, "bisect: concatenated prog still exceeds %d calls", prog.MaxCalls)
return nil, nil
}
- ret, err := ctx.testProg(p, dur, ctx.startOpts)
+ ret, err := ctx.testProg(p, dur, ctx.startOpts, false)
if err != nil {
ctx.reproLogf(3, "bisect: error during concatenation testing: %v", err)
return nil, err
@@ -462,7 +464,7 @@ func (ctx *reproContext) minimizeProg(res *Result) (*Result, error) {
// will immediately exit.
return false
}
- ret, err := ctx.testProg(p1, res.Duration, res.Opts)
+ ret, err := ctx.testProg(p1, res.Duration, res.Opts, false)
if err != nil {
ctx.reproLogf(0, "minimization failed with %v", err)
return false
@@ -487,7 +489,7 @@ func (ctx *reproContext) simplifyProg(res *Result) (*Result, error) {
if !simplify(&opts) || !checkOpts(&opts, ctx.timeouts, res.Duration) {
continue
}
- ret, err := ctx.testProg(res.Prog, res.Duration, opts)
+ ret, err := ctx.testProg(res.Prog, res.Duration, opts, true)
if err != nil {
return nil, err
}
@@ -516,7 +518,7 @@ func (ctx *reproContext) extractC(res *Result) (*Result, error) {
ctx.stats.ExtractCTime = time.Since(start)
}()
- ret, err := ctx.testCProg(res.Prog, res.Duration, res.Opts)
+ ret, err := ctx.testCProg(res.Prog, res.Duration, res.Opts, true)
if err != nil {
return nil, err
}
@@ -537,7 +539,7 @@ func (ctx *reproContext) simplifyC(res *Result) (*Result, error) {
if !simplify(&opts) || !checkOpts(&opts, ctx.timeouts, res.Duration) {
continue
}
- ret, err := ctx.testCProg(res.Prog, res.Duration, opts)
+ ret, err := ctx.testCProg(res.Prog, res.Duration, opts, true)
if err != nil {
return nil, err
}
@@ -572,10 +574,10 @@ func checkOpts(opts *csource.Options, timeouts targets.Timeouts, timeout time.Du
return true
}
-func (ctx *reproContext) testProg(p *prog.Prog, duration time.Duration,
- opts csource.Options) (ret verdict, err error) {
+func (ctx *reproContext) testProg(p *prog.Prog, duration time.Duration, opts csource.Options,
+ strict bool) (ret verdict, err error) {
entry := prog.LogEntry{P: p}
- return ctx.testProgs([]*prog.LogEntry{&entry}, duration, opts)
+ return ctx.testProgs([]*prog.LogEntry{&entry}, duration, opts, strict)
}
type verdict struct {
@@ -583,7 +585,7 @@ type verdict struct {
Duration time.Duration
}
-func (ctx *reproContext) getVerdict(callback func() (rep *instance.RunResult, err error)) (
+func (ctx *reproContext) getVerdict(callback func() (rep *instance.RunResult, err error), strict bool) (
verdict, error) {
var result *instance.RunResult
var err error
@@ -614,6 +616,14 @@ func (ctx *reproContext) getVerdict(callback func() (rep *instance.RunResult, er
ctx.reproLogf(2, "not a leak crash: %v", rep.Title)
return verdict{false, result.Duration}, nil
}
+ if strict && len(ctx.observedTitles) > 0 {
+ if !ctx.observedTitles[rep.Title] {
+ ctx.reproLogf(2, "a never seen crash title: %v, ignore", rep.Title)
+ return verdict{false, result.Duration}, nil
+ }
+ } else {
+ ctx.observedTitles[rep.Title] = true
+ }
ctx.report = rep
return verdict{true, result.Duration}, nil
}
@@ -631,8 +641,8 @@ func encodeEntries(entries []*prog.LogEntry) []byte {
return buf.Bytes()
}
-func (ctx *reproContext) testProgs(entries []*prog.LogEntry, duration time.Duration, opts csource.Options) (
- ret verdict, err error) {
+func (ctx *reproContext) testProgs(entries []*prog.LogEntry, duration time.Duration, opts csource.Options,
+ strict bool) (ret verdict, err error) {
if len(entries) == 0 {
return ret, fmt.Errorf("no programs to execute")
}
@@ -652,14 +662,14 @@ func (ctx *reproContext) testProgs(entries []*prog.LogEntry, duration time.Durat
ctx.reproLogf(3, "detailed listing:\n%s", pstr)
return ctx.getVerdict(func() (*instance.RunResult, error) {
return ctx.exec.RunSyzProg(pstr, duration, opts, instance.SyzExitConditions)
- })
+ }, strict)
}
func (ctx *reproContext) testCProg(p *prog.Prog, duration time.Duration,
- opts csource.Options) (ret verdict, err error) {
+ opts csource.Options, strict bool) (ret verdict, err error) {
return ctx.getVerdict(func() (*instance.RunResult, error) {
return ctx.exec.RunCProg(p, duration, opts)
- })
+ }, strict)
}
func (ctx *reproContext) reproLogf(level int, format string, args ...interface{}) {