2020-06-19 13:31:20 +00:00
|
|
|
package watchdog
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"github.com/maride/afl-transmit/logistic"
|
|
|
|
"github.com/maride/afl-transmit/net"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2021-04-23 15:00:57 +00:00
|
|
|
rescan int
|
2020-06-19 13:31:20 +00:00
|
|
|
)
|
|
|
|
|
2021-04-23 15:00:57 +00:00
|
|
|
// RegisterWatchdogFlags registers required flags for the watchdog
|
2020-06-19 13:31:20 +00:00
|
|
|
func RegisterWatchdogFlags() {
|
2021-05-17 13:20:37 +00:00
|
|
|
flag.IntVar(&rescan, "rescan", 30, "Minutes to wait before rescanning local fuzzer directory")
|
2020-06-19 13:31:20 +00:00
|
|
|
}
|
|
|
|
|
2021-05-17 13:20:37 +00:00
|
|
|
// WatchFuzzers watches over the specified directory, sends updates to peers and re-scans after the specified amount of seconds
|
2020-06-19 13:31:20 +00:00
|
|
|
func WatchFuzzers(outputDirectory string) {
|
|
|
|
// Loop forever
|
|
|
|
for {
|
2021-05-17 13:20:37 +00:00
|
|
|
// Search for main fuzzer
|
|
|
|
targetFuzzer, targetErr := getTargetFuzzer(outputDirectory)
|
|
|
|
if targetErr != nil {
|
|
|
|
log.Printf("Failed to detect main fuzzer: %s", targetErr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pack important parts of the fuzzer into an archive
|
|
|
|
packedFuzzers, packerErr := logistic.PackFuzzer(targetFuzzer, outputDirectory)
|
2021-04-22 19:46:01 +00:00
|
|
|
if packerErr != nil {
|
|
|
|
log.Printf("Failed to pack fuzzer: %s", packerErr)
|
|
|
|
continue
|
2020-06-19 13:31:20 +00:00
|
|
|
}
|
|
|
|
|
2021-04-22 19:46:01 +00:00
|
|
|
// and send it to our peers
|
2021-04-26 10:22:11 +00:00
|
|
|
go net.SendToPeers(packedFuzzers)
|
2021-04-22 19:46:01 +00:00
|
|
|
|
2020-06-19 13:31:20 +00:00
|
|
|
// Sleep a bit
|
2021-04-23 15:00:57 +00:00
|
|
|
time.Sleep(time.Duration(rescan) * time.Minute)
|
2020-06-19 13:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-17 13:20:37 +00:00
|
|
|
// Searches in the specified output directory for the main fuzzer.
|
|
|
|
// Identifying the main fuzzer is done by searching for the file "is_main_node". On secondary-only servers, this relies
|
|
|
|
// on the "election process" done by secondary fuzzers if they don't find a local main node. In that election process, a
|
|
|
|
// secondary fuzzer becomes the main node in terms of syncing. That means we need to focus on the main node even if
|
|
|
|
// there is no real main node running locally. Good thing is that the elected node will create a "is_main_node" file, so
|
|
|
|
// we really just need to search for that.
|
2021-05-07 12:29:42 +00:00
|
|
|
// However it is important to avoid transmitting the is_main_node file - else, the secondaries will think that there is
|
|
|
|
// a main node running locally. Skipping the is_main_node file means the other fuzzers (and especially the elected main
|
2021-05-17 13:20:37 +00:00
|
|
|
// fuzzer) detect the transmitted fuzzer as a normal (and dead) secondary.
|
2021-04-22 18:47:07 +00:00
|
|
|
//
|
|
|
|
// (see AFLplusplus/src/afl-fuzz.run.c:542)
|
2021-05-17 13:20:37 +00:00
|
|
|
func getTargetFuzzer(outputDirectory string) (string, error) {
|
2021-05-07 12:29:42 +00:00
|
|
|
// find main fuzzer directory - its the only one required by the secondaries
|
2021-04-22 18:47:07 +00:00
|
|
|
|
2021-05-07 12:29:42 +00:00
|
|
|
// List files (read: fuzzers) in output directory
|
|
|
|
filesInDir, readErr := ioutil.ReadDir(outputDirectory)
|
|
|
|
if readErr != nil {
|
2021-05-17 13:20:37 +00:00
|
|
|
return "", fmt.Errorf("Failed to list directory content of %s: %s", outputDirectory, readErr)
|
2021-05-07 12:29:42 +00:00
|
|
|
}
|
2020-06-19 13:31:20 +00:00
|
|
|
|
2021-05-07 12:29:42 +00:00
|
|
|
for _, f := range filesInDir {
|
|
|
|
// Get stat for maybe-existent file
|
|
|
|
mainnodePath := fmt.Sprintf("%s%c%s%cis_main_node", outputDirectory, os.PathSeparator, f.Name(), os.PathSeparator)
|
|
|
|
_, statErr := os.Stat(mainnodePath)
|
|
|
|
if os.IsNotExist(statErr) {
|
|
|
|
// File does not exist. Not the ~~chosen one~~ main fuzzer. Next.
|
|
|
|
continue
|
|
|
|
} else if statErr != nil {
|
|
|
|
// An error occurred. File is maybe in a Schrödinger state.
|
|
|
|
log.Printf("Unable to stat file %s: %s", mainnodePath, statErr)
|
|
|
|
continue
|
2020-06-19 13:31:20 +00:00
|
|
|
}
|
|
|
|
|
2021-05-07 12:29:42 +00:00
|
|
|
// is_main_node file exists, so we found the correct fuzzer directory - return it
|
2021-05-17 13:20:37 +00:00
|
|
|
return fmt.Sprintf("%s%c%s", outputDirectory, os.PathSeparator, f.Name()), nil
|
2021-04-22 18:47:07 +00:00
|
|
|
}
|
2021-05-07 12:29:42 +00:00
|
|
|
|
|
|
|
// Failed to find the main node - probably we are in --main mode by accident
|
2021-05-17 13:20:37 +00:00
|
|
|
return "", fmt.Errorf("Unable to find main node in %s", outputDirectory)
|
2020-06-19 13:31:20 +00:00
|
|
|
}
|