5. 与沙盒进程通信

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

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

共享文件描述符

使用进程间通信 API(请参阅 ipc.h),您可以使用 MapFd()ReceiveFd()

  • 使用 MapFd() 将文件描述符从执行器映射到 Sandboxee。这可用于共享从执行程序打开的文件,以便在 Sandboxee 中使用。您可以在静态中查看使用示例。

    // 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);
    

使用通信 API

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

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

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

通信对象可用后,可以使用 Send* 系列函数之一将数据发送到 Sandboxee。您可以在 crc4 示例中找到通信 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 */
}

与缓冲区共享数据

另一种数据共享功能是使用缓冲区 API 来共享大量数据,并避免在执行程序和 Sandboxee 之间来回发送的昂贵副本。

执行器通过大小和要传递的数据或直接从文件描述符创建缓冲区,并使用执行器中的 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 */