消息传递

LibAFL 提供了一个标准的机制,用于在进程和机器上进行低开销的消息传递。 我们使用消息传递来通知其他连接的客户端/模糊器/节点关于新的测试案例、元数据和关于当前运行的统计数据。 根据个人需要,LibAFL 也可以将测试案例的内容写到磁盘上,同时仍然使用事件来通知其他模糊器,使用一个 OnDiskCorpus.

在我们的测试中,消息传递可以很好地在多个运行中的模糊器实例之间分享新的测试案例和元数据,以进行多核模糊处理。 具体来说,它比在共享语料库上使用内存锁要好得多,也比通过文件系统共享测试案例要好得多,就像 AFL 传统的做法。 用起来 htop所有核心都是绿色的,也就是说,没有内核交互。

EventManager 接口用于使用 Low Level Message Passing 发送事件,这是一种通过共享内存或TCP的自定义消息传递机制。

低水平消息传递(LLMP)

LibAFL 有一个合理的无锁消息传递机制,可以很好地跨核扩展,使用其 broker2broker 机制,甚至可以通过TCP连接机器。 大多数模糊测试的例子都使用这种机制,如果你想在一个以上的核心上进行模糊测试,它是最好的 事件管理器。 在下文中,我们将描述 LLMP 的内部工作原理。

LLMP 有一个 broker 进程,可以将任何客户进程发送的消息转发给所有其他客户。 broker 也可以拦截和过滤它收到的消息,而不是转发它们。 broker 过滤的信息的一个常见用例是每个客户直接发送给 broker 的状态信息。 broker 用这些信息来绘制一个简单的用户界面,其中有所有客户的最新信息,然而其他客户不需要接收这些信息。

通过共享内存的快速本地消息

在整个 LibAFL 中,我们使用了一个围绕不同操作系统的共享 map 的包装器,称为 ShMem,它是 LLMP 的骨干。 每个client,通常是试图分享统计数据和新的测试案例的摸索者,都会映射一个输出的 ShMem map。 除了极少数的例外,只有这个客户写到这个 map 上,因此,我们不会在竞赛条件下运行,可以不用锁。 broker 从所有客户的 ShMem 映射中读取。 它定期检查所有传入的客户端映射,然后将新消息转发到由所有连接的客户端映射的 出站广播-`ShMem。

为了发送新消息,客户端在其共享内存的末端放置一个新消息,然后更新一个静态字段来通知代理。 一旦传出的映射已满,发送者使用各自的 ShMemProvider 分配一个新的 ShMem。 然后,它使用页面结束 (EOP) 消息发送所需信息,将连接进程中新分配的页面映射到旧的页面。 一旦接收者映射了新的页面,就把它标记为安全的,可以从发送进程中解除映射 (如果我们在短时间内有超过一个EOP,就可以避免竞赛条件),然后继续从新的 ShMem 中读取。

Client 对 broker 的映射模式如下:

[client0]        [client1]    ...    [clientN]
  |                  |                 /
[client0_out] [client1_out] ... [clientN_out]
  |                 /                /
  |________________/                /
  |________________________________/
 \|/
[broker]

broker 在所有传入的 map 上循环,并检查新的消息。 在 std 构建中,broker 会在循环后睡眠几毫秒,因为我们不需要消息立即到达。 在 broker 收到来自客户端N的新消息后,(clientN_out->current_id != last_message->message_id) broker 会将消息内容复制到自己的广播共享内存。

客户端定期地,例如在完成 n 次突变后,通过检查是否有新的消息进入 (current_broadcast_map->current_id != last_message->message_id) 。 虽然 broker 使用相同的 EOP 机制为其传出的 map 映射新的 ShMem,但它从不解除旧页面的映射。 这种额外的内存开销有一个很好的目的: 通过保留所有的广播页面,我们确保新的客户可以在以后的时间点加入到模糊测试活动中来,他们只需要从头到尾重新阅读所有广播的信息。

所以传出的消息在传出的广播 Shmem 上是这样流动的:

[broker]
  |
[current_broadcast_shmem]
  |
  |___________________________________
  |_________________                  \
  |                 \                  \
  |                  |                  |
 \|/                \|/                \|/
[client0]        [client1]    ...    [clientN]

要在 LibAFL 中使用 LLMP,你通常要使用 LlmpEventManager 或其重启的变体。 如果使用 LibAFL 的 Launcher,它们是默认的。

如果你想使用 LLMP 的原始形式,没有任何 LibAFL 的抽象,看看 ./libafl/examples 中的 llmp_test 例子。 你可以使用 cargo run --example llmp_test 以适当的模式运行这个例子,正如其帮助输出所指出的。 首先,你必须使用 LlmpBroker::new() 创建一个broker 。 然后,在其他线程中创建一些 LlmpClients,并使用 LlmpBroker::register_client 在主线程中注册它们。 最后,调用 LlmpBroker::loop_forever()

B2B: 通过TCP连接模糊器

对于 broker2broker 的通信,所有的广播信息都通过网络套接字转发。 为了方便起见,我们在 broker 中产生了一个额外的客户线程,它可以像其他客户那样读取广播共享内存。 对于 b2b 的通信,这个 b2b 客户端监听来自其他远程 broker 的 TCP 连接。 它在任何时候都保持一个开放的套接字池,用于连接其他远程的b2b broker。 当在本地 broker 共享内存中收到一个新消息时,b2b 客户端将通过 TCP 将其转发给所有连接的远程 broker。 另外,broker 可以从所有连接的 (远程) broker 那里接收消息,并通过客户端 ShMem 转发给本地broker。

作为附带说明,用于 b2b 通信的 tcp 监听器也用于新客户试图连接到本地 broker 时的初始握手,简单地交换初始 ShMem 描述。