如何设置输入的约束?

发布于 2025-01-18 13:05:58 字数 1025 浏览 3 评论 0原文

假设我有以下结构,

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

我有两个函数 MarshalUnmarshal,它们将 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 像 Priority 这样的字段不能大于 15,否则它们在编组时会被截断(只有 4 位)。我如何设置这个约束?

更新

这只是一个玩具盒。很多时候,像上面这样的协议中,诸如操作码之类的东西会触发二次更复杂的解析/审查。模糊测试仍然可以在约束内发现非常有用的问题(即:如果 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 \ ).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

神魇的王 2025-01-25 13:05:58

edit

我不确定在这里模糊是否合适。模糊旨在查找意外输入:多字节UTF8输入(有效和非valid);负值;巨大的价值,长度等。这些将试图捕获“边缘”案例。

在您的情况下,您知道:

  • unmarshal输入有效载荷必须是6个字节(否则应发生错误),
  • 您确切地知道您的内部“边”,

因此vanilla testing.t测试可能在这里更适合。


保持简单。

如果您不想“浪费”模糊输入&您知道代码的输入约束,可以尝试类似的内容:

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

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)
    }
}
诗化ㄋ丶相逢 2025-01-25 13:05:58

为了跳过无趣的结果,请在模糊功能中调用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!")
    }
})

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!")
    }
})
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文