如何为模糊测试设置输入约束?

How to set constraint on input for fuzzing?

假设我有以下结构

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

我有两个函数 MarshalUnmarshalHdr 编码为二进制格式:

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

我想使用 Go Fuzz 生成随机、有效的 Hdr 个实例,Marshal 然后是二进制,Unmarshal 二进制并确保输出与原始输入匹配.

我遇到的主要问题是我不知道如何告诉 Go Fuzz 像 Priotity 这样的字段不能大于 15,否则它们在编组时会被截断(只有 4 位)。如何设置此约束?

更新

这只是一个玩具箱。像上面这样的协议有很多次,其中 opcode 之类的东西会触发更复杂的次要 parsing/vetting。模糊测试仍然可以在约束内找到非常有用的问题(即:如果 Prio 0x00 和 Cnt 0x2F 辅助解析器将出错,因为分隔符是 \)。

为了跳过无趣的结果,请在您的模糊测试函数中调用 t.Skip。像这样:

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!")
    }
})

编辑

我不确定模糊测试是否适合这里。模糊测试旨在发现意外输入:multi-byte UTF8 输入(有效且 non-valid);负值;巨大的价值,很长的长度等。这些将试图捕捉“边缘”情况。

在你这里,你知道:

  • Unmarshal 输入负载必须为 6 个字节(否则会出错)
  • 你很清楚自己的内在“优势”

所以普通 testing.T 测试可能更适合这里。


保持简单。

如果您不想“浪费”一个 Fuzz 输入并且您知道代码的输入限制,您可以尝试这样的事情:

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
}

并且在您的测试中 - 可以测试强制值 - 或跳过(如@jch 所示):

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)
    }
}