Index: fuzzer/go/aggregator/aggregator.go |
diff --git a/fuzzer/go/aggregator/aggregator.go b/fuzzer/go/aggregator/aggregator.go |
index 937bf504439effb497637539d80fa17c04281297..9dacc39a8303b74dfc23414b24de9ea191ba1167 100644 |
--- a/fuzzer/go/aggregator/aggregator.go |
+++ b/fuzzer/go/aggregator/aggregator.go |
@@ -72,26 +72,22 @@ type Aggregator struct { |
bugReportCount int64 |
} |
-// AnalysisPackage is a generic holder for the functions needed to analyze |
-type AnalysisPackage struct { |
- Setup func(workingDirPath string) error |
- Analyze func(workingDirPath, pathToFile string) (uploadPackage, error) |
-} |
- |
const ( |
BAD_FUZZ = "bad" |
GREY_FUZZ = "grey" |
) |
+var ( |
+ CLANG_DEBUG = common.TEST_HARNESS_NAME + "_clang_debug" |
+ CLANG_RELEASE = common.TEST_HARNESS_NAME + "_clang_release" |
+ ASAN_DEBUG = common.TEST_HARNESS_NAME + "_asan_debug" |
+ ASAN_RELEASE = common.TEST_HARNESS_NAME + "_asan_release" |
+) |
+ |
// uploadPackage is a struct containing all the pieces of a fuzz that need to be uploaded to GCS |
type uploadPackage struct { |
- Name string |
- FilePath string |
- DebugDump string |
- DebugErr string |
- ReleaseDump string |
- ReleaseErr string |
- FileType string |
+ Data data.GCSPackage |
+ FilePath string |
// Must be BAD_FUZZ or GREY_FUZZ |
FuzzType string |
} |
@@ -116,6 +112,7 @@ func StartAggregator(s *storage.Client, category string) (*Aggregator, error) { |
forUpload: make(chan uploadPackage, 100), |
forBugReporting: make(chan bugReportingPackage, 100), |
MakeBugOnBadFuzz: false, |
+ UploadGreyFuzzes: false, |
monitoringShutdown: make(chan bool, 2), |
// aggregationShutdown needs to be created with a calculated capacity in start |
} |
@@ -133,16 +130,7 @@ func (agg *Aggregator) start() error { |
agg.analysisCount = 0 |
agg.uploadCount = 0 |
agg.bugReportCount = 0 |
- if _, err := fileutil.EnsureDirExists(agg.fuzzPath); err != nil { |
- return err |
- } |
- if _, err := fileutil.EnsureDirExists(agg.executablePath); err != nil { |
- return err |
- } |
- if err := common.BuildClangHarness("Debug", true); err != nil { |
- return err |
- } |
- if err := common.BuildClangHarness("Release", true); err != nil { |
+ if err := agg.buildAnalysisBinaries(); err != nil { |
return err |
} |
@@ -178,6 +166,44 @@ func (agg *Aggregator) start() error { |
return nil |
} |
+// buildAnalysisBinaries creates the 4 executables we need to perform analysis and makes a copy of |
+// them in the executablePath. We need (Debug,Release) x (Clang,ASAN). The copied binaries have |
+// a suffix like _clang_debug |
+func (agg *Aggregator) buildAnalysisBinaries() error { |
+ if _, err := fileutil.EnsureDirExists(agg.fuzzPath); err != nil { |
+ return err |
+ } |
+ if _, err := fileutil.EnsureDirExists(agg.executablePath); err != nil { |
+ return err |
+ } |
+ if err := common.BuildClangHarness("Debug", true); err != nil { |
+ return err |
+ } |
+ outPath := filepath.Join(config.Generator.SkiaRoot, "out") |
+ if err := fileutil.CopyExecutable(filepath.Join(outPath, "Debug", common.TEST_HARNESS_NAME), filepath.Join(agg.executablePath, CLANG_DEBUG)); err != nil { |
+ return err |
+ } |
+ if err := common.BuildClangHarness("Release", true); err != nil { |
+ return err |
+ } |
+ if err := fileutil.CopyExecutable(filepath.Join(outPath, "Release", common.TEST_HARNESS_NAME), filepath.Join(agg.executablePath, CLANG_RELEASE)); err != nil { |
+ return err |
+ } |
+ if err := common.BuildASANHarness("Debug", false); err != nil { |
+ return err |
+ } |
+ if err := fileutil.CopyExecutable(filepath.Join(outPath, "Debug", common.TEST_HARNESS_NAME), filepath.Join(agg.executablePath, ASAN_DEBUG)); err != nil { |
+ return err |
+ } |
+ if err := common.BuildASANHarness("Release", false); err != nil { |
+ return err |
+ } |
+ if err := fileutil.CopyExecutable(filepath.Join(outPath, "Release", common.TEST_HARNESS_NAME), filepath.Join(agg.executablePath, ASAN_RELEASE)); err != nil { |
+ return err |
+ } |
+ return nil |
+} |
+ |
// scanForNewCandidates runs scanHelper once every config.Aggregator.RescanPeriod, which scans the |
// config.Generator.AflOutputPath for new fuzzes. If scanHelper returns an error, this method |
// will terminate. |
@@ -245,6 +271,10 @@ func (agg *Aggregator) findBadFuzzPaths(alreadyFoundFuzzes *SortedStringSlice) ( |
scanPath := filepath.Join(config.Generator.AflOutputPath, agg.Category) |
aflDir, err := os.Open(scanPath) |
+ if os.IsNotExist(err) { |
+ glog.Warningf("Path to scan %s does not exist. Returning 0 found fuzzes", scanPath) |
+ return []string{}, nil |
+ } |
if err != nil { |
return nil, err |
} |
@@ -357,12 +387,17 @@ func (agg *Aggregator) setupAnalysis(workingDirPath string) error { |
return err |
} |
- // make a copy of the debug and release executables |
- basePath := filepath.Join(config.Generator.SkiaRoot, "out") |
- if err := fileutil.CopyExecutable(filepath.Join(basePath, "Debug", common.TEST_HARNESS_NAME), filepath.Join(workingDirPath, common.TEST_HARNESS_NAME+"_debug")); err != nil { |
+ // make a copy of the 4 executables that were made in buildAnalysisBinaries() |
+ if err := fileutil.CopyExecutable(filepath.Join(agg.executablePath, CLANG_DEBUG), filepath.Join(workingDirPath, CLANG_DEBUG)); err != nil { |
return err |
} |
- if err := fileutil.CopyExecutable(filepath.Join(basePath, "Release", common.TEST_HARNESS_NAME), filepath.Join(workingDirPath, common.TEST_HARNESS_NAME+"_release")); err != nil { |
+ if err := fileutil.CopyExecutable(filepath.Join(agg.executablePath, CLANG_RELEASE), filepath.Join(workingDirPath, CLANG_RELEASE)); err != nil { |
+ return err |
+ } |
+ if err := fileutil.CopyExecutable(filepath.Join(agg.executablePath, ASAN_DEBUG), filepath.Join(workingDirPath, ASAN_DEBUG)); err != nil { |
+ return err |
+ } |
+ if err := fileutil.CopyExecutable(filepath.Join(agg.executablePath, ASAN_RELEASE), filepath.Join(workingDirPath, ASAN_RELEASE)); err != nil { |
return err |
} |
return nil |
@@ -372,25 +407,38 @@ func (agg *Aggregator) setupAnalysis(workingDirPath string) error { |
// completion, it checks to see if the fuzz is a grey fuzz and sets the FuzzType accordingly. |
func (agg *Aggregator) analyze(workingDirPath, fileName string) (uploadPackage, error) { |
upload := uploadPackage{ |
- Name: fileName, |
- FileType: agg.Category, |
+ Data: data.GCSPackage{ |
+ Name: fileName, |
+ FuzzCategory: agg.Category, |
+ }, |
FuzzType: BAD_FUZZ, |
FilePath: filepath.Join(agg.fuzzPath, fileName), |
} |
- if dump, stderr, err := agg.performAnalysis(workingDirPath, common.TEST_HARNESS_NAME, upload.FilePath, true); err != nil { |
+ if dump, stderr, err := agg.performAnalysis(workingDirPath, CLANG_DEBUG, upload.FilePath); err != nil { |
return upload, err |
} else { |
- upload.DebugDump = dump |
- upload.DebugErr = stderr |
+ upload.Data.Debug.Dump = dump |
+ upload.Data.Debug.StdErr = stderr |
} |
- if dump, stderr, err := agg.performAnalysis(workingDirPath, common.TEST_HARNESS_NAME, upload.FilePath, false); err != nil { |
+ if dump, stderr, err := agg.performAnalysis(workingDirPath, CLANG_RELEASE, upload.FilePath); err != nil { |
return upload, err |
} else { |
- upload.ReleaseDump = dump |
- upload.ReleaseErr = stderr |
+ upload.Data.Release.Dump = dump |
+ upload.Data.Release.StdErr = stderr |
} |
- if r := data.ParseFuzzResult(upload.DebugDump, upload.DebugErr, upload.ReleaseDump, upload.ReleaseErr); r.Flags == data.DebugFailedGracefully|data.ReleaseFailedGracefully { |
+ // AddressSanitizer only outputs to stderr |
+ if _, stderr, err := agg.performAnalysis(workingDirPath, ASAN_DEBUG, upload.FilePath); err != nil { |
+ return upload, err |
+ } else { |
+ upload.Data.Debug.Asan = stderr |
+ } |
+ if _, stderr, err := agg.performAnalysis(workingDirPath, ASAN_RELEASE, upload.FilePath); err != nil { |
+ return upload, err |
+ } else { |
+ upload.Data.Release.Asan = stderr |
+ } |
+ if r := data.ParseGCSPackage(upload.Data); r.Debug.Flags == data.TerminatedGracefully && r.Release.Flags == data.TerminatedGracefully { |
upload.FuzzType = GREY_FUZZ |
} |
return upload, nil |
@@ -399,13 +447,7 @@ func (agg *Aggregator) analyze(workingDirPath, fileName string) (uploadPackage, |
// performAnalysis executes a command from the working dir specified using |
// AnalysisArgs for a given fuzz category. The crash dumps (which |
// come via standard out) and standard errors are recorded as strings. |
-func (agg *Aggregator) performAnalysis(workingDirPath, baseExecutableName, pathToFile string, isDebug bool) (string, string, error) { |
- suffix := "_release" |
- if isDebug { |
- suffix = "_debug" |
- } |
- |
- pathToExecutable := fmt.Sprintf("./%s%s", baseExecutableName, suffix) |
+func (agg *Aggregator) performAnalysis(workingDirPath, executableName, pathToFile string) (string, string, error) { |
var dump bytes.Buffer |
var stdErr bytes.Buffer |
@@ -413,13 +455,15 @@ func (agg *Aggregator) performAnalysis(workingDirPath, baseExecutableName, pathT |
// GNU timeout is used instead of the option on exec.Command because experimentation with the |
// latter showed evidence of that way leaking processes, which led to OOM errors. |
cmd := &exec.Command{ |
- Name: "timeout", |
- Args: common.AnalysisArgsFor(agg.Category, pathToExecutable, pathToFile), |
- LogStdout: false, |
- LogStderr: false, |
- Stdout: &dump, |
- Stderr: &stdErr, |
- Dir: workingDirPath, |
+ Name: "timeout", |
+ Args: common.AnalysisArgsFor(agg.Category, "./"+executableName, pathToFile), |
+ LogStdout: false, |
+ LogStderr: false, |
+ Stdout: &dump, |
+ Stderr: &stdErr, |
+ Dir: workingDirPath, |
+ InheritPath: true, |
+ Env: []string{common.ASAN_OPTIONS}, |
} |
//errors are fine/expected from this, as we are dealing with bad fuzzes |
@@ -471,7 +515,7 @@ func (agg *Aggregator) waitForUploads(identifier int) { |
case p := <-agg.forUpload: |
atomic.AddInt64(&agg.uploadCount, int64(1)) |
if !agg.UploadGreyFuzzes && p.FuzzType == GREY_FUZZ { |
- glog.Infof("[%s] Skipping upload of grey fuzz %s", agg.Category, p.Name) |
+ glog.Infof("[%s] Skipping upload of grey fuzz %s", agg.Category, p.Data.Name) |
continue |
} |
if err := agg.upload(p); err != nil { |
@@ -479,7 +523,7 @@ func (agg *Aggregator) waitForUploads(identifier int) { |
return |
} |
agg.forBugReporting <- bugReportingPackage{ |
- FuzzName: p.Name, |
+ FuzzName: p.Data.Name, |
CommitHash: config.Generator.SkiaVersion.Hash, |
IsBadFuzz: p.FuzzType == BAD_FUZZ, |
} |
@@ -493,27 +537,33 @@ func (agg *Aggregator) waitForUploads(identifier int) { |
// upload breaks apart the uploadPackage into its constituant parts and uploads them to GCS using |
// some helper methods. |
func (agg *Aggregator) upload(p uploadPackage) error { |
- glog.Infof("[%s] uploading %s with file %s and analysis bytes %d;%d;%d;%d", agg.Category, p.Name, p.FilePath, len(p.DebugDump), len(p.DebugErr), len(p.ReleaseDump), len(p.ReleaseErr)) |
+ glog.Infof("[%s] uploading %s with file %s and analysis bytes %d;%d;%d|%d;%d;%d", agg.Category, p.Data.Name, p.FilePath, len(p.Data.Debug.Asan), len(p.Data.Debug.Dump), len(p.Data.Debug.StdErr), len(p.Data.Release.Asan), len(p.Data.Release.Dump), len(p.Data.Release.StdErr)) |
- if err := agg.uploadBinaryFromDisk(p, p.Name, p.FilePath); err != nil { |
+ if err := agg.uploadBinaryFromDisk(p, p.Data.Name, p.FilePath); err != nil { |
return err |
} |
- if err := agg.uploadString(p, p.Name+"_debug.dump", p.DebugDump); err != nil { |
+ if err := agg.uploadString(p, p.Data.Name+"_debug.asan", p.Data.Debug.Asan); err != nil { |
return err |
} |
- if err := agg.uploadString(p, p.Name+"_debug.err", p.DebugErr); err != nil { |
+ if err := agg.uploadString(p, p.Data.Name+"_debug.dump", p.Data.Debug.Dump); err != nil { |
return err |
} |
- if err := agg.uploadString(p, p.Name+"_release.dump", p.ReleaseDump); err != nil { |
+ if err := agg.uploadString(p, p.Data.Name+"_debug.err", p.Data.Debug.StdErr); err != nil { |
return err |
} |
- return agg.uploadString(p, p.Name+"_release.err", p.ReleaseErr) |
+ if err := agg.uploadString(p, p.Data.Name+"_release.asan", p.Data.Release.Asan); err != nil { |
+ return err |
+ } |
+ if err := agg.uploadString(p, p.Data.Name+"_release.dump", p.Data.Release.Dump); err != nil { |
+ return err |
+ } |
+ return agg.uploadString(p, p.Data.Name+"_release.err", p.Data.Release.StdErr) |
} |
// uploadBinaryFromDisk uploads a binary file on disk to GCS, returning an error if anything |
// goes wrong. |
func (agg *Aggregator) uploadBinaryFromDisk(p uploadPackage, fileName, filePath string) error { |
- name := fmt.Sprintf("%s/%s/%s/%s/%s", p.FileType, config.Generator.SkiaVersion.Hash, p.FuzzType, p.Name, fileName) |
+ name := fmt.Sprintf("%s/%s/%s/%s/%s", p.Data.FuzzCategory, config.Generator.SkiaVersion.Hash, p.FuzzType, p.Data.Name, fileName) |
w := agg.storageClient.Bucket(config.GS.Bucket).Object(name).NewWriter(context.Background()) |
defer util.Close(w) |
// We set the encoding to avoid accidental crashes if Chrome were to try to render a fuzzed png |
@@ -534,7 +584,7 @@ func (agg *Aggregator) uploadBinaryFromDisk(p uploadPackage, fileName, filePath |
// uploadBinaryFromDisk uploads the contents of a string as a file to GCS, returning an error if |
// anything goes wrong. |
func (agg *Aggregator) uploadString(p uploadPackage, fileName, contents string) error { |
- name := fmt.Sprintf("%s/%s/%s/%s/%s", p.FileType, config.Generator.SkiaVersion.Hash, p.FuzzType, p.Name, fileName) |
+ name := fmt.Sprintf("%s/%s/%s/%s/%s", p.Data.FuzzCategory, config.Generator.SkiaVersion.Hash, p.FuzzType, p.Data.Name, fileName) |
w := agg.storageClient.Bucket(config.GS.Bucket).Object(name).NewWriter(context.Background()) |
defer util.Close(w) |
w.ObjectAttrs.ContentEncoding = "text/plain" |