5. 與 Sandboxee 通訊

依預設,執行器可以透過檔案描述元與 Sandboxee 通訊。舉例來說,如果您只想與 Sandboxee 分享檔案,或讀取 Sandboxee 的標準輸出內容,可能只需要這樣做。

不過,您很可能需要在執行器和 Sandboxee 之間建立更複雜的通訊邏輯。通訊 API (請參閱 comms.h 標頭檔) 可用於傳送整數、字串、位元組緩衝區、protobuf 或檔案描述元。

分享檔案描述元

使用 Inter-Process Communication 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() 建立 socketpair 端點。可用於讀取 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 範例中的一些程式碼片段。

如要開始使用通訊 API,請先從 Sandbox2 物件取得通訊物件:

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

通訊物件可用後,即可使用 Send* 系列函式,將資料傳送至 Sandboxee。您可以在 crc4 範例中找到通訊 API 的使用範例。以下程式碼片段是該範例的節錄內容。執行器會傳送含有 SendBytes(buf, size)unsigned char buf[size],並提供 SendBytes(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 */