func main() { flag.Usage = func() { fmt.Fprintf( os.Stderr, "\n %s summarizes crashes in the given crashwalk databases by major.minor hash\n", path.Base(os.Args[0]), ) fmt.Fprintf( os.Stderr, " Usage: %s /path/to/crashwalk.db [db db ...]\n\n", path.Base(os.Args[0]), ) } flag.Parse() for _, arg := range flag.Args() { db, err := bolt.Open(arg, 0600, nil) if err != nil { log.Fatalf("failed to open DB (%s): %s", arg, err) } defer db.Close() db.View(func(tx *bolt.Tx) error { tx.ForEach(func(name []byte, b *bolt.Bucket) error { summaries := make(map[string]summary) b.ForEach(func(k, v []byte) error { ce := &crash.Entry{} err := proto.Unmarshal(v, ce) if err != nil { return err } s, ok := summaries[ce.Hash] if !ok { s = summary{detail: crashwalk.Summarize(crash.Crash{Entry: *ce})} } s.count++ summaries[ce.Hash] = s return nil }) for k, v := range summaries { fmt.Printf("(1 of %v) - Hash: %v\n", v.count, k) fmt.Println(v.detail) } return nil }) return nil }) } }
func main() { flag.Usage = func() { fmt.Fprintf( os.Stderr, "\n %s runs crashfiles with instrumentation and outputs results in various formats\n", path.Base(os.Args[0]), ) fmt.Fprintf( os.Stderr, " Usage: %s -root /path/to/afl-dir [-match pattern] -- /path/to/target -in @@ -out whatever\n", path.Base(os.Args[0]), ) fmt.Fprintf(os.Stderr, " ( @@ will be substituted for each crashfile )\n\n") flag.PrintDefaults() fmt.Fprintf(os.Stderr, "\n") } flag.Parse() if *crashRoot == "" { fmt.Fprintf(os.Stderr, " FATAL: No root directory given\n") flag.Usage() os.Exit(1) } if *matchPattern == "" { if *flagAfl { *matchPattern = "crashes.*id:" } else { *matchPattern = ".*" } } crashRegex, err := regexp.Compile(fmt.Sprintf("%s", *matchPattern)) if err != nil { fmt.Fprintf(os.Stderr, " FATAL: %s\n", err) flag.Usage() os.Exit(1) } var skipRegex *regexp.Regexp if *ignorePattern != "" { skipRegex, err = regexp.Compile(fmt.Sprintf("%s", *ignorePattern)) if err != nil { fmt.Fprintf(os.Stderr, " FATAL: %s\n", err) flag.Usage() os.Exit(1) } } // If the user hasn't supplied a skip regex AND they want AFL auto mode, // we supply a useful one. if skipRegex == nil && *flagAfl { // I, personally, name sync dirs from other fuzzers something.sync // This skips those, which might trip you up, although it seems // unlikely. The other directories it skips are the queue and hang // dirs, which means the walker doesn't need to visit each file in // those directories (quite a big speedup). // UPDATE: we also skip our own .cwtidy directory, if present. skipRegex = regexp.MustCompile(".sync/|queue/|hang/|.cwtidy/") } else { // when people use manual mode via -match is surprises them when the // .cwtidy directory is not ignored, so it makes sense to do that by // default (#6). They can always unignore it by manually specifying // something else with -ignore if they want to re-try tidied // crashfiles. skipRegex = regexp.MustCompile(".cwtidy/") } skipErr := errors.New("no match") filter := func(path string) error { if skipRegex != nil && skipRegex.MatchString(path) { // Whole directory will be exited. Note that the directory itself // (without the /) will first be evaluated as a file. This is why // .cwtidy was erroneously passing, since it matched crash.*id but not // .cwtidy/ (no trailing slash) return filepath.SkipDir } if crashRegex.MatchString(path) { return nil } return skipErr } command := flag.Args() if len(command) < 2 && !*flagAfl { fmt.Fprintf(os.Stderr, " FATAL: Minimum target command is: /path/to/target @@\n") flag.Usage() os.Exit(1) } runtime.GOMAXPROCS(*flagWorkers) var debugger crashwalk.Debugger switch *flagDebugger { default: fmt.Fprintf(os.Stderr, " FATAL: Unknown debugging engine %s, only [gdb lldb]\n", *flagDebugger) flag.Usage() os.Exit(1) case "gdb": debugger = &gdb.Engine{} case "lldb": debugger = &francis.Engine{} } switch *flagOutput { default: fmt.Fprintf(os.Stderr, " FATAL: Unknown output format %s, only [json pb text]\n", *flagOutput) flag.Usage() os.Exit(1) case "pb": case "json": case "text": } // If the user provided a template filename, create or check the base path // before we go any further. if *flagFile != "" { // Is the directory OK ( create if it doesn't exist ) base, _ := path.Split(*flagFile) err := os.MkdirAll(base, 0700) if err != nil { fmt.Fprintf(os.Stderr, " FATAL: failed sanity check for -f: %s\n", err) flag.Usage() os.Exit(1) } // Can we create files there? fd, err := os.Create(path.Join(base, "cwtriage.test")) if err != nil { fmt.Fprintf(os.Stderr, " FATAL: failed sanity check for -f: %s\n", err) flag.Usage() os.Exit(1) } // Clean up. fd.Close() err = os.Remove(path.Join(base, "cwtriage.test")) if err != nil { fmt.Fprintf(os.Stderr, " FATAL: failed sanity check for -f: %s\n", err) flag.Usage() os.Exit(1) } } config := crashwalk.CrashwalkConfig{ Command: command, Strict: *flagStrict, Debugger: debugger, Root: *crashRoot, FilterFunc: filter, Workers: *flagWorkers, IncludeSeen: *includeSeen, Afl: *flagAfl, MemoryLimit: *flagMem, Timeout: *flagTimeout, Tidy: *flagTidy, File: *flagFile, } cw, err := crashwalk.NewCrashwalk(config) if err != nil { log.Fatalf("Unable to create Crashwalk: %s", err) } var ticker <-chan (time.Time) if *flagEvery > 0 { ticker = time.Tick(time.Duration(*flagEvery) * time.Second) } for { ch := cw.Run() for crash := range ch { switch *flagOutput { case "text": fmt.Println(crashwalk.Summarize(crash)) case "pb": fmt.Println(crash.String()) case "json": j, err := json.Marshal(crash) if err != nil { log.Fatalf("[BUG]: Failed to marshal crash as JSON: %s", err) } fmt.Println(string(j)) default: log.Fatalf("[BUG] Unknown output format %q, how did we pass startup checks?", *flagOutput) } } if ticker == nil { break } <-ticker } }