假设我有以下结构
type Hdr struct{
Src uint16
Dst uint16
Priotity byte
Pktcnt byte
Opcode byte
Ver byte
}
我有两个函数Marshal
和Unmarshal
,它们将Hdr
编码为二进制格式:
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
之类的东西会触发第二次更复杂的解析/审查。模糊化仍然可以在约束中找到非常有用的问题(IE:如果Prio0x00
和Cnt0x2F
辅助解析器将出错,因为分隔符是(。
编辑
我不确定Fuzzing是否适合这里。Fuzzing被设计用于发现意外输入:多字节UTF8输入(有效和无效(;负值;巨大的价值、长的长度等等;边缘";案例。
在您的情况下,您知道:
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)
}
}
为了跳过不感兴趣的结果,请在模糊函数中调用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!")
}
})