Header

Fuzzing is a technique which automatically generates input values for your testing functions.

Why it’s called Fuzzing?

The term fuzz was created by Professor Barton Miller in the 1980s. Logged into a UNIX system via a dial-up network during a storm, Miller noticed considerable interference on the signal. The interference ultimately resulted in a crash. Miller later had his students perform a simulation of his experience using a fuzz generator to bombard UNIX systems with noise to see if they would crash.

Now, fuzzing is a part of the built-in testing library in Go 1.18.

Fuzzing main components

  • Fuzz Test

    A function in a test file which can be used for fuzzing and has no return value.

    func FuzzFoo(f *testing.F) {
    
    }
    
  • Fuzz Target

    The function of the fuzz test which is executed while fuzzing. It is provided to the fuzz test by passing the function to (*testing.F).Fuzz. There must be exactly one fuzz target per fuzz test.

    func Foo(a int, b int) {
      println(a, b)
    }
    
    // fuzz test
    func FuzzFoo(f *testing.F) {
    
      // fuzz target
      f.Fuzz(func(t *testing.T, a int, b int) {
          
      })
    
    }
    
  • Fuzzing Arguments

    The types which will be passed to the fuzz target, and mutated by the mutator. The fuzzing arguments can only be the following types:

    • string, []byte
    • int, int8, int16, int32/rune, int64
    • uint, uint8/byte, uint16, uint32, uint64
    • float32, float64
    • bool
    (*testing.F).Fuzz(func(t *testing.T, a int, b int) {
    
    })
    
  • Seed Corpus

    A user-provided corpus for a fuzz test which can be used to guide the fuzzing engine. It is composed of the corpus entries provided by f.Add calls within the fuzz test, and the files in the testdata/fuzz/{FuzzTestName} directory within the package. These entries are run by default with go test, whether fuzzing or not. All seed corpus entries must have types which are identical to the fuzzing arguments, in the same order.

    (*testing.F).Add(1, 2)
    (*testing.F).Add(1000, 2000)
    

Implementation in Golang

  • Write a function to fuzz

    write a method named equal to fuzz in main.go file

      // equal: a function to compare to byte slices
      func equal(a []byte, b []byte) bool {
          // compare lengths
          if len(a) != len(b) {
              return false
          }
            
          // compare elements
          for i := range a {
              if a[i] != b[i] {
                  return false
              }
          }
            
          return true
      }
    
  • Write fuzz test

    write test in main_test.go file

      // fuzz test
      func FuzzEqual(f *testing.F) {
    
          // seed corpus
          f.Add([]byte{1, 2, 3}, []byte{1, 2, 3})
                
          // fuzz target with fuzzing arguments
          f.Fuzz(func(t *testing.T, a []byte, b []byte) {
              equal(a, b)
          })
      }
    

How to Run?

  • To running, execute the following

      go test --fuzz=FuzzEqual
    

    It will keep generating inputs until you interrupt. To limit it, we can use --fuzztime parameter like this

      go test --fuzz=FuzzEqual --fuzztime=9s
    
  • Command line output

      fuzz: elapsed: 0s, gathering baseline coverage: 0/192 completed
      fuzz: elapsed: 0s, gathering baseline coverage: 192/192 completed, now fuzzing with 8 workers
      fuzz: elapsed: 3s, execs: 325017 (108336/sec), new interesting: 11 (total: 202)
      fuzz: elapsed: 6s, execs: 680218 (118402/sec), new interesting: 12 (total: 203)
      fuzz: elapsed: 9s, execs: 1039901 (119895/sec), new interesting: 19 (total: 210)
      fuzz: elapsed: 12s, execs: 1386684 (115594/sec), new interesting: 21 (total: 212)
      PASS
      ok      foo 12.692s
    
    • baseline coverage: the fuzzing engine executes both the seed corpus and the generated corpus, to ensure that no errors occurred and to understand the code coverage the existing corpus already provides.
    • generated corpus: A corpus which is maintained by the fuzzing engine over time while fuzzing to keep track of progress. It is stored in $GOCACHE/fuzz. These entries are only used while fuzzing.
    • elapsed: the amount of time that has elapsed since the process began
    • execs: the total number of inputs that have been run against the fuzz target (with an average execs/sec since the last log line)
    • new interesting: the total number of interesting inputs that have been added to the generated corpus during this fuzzing execution (with the total size of the entire corpus)
  • Failing input

    When an input cause a failure, it will be saved in testdata/fuzz/FuzzFoo/ directory.

References

Get the full source code from this github repository.

Header

Tell us what you think in comments below.