5. Giao tiếp với Sandboxee

Theo mặc định, executor có thể giao tiếp với Sandboxee thông qua chỉ số mô tả tệp. Đây có thể là tất cả những gì bạn cần, chẳng hạn như nếu bạn chỉ muốn chia sẻ một tệp với Sandboxee hoặc đọc đầu ra tiêu chuẩn của Sandboxee.

Tuy nhiên, rất có thể bạn cần phải có logic giao tiếp phức tạp hơn giữa executor và Sandboxee. Bạn có thể dùng API comms (xem tệp tiêu đề comms.h) để gửi số nguyên, chuỗi, vùng đệm byte, protobufs hoặc chỉ số mô tả tệp.

Chia sẻ phần mô tả tệp

Khi sử dụng API giao tiếp giữa các quá trình (xem ipc.h), bạn có thể sử dụng MapFd() hoặc ReceiveFd():

  • Sử dụng MapFd() để liên kết chỉ số mô tả tệp từ trình thực thi đến Sandboxee. Bạn có thể sử dụng tệp này để chia sẻ tệp được mở từ trình thực thi để sử dụng trong Sandboxee. Bạn có thể xem ví dụ về cách sử dụng ở dạng tĩnh.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • Sử dụng ReceiveFd() để tạo điểm cuối của socketPair. Bạn có thể dùng giá trị này để đọc dữ liệu đầu ra chuẩn hoặc lỗi chuẩn của Sandboxee. Bạn có thể xem ví dụ về cách sử dụng trong công cụ.

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

Sử dụng API giao tiếp

Sandbox2 cung cấp một API giao tiếp tiện lợi. Đây là một cách đơn giản và dễ dàng để chia sẻ số nguyên, chuỗi hoặc vùng đệm byte giữa executor và Sandboxee. Dưới đây là một số đoạn mã mà bạn có thể tìm thấy trong ví dụ về crc4.

Để bắt đầu với API giao tiếp, trước tiên, bạn phải lấy đối tượng giao tiếp từ đối tượng Sandbox2:

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

Khi đối tượng giao tiếp sẵn có, dữ liệu có thể được gửi tới Sandboxee bằng một trong các nhóm hàm Send*. Bạn có thể xem ví dụ về cách sử dụng API giao tiếp trong crc4. Đoạn mã dưới đây cho thấy một phần trích dẫn từ ví dụ đó. Trình thực thi gửi một unsigned char buf[size] với SendBytes(buf, size):

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

Để nhận dữ liệu từ Sandboxee, hãy sử dụng một trong các hàm Recv*. Đoạn mã dưới đây là phần trích dẫn từ ví dụ về crc4. Trình thực thi nhận giá trị tổng kiểm ở một số nguyên 32 bit chưa ký: uint32_t crc4;

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

Chia sẻ dữ liệu với vùng đệm

Một chức năng chia sẻ dữ liệu khác là sử dụng API bộ đệm để chia sẻ lượng lớn dữ liệu và để tránh các bản sao tốn kém được gửi qua lại giữa trình thực thi và Sandboxee.

Trình thực thi tạo một Vùng đệm, theo kích thước và dữ liệu cần truyền, hoặc trực tiếp từ chỉ số mô tả tệp rồi truyền tệp này cho Sandboxee bằng cách sử dụng comms->SendFD() trong trình thực thi và comms->RecvFD() trong Sandboxee.

Trong đoạn mã dưới đây, bạn có thể thấy phía của executor. Hộp cát chạy không đồng bộ và chia sẻ dữ liệu qua vùng đệm với 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());

Về phía Sandboxee, bạn cũng phải tạo một đối tượng vùng đệm và đọc dữ liệu từ chỉ số mô tả tệp do trình thực thi gửi:

// 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 */