'How to set constraint on input for fuzzing?

Assume I have the following structure

type Hdr struct{
  Src      uint16
  Dst      uint16
  Priotity byte
  Pktcnt   byte
  Opcode   byte
  Ver      byte
}

I have two functions Marshal and Unmarshal that encode Hdr to and from a binary format of:

 0                   1          
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              Src              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              Dst              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Prio |  Cnt  | Opcode|  Ver  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

I'd like to use Go Fuzz to make random, valid Hdr instances, Marshal then to binary, Unmarshal the binary and make sure the output matches the original input.

The main issue I am having is that I cannot figure out how to tell Go Fuzz that fields like Priotity cannot be greater than 15 otherwise they will get truncated when they are marshalled (only 4 bits). How do I set this constraint?

Update

This is just a toy case. There are many times with protocols like the above where something like the opcode would trigger secondary more complex parsing/vetting. Fuzzing could still find very useful issues within a constraint (IE: if Prio 0x00 and Cnt 0x2F secondary parser will error because delimiter is \ ).



Solution 1:[1]

EDIT

I'm not sure Fuzzing is a good fit here. Fuzzing is designed to find unexpected inputs: multi-byte UTF8 inputs (valid and non-valid); negative values; huge values, long lengths etc. These will try to catch "edge" cases.

In your case here, you know the:

  • Unmarshal input payload must be 6 bytes (should error otherwise)
  • you know precisely your internal "edges"

so vanilla testing.T tests may be a better fit here.


Keep it simple.

If you don't want to "waste" a Fuzz input & you know the input constraints of your code, you can try something like this:

func coerce(h *Hdr) (skip bool) {

    h.Priotity &= 0x0f // ensure priority is 0-15
    h.OpCode %= 20     // ensure opcode is 0-19 

    return false       // optionally skip this test
}

and in your test - the coerced value can be tested - or skipped (as @jch showed):

import "github.com/google/go-cmp/cmp"

f.Fuzz(func(t *testing.T, src, dst uint16, pri, count, op, ver byte) {
    h := Hdr{src, dst, pri, count, op, ver}

    if coerce(&h) {
        t.Skip()
        return
    }

    bs, err := Marshal(h)     // check err

    h2, err := Unmarhsal(bs)  // check err

    if !cmp.Equal(h, h2) {
        t.Errorf("Marshal/Unmarshal validation failed for: %+v", h)
    }
}

Solution 2:[2]

In order to skip uninteresting results, call t.Skip in your fuzzing function. Something like this:

f.Fuzz(func(t *testing.T, b []byte) {
    a, err := Unmarshal(b)
    if err != nil {
        t.Skip()
        return
    }
    c, err := Marshal(a)
    if err != nil || !bytes.Equal(b, c) {
        t.Errorf("Eek!")
    }
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 jch