From 11a90c7ff4b59ba4a0d639c74fe8eb0fa0761f85 Mon Sep 17 00:00:00 2001 From: AdamKorcz Date: Sat, 7 Aug 2021 14:47:07 +0100 Subject: [PATCH 1/2] Fuzzing: Add experimental version of container fuzzer Signed-off-by: AdamKorcz --- contrib/fuzz/container_fuzzer.go | 100 +++++++++++++++++++++++++++++-- contrib/fuzz/oss_fuzz_build.sh | 2 + 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/contrib/fuzz/container_fuzzer.go b/contrib/fuzz/container_fuzzer.go index 0ed7501592ae..ed3fa9a6dc45 100644 --- a/contrib/fuzz/container_fuzzer.go +++ b/contrib/fuzz/container_fuzzer.go @@ -38,11 +38,69 @@ import ( "time" ) -func init() { - err := updatePathEnv() - if err != nil { - fmt.Println(err) +var ( + haveInstalledWget = false + haveDownloadedbinaries = false + haveExtractedBinaries = false + haveChangedPATH = false + haveInitialized = false +) + +// initInSteps() performs initialization in several steps +// The reason for spreading the initialization out in +// multiple steps is that each fuzz iteration can maximum +// take 25 seconds when running through OSS-fuzz. +// Should an iteration exceed that, then the fuzzer stops. +func initInSteps() bool { + // Install wget + if !haveInstalledWget { + cmd := exec.Command("apt-get", "install", "-y", "wget") + err := cmd.Run() + if err != nil { + return true + } + haveInstalledWget = true + return true + } + // Download binaries + if !haveDownloadedbinaries { + tarLink := "https://github.com/containerd/containerd/releases/download/v1.5.4/containerd-1.5.4-linux-amd64.tar.gz" + cmd := exec.Command("wget", tarLink) + err := cmd.Run() + if err != nil { + return true + } + haveDownloadedbinaries = true + return true + } + // Extract binaries + binariesDir := "/tmp/containerd-binaries" + if !haveExtractedBinaries { + err := os.MkdirAll(binariesDir, 0777) + if err != nil { + return true + } + cmd := exec.Command("tar", "xvf", "containerd-1.5.4-linux-amd64.tar.gz", "-C", binariesDir) + err = cmd.Run() + if err != nil { + return true + } + haveExtractedBinaries = true + return true + } + // Add binaries to $PATH: + if !haveChangedPATH { + oldPathEnv := os.Getenv("PATH") + newPathEnv := fmt.Sprintf("%s:%s/bin", oldPathEnv, binariesDir) + err := os.Setenv("PATH", newPathEnv) + if err != nil { + return true + } + haveChangedPATH = true + return true } + haveInitialized = true + return false } func tearDown() error { @@ -153,6 +211,7 @@ func updatePathEnv() error { if err != nil { return err } + haveInitialized = true return nil } @@ -327,12 +386,39 @@ func doFuzz(data []byte, shouldTearDown bool) int { return 1 } +// FuzzCreateContainerNoTearDown() implements a fuzzer +// similar to FuzzCreateContainerWithTearDown() and +// FuzzCreateContainerWithTearDown(), but it takes a +// different approach to the initialization. Where +// the other 2 fuzzers depend on the containerd binaries +// that were built manually, this fuzzer downloads them +// when starting a fuzz run. +// This fuzzer is experimental for now and is being run +// continuously by OSS-fuzz to collect feedback on +// its sustainability. +func FuzzNoTearDownWithDownload(data []byte) int { + if !haveInitialized { + shouldRestart := initInSteps() + if shouldRestart { + return 0 + } + } + ret := doFuzz(data, false) + return ret +} + // FuzzCreateContainerNoTearDown() implements a fuzzer // similar to FuzzCreateContainerWithTearDown() with // with one minor distinction: One tears down the // daemon after each iteration whereas the other doesn't. // The two fuzzers' performance will be compared over time. func FuzzCreateContainerNoTearDown(data []byte) int { + if !haveInitialized { + err := updatePathEnv() + if err != nil { + return 0 + } + } ret := doFuzz(data, false) return ret } @@ -342,6 +428,12 @@ func FuzzCreateContainerNoTearDown(data []byte) int { // FuzzCreateContainerWithTearDown tears down the daemon // after each iteration. func FuzzCreateContainerWithTearDown(data []byte) int { + if !haveInitialized { + err := updatePathEnv() + if err != nil { + return 0 + } + } ret := doFuzz(data, true) return ret } diff --git a/contrib/fuzz/oss_fuzz_build.sh b/contrib/fuzz/oss_fuzz_build.sh index 62d8382f3e12..f4fe3d5efad7 100755 --- a/contrib/fuzz/oss_fuzz_build.sh +++ b/contrib/fuzz/oss_fuzz_build.sh @@ -69,5 +69,7 @@ for i in $( ls *_test.go ); do mv $i ./${i%.*}_fuzz.go; done # Remove windows test to avoid double declarations: rm ./client_windows_test_fuzz.go +rm ./helpers_windows_test_fuzz.go compile_go_fuzzer . FuzzCreateContainerNoTearDown fuzz_create_container_no_teardown compile_go_fuzzer . FuzzCreateContainerWithTearDown fuzz_create_container_with_teardown +compile_go_fuzzer . FuzzNoTearDownWithDownload fuzz_no_teardown_with_download From 5e49ec27dc4ff4f619453578a624d8cc35b21630 Mon Sep 17 00:00:00 2001 From: AdamKorcz Date: Tue, 10 Aug 2021 12:48:33 +0100 Subject: [PATCH 2/2] Use http.Get to download binaries instead of exec.Command Signed-off-by: AdamKorcz --- contrib/fuzz/container_fuzzer.go | 51 ++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/contrib/fuzz/container_fuzzer.go b/contrib/fuzz/container_fuzzer.go index ed3fa9a6dc45..2433db6775bd 100644 --- a/contrib/fuzz/container_fuzzer.go +++ b/contrib/fuzz/container_fuzzer.go @@ -32,6 +32,7 @@ import ( "github.com/containerd/containerd/sys" "io" "io/ioutil" + "net/http" "os" "os/exec" "strings" @@ -39,48 +40,60 @@ import ( ) var ( - haveInstalledWget = false haveDownloadedbinaries = false haveExtractedBinaries = false haveChangedPATH = false haveInitialized = false + + downloadLink = "https://github.com/containerd/containerd/releases/download/v1.5.4/containerd-1.5.4-linux-amd64.tar.gz" + downloadPath = "/tmp/containerd-1.5.4-linux-amd64.tar.gz" + binariesDir = "/tmp/containerd-binaries" ) +// downloadFile downloads a file from a url +func downloadFile(filepath string, url string) (err error) { + + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} + // initInSteps() performs initialization in several steps // The reason for spreading the initialization out in // multiple steps is that each fuzz iteration can maximum // take 25 seconds when running through OSS-fuzz. // Should an iteration exceed that, then the fuzzer stops. func initInSteps() bool { - // Install wget - if !haveInstalledWget { - cmd := exec.Command("apt-get", "install", "-y", "wget") - err := cmd.Run() - if err != nil { - return true - } - haveInstalledWget = true - return true - } // Download binaries if !haveDownloadedbinaries { - tarLink := "https://github.com/containerd/containerd/releases/download/v1.5.4/containerd-1.5.4-linux-amd64.tar.gz" - cmd := exec.Command("wget", tarLink) - err := cmd.Run() + err := downloadFile(downloadPath, downloadLink) if err != nil { - return true + panic(err) } haveDownloadedbinaries = true - return true } // Extract binaries - binariesDir := "/tmp/containerd-binaries" if !haveExtractedBinaries { err := os.MkdirAll(binariesDir, 0777) if err != nil { return true } - cmd := exec.Command("tar", "xvf", "containerd-1.5.4-linux-amd64.tar.gz", "-C", binariesDir) + cmd := exec.Command("tar", "xvf", downloadPath, "-C", binariesDir) err = cmd.Run() if err != nil { return true @@ -91,7 +104,7 @@ func initInSteps() bool { // Add binaries to $PATH: if !haveChangedPATH { oldPathEnv := os.Getenv("PATH") - newPathEnv := fmt.Sprintf("%s:%s/bin", oldPathEnv, binariesDir) + newPathEnv := fmt.Sprintf("%s/bin:%s", binariesDir, oldPathEnv) err := os.Setenv("PATH", newPathEnv) if err != nil { return true