5. 与 Sandboxee 通信

默认情况下,执行器可以通过文件描述符与 Sandboxee 通信。例如,如果您只想与 Sandboxee 共享文件,或要读取 Sandboxee 的标准输出内容,您可能只需要这样设置。

不过,您很可能需要在执行器和 Sandboxee 之间实现更复杂的通信逻辑。comms API(请参阅 comms.h 头文件)可用于发送整数、字符串、字节缓冲区、protobuf 或文件描述符。

共享文件描述符

借助 Inter-Process Communication API(请参阅 ipc.h),您可以使用 MapFd()ReceiveFd()

  • 使用 MapFd() 将文件描述符从执行程序映射到 Sandboxee。这可用于共享从执行程序打开的文件,以便在 Sandboxee 中使用。static 提供了示例用法。

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • 使用 ReceiveFd() 创建套接字对端点。此参数可用于读取 Sandboxee 的标准输出或标准错误。可在工具中查看使用示例。

    // The executor receives a file descriptor of the sandboxee stdout
    int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
    

使用 Comms API

Sandbox2 提供了便捷的通信 API。这是一种在执行器和 Sandboxee 之间共享整数、字符串或字节缓冲区的简单方法。以下一些代码段可在 crc4 示例中找到。

如需开始使用 comms API,您必须先从 Sandbox2 对象中获取 comms 对象:

sandbox2::Comms* comms = s2.comms();

通信对象可用后,可以使用 Send* 系列函数之一将数据发送到 Sandboxee。您可以在 crc4 示例中找到 Commms API 的用法示例。以下代码段展示了该示例的摘录。执行器会发送带有 SendBytes(buf, size)unsigned char buf[size]

if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
  /* handle error */
}

如需从 Sandboxee 接收数据,请使用其中一个 Recv* 函数。以下代码段摘自 crc4 示例。执行器接收 32 位无符号整数中的校验和:uint32_t crc4;

if (!(comms->RecvUint32(&crc4))) {
  /* handle error */
}

与缓冲区共享数据

另一个数据共享功能是使用 buffer API 共享大量数据,以及避免在执行器和 Sandboxee 之间来回发送开销非常大的副本。

执行器会按照要传递的大小和数据创建 Buffer,也可以直接从文件描述符创建 Buffer,然后使用执行器中的 comms->SendFD() 和 Sandboxee 中的 comms->RecvFD() 将其传递给 Sandboxee。

在下面的代码段中,您可以看到执行器端。沙盒以异步方式运行,并通过缓冲区与 Sandboxee 共享数据:

// start the sandbox asynchronously
s2.RunAsync();

// instantiate the comms object
sandbox2::Comms* comms = s2.comms();

// random buffer data we want to send
constexpr unsigned char buffer_data[] = /* random data */;
constexpr unsigned int buffer_dataLen = 34;

// create sandbox2 buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
     sandbox2::Buffer::CreateWithSize(1ULL << 20 /* 1Mib */);
std::unique_ptr<sandbox2::Buffer> buffer_ptr = std::move(buffer).value();

// point to the sandbox2 buffer and fill with data
uint8_t* buf = buffer_ptr‑>data();
memcpy(buf, buffer_data, buffer_data_len);

// send the data to the sandboxee
comms‑>SendFd(buffer_ptr‑>fd());

在 Sandboxee 端,您还必须创建一个缓冲区对象,并从执行程序发送的文件描述符中读取数据:

// establish the communication with the executor
int fd;
comms.RecvFD(&fd);

// create the buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
     sandbox2::Buffer::createFromFd(fd);

// get the data
auto buffer_ptr = std::move(buffer).value();
uint8_t* buf = buffer_ptr‑>data();

/* work with the buf object */