英文:
How to set constraint on input for fuzzing?
问题
假设我有以下结构体:
type Hdr struct{
Src uint16
Dst uint16
Priotity byte
Pktcnt byte
Opcode byte
Ver byte
}
我有两个函数Marshal
和Unmarshal
,用于将Hdr
编码为二进制格式,并从二进制格式解码为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
实例,将其编码为二进制格式,解码二进制格式,并确保输出与原始输入匹配。
我遇到的主要问题是我无法弄清楚如何告诉Go Fuzz,像Priotity
这样的字段不能大于15,否则在编码时会被截断(只有4位)。我该如何设置这个约束?
更新
这只是一个玩具案例。在像上面的协议中,有很多情况下,类似opcode
的东西会触发次要的更复杂的解析/验证。在约束条件下,模糊测试仍然可以找到非常有用的问题(例如:如果Prio为0x00
,Cnt为0x2F
,次要解析器将因为分隔符为\
而出错)。
英文:
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 \
).
答案1
得分: 3
为了跳过不感兴趣的结果,在你的模糊测试函数中调用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("糟糕!")
}
})
英文:
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!")
}
})
答案2
得分: 3
EDIT
我不确定模糊测试是否适合这里。模糊测试旨在查找意外的输入:多字节的UTF8输入(有效和无效);负值;巨大的值,长长度等。这些将尝试捕捉“边缘”情况。
在这种情况下,您知道:
Unmarshal
输入负载必须是6个字节(否则应该出错)- 您精确地知道您的内部“边缘”
因此,普通的 testing.T
测试可能更适合这里。
保持简单。
如果您不想“浪费”一个模糊输入,并且您知道代码的输入约束,您可以尝试像这样的方法:
func coerce(h *Hdr) (skip bool) {
h.Priotity &= 0x0f // 确保优先级在0-15之间
h.OpCode %= 20 // 确保操作码在0-19之间
return false // 可选择跳过此测试
}
在您的测试中,可以测试或跳过强制值(如 @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) // 检查错误
h2, err := Unmarhsal(bs) // 检查错误
if !cmp.Equal(h, h2) {
t.Errorf("Marshal/Unmarshal validation failed for: %+v", h)
}
})
英文:
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)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论