Go version

go version go1.24.4 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/sontapaa_jokulainen/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/sontapaa_jokulainen/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1537132889=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/sontapaa_jokulainen/fuzz/archive_fuzzer/go.mod'
GOMODCACHE='/home/sontapaa_jokulainen/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/sontapaa_jokulainen/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/sontapaa_jokulainen/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.4'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

Hi!

I am trying to fuzz this program here:

// archive_test.go
package archivedifffuzz

import (
        "archive/zip"
        "bytes"
        "crypto/sha1"
        "encoding/hex"
        "fmt"
        "io"
        "os"
        "os/exec"
        "path/filepath"
        "strings"
        "testing"
        "runtime"
        "context"
        "time"
        "strconv"
)

type FileEntry struct {
        Name string
        Size int
}

func getLocalUnzipPath() string {
        _, filename, _, ok := runtime.Caller(0)
        if !ok {
         panic("unable to get caller info")
        }
        dir := filepath.Dir(filename)
        return filepath.Join(dir, "unzip")
}

func ExtractWithUnzipAndStatFiles(t *testing.T, zipData []byte) (map[string]int, string, error) {
        tmpDir, err := os.MkdirTemp("", "unzipped-*")
        if err != nil {
         return nil, "", err
        }

        tmpZip, err := os.CreateTemp("", "fuzz-*.zip")
        if err != nil {
         os.RemoveAll(tmpDir)
         return nil, "", err
        }
        defer os.Remove(tmpZip.Name())
        defer tmpZip.Close()

        if _, err := tmpZip.Write(zipData); err != nil {
         os.RemoveAll(tmpDir)
         return nil, "", err
        }

        cmd := exec.Command(getLocalUnzipPath(), "-o", tmpZip.Name(), "-d", tmpDir)


        var outBuf bytes.Buffer
        cmd.Stdout = &outBuf
        cmd.Stderr = &outBuf

        if err := cmd.Run(); err != nil {
         os.RemoveAll(tmpDir)
         return nil, "", fmt.Errorf("unzip failed!!!")
        }

        output := outBuf.String()
        if strings.Contains(output, "mismatch") {
         os.RemoveAll(tmpDir)
         return nil, "", fmt.Errorf("filename mismatch detected")
        }

        fileMap := make(map[string]int)
        err = filepath.Walk(tmpDir, func(path string, info os.FileInfo, err error) error {
         if err != nil {
          return err
         }
         if info.IsDir() {
          return nil
         }
         rel, err := filepath.Rel(tmpDir, path)
         if err != nil {
          return err
         }
         fileMap[rel] = int(info.Size())
         return nil
        })
        if err != nil {
         os.RemoveAll(tmpDir)
         return nil, "", err
        }


        return fileMap, tmpDir, nil
}
func LoadCorpus(f *testing.F) {
        files, _ := os.ReadDir("corpus/")
        for _, file := range files {
         if data, err := os.ReadFile("corpus/" + file.Name()); err == nil {
          f.Add(data)
         }
        }
}

func FuzzZipDifferential(f *testing.F) {
        f.Add([]byte("PK\x03\x04..."))
        LoadCorpus(f)

        f.Fuzz(func(t *testing.T, data []byte) {
         if len(data) > 100_000 {
          return
         }

         // runDifferentialTest(t, data)


         ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
         defer cancel()

         done := make(chan struct{})
         go func() {
          defer func() {

                if r := recover(); r != nil {
                  panic(r)
                }

                return
          }()

          runDifferentialTest(t, data)
          close(done)
         }()

         select {
         case <-done:
         case <-ctx.Done():
          return
          // panic("timeout")
         }




        })
}

func mapsEqual(a, b map[string]int) bool {
        if len(a) != len(b) {
         return false
        }
        for k, v := range a {
         if b[k] != v {
          return false
         }
        }
        return true
}

func isValidFilename(s string) bool {
        for _, r := range s {
         if r < 32 || r > 127 || r == '\n' || r == '\r' {
          return false
         }
        }
        return true
}

func findFileInZip(reader *zip.Reader, name string) *zip.File {
        for _, f := range reader.File {
         if f.Name == name {
          return f
         }
        }
        return nil
}

func IncrementCounterInFile(filename string) error {
        // Read existing file contents (if any)
        data, err := os.ReadFile(filename)
        count := 0
        if err == nil {
         trimmed := strings.TrimSpace(string(data))
         if trimmed != "" {
          count, err = strconv.Atoi(trimmed)
          if err != nil {
                return fmt.Errorf("invalid number in file: %v", err)
          }
         }
        } else if !os.IsNotExist(err) {
         return err
        }

        // Increment and write back
        count++
        return os.WriteFile(filename, []byte(fmt.Sprintf("%d\n", count)), 0644)
}

func runDifferentialTest(t *testing.T, data []byte) {

        /*
        unzipFiles, unzipDir, err := ExtractWithUnzipAndStatFiles(t, data)
        if err != nil || len(unzipFiles) == 0 {
         return
        }
        defer os.RemoveAll(unzipDir)
        */

        reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
        if err != nil {
         return
        }

        goFiles := make(map[string]int)
        for _, f := range reader.File {
         if !isValidFilename(f.Name) {
          return
         }
         rc, err := f.Open()
         if err != nil {
          return
         }
         n, err := io.Copy(io.Discard, rc)
         rc.Close()
         if err != nil {
          return
         }
         goFiles[f.Name] = int(n)
        }

        // IncrementCounterInFile("/home/YOURHOMEDIRECTORY/integer.txt")
        unzipFiles, unzipDir, err := ExtractWithUnzipAndStatFiles(t, data)
        if err != nil || len(unzipFiles) == 0 {
                // defer os.RemoveAll(unzipDir)
                // t.Errorf("Here is the error: %s", err.Error())
                // panic("fuck")
         return
        }
        // defer os.RemoveAll(unzipDir)

        common := 0
        for name, unzipSize := range unzipFiles {
         if goSize, ok := goFiles[name]; ok {
          common++
          if goSize != unzipSize {
                sum := sha1.Sum(data)
                t.Errorf("Size mismatch for file %s (Go: %d, Unzip: %d)", name, goSize, unzipSize)
                t.Errorf("SHA1: %x", sum)
                if entry := findFileInZip(reader, name); entry != nil {
                  rc, err := entry.Open()
                  if err == nil {
                   goData, _ := io.ReadAll(rc)
                   rc.Close()
                   t.Logf("=== Go version of %s ===\n%s", name, hex.Dump(goData))
                  }
                }
                unzipPath := filepath.Join(unzipDir, name)
                unzipData, err := os.ReadFile(unzipPath)
                if err == nil {
                  t.Logf("=== unzip version of %s ===\n%s", name, hex.Dump(unzipData))
                } else {
                  t.Logf("Failed to read %s from unzip dir: %v", name, err)
                }
                panic("size mismatch")
          }
         }
        }
        // panic("fuck")
        if !mapsEqual(unzipFiles, goFiles) {
         sum := sha1.Sum(data)
         t.Errorf("File mismatch (common: %d, Go: %d, Unzip: %d)", common, len(goFiles), len(unzipFiles))
         t.Errorf("SHA1: %x", sum)
         goOnly := []string{}
         unzipOnly := []string{}
         for name := range goFiles {
          if _, ok := unzipFiles[name]; !ok {
                goOnly = append(goOnly, name)
          }
         }
         for name := range unzipFiles {
          if _, ok := goFiles[name]; !ok {
                unzipOnly = append(unzipOnly, name)
          }
         }
         if len(goOnly) > 0 {
          t.Errorf("Files only in Go archive:\n%s", strings.Join(goOnly, "\n"))
         }
         if len(unzipOnly) > 0 {
          t.Errorf("Files only in Unzip archive:\n%s", strings.Join(unzipOnly, "\n"))
         }
         t.Logf("=== RAW FILENAME DUMP (Go) ===")
         for name := range goFiles {
          t.Logf("%q | bytes: % x", name, []byte(name))
         }
         t.Logf("=== RAW FILENAME DUMP (Unzip) ===")
         for name := range unzipFiles {
          t.Logf("%q | bytes: % x", name, []byte(name))
         }
         panic("filename mismatch")
        }
}

I run it with go test -fuzz=FuzzZipDifferential and then after a while I am getting this output here:

fuzz: elapsed: 38m37s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 38m40s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 38m43s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 38m46s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 38m49s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 38m52s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 38m55s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 38m58s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m1s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m4s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m7s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m10s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m13s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m16s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m19s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m22s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m25s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m28s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m31s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m34s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m37s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m40s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m43s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m46s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m49s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m52s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m55s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 39m58s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 40m1s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)
fuzz: elapsed: 40m4s, execs: 1548489 (0/sec), new interesting: 4 (total: 577)

in the output log. It claims that no executions are done, even though I added the timeout to the code. This leads me to believe that execution gets stuck somewhere else other than the fuzz target code. I have verified that I have disk space on my machine. I also should have adequate memory and processing power.

What did you see happen?

Fuzzing grinds to a halt for some odd reason.

What did you expect to see?

Fuzzing continues normally.