5. Sandboxee との通信

デフォルトでは、実行プログラムはファイル記述子を介して Sandboxee と通信できます。たとえば、Sandboxee とファイルを共有したり、Sandboxee の標準出力を読み取ったりするだけの場合は、これで十分です。

ただし、実行プログラムと Sandboxee の間で、より複雑な通信ロジックが必要になる可能性は十分にあります。通信 API(comms.h ヘッダー ファイルを参照)を使用して、整数、文字列、バイトバッファ、protobuf、ファイル記述子を送信できます。

ファイル記述子の共有

プロセス間通信 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() を使用して socketpair エンドポイントを作成します。これは、Sandboxee の標準出力または標準エラーを読み取るために使用できます。使用例については、ツールをご覧ください。

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

Comms API の使用

Sandbox2 は便利な comms API を提供します。これは、実行プログラムと Sandboxee の間で整数、文字列、バイトバッファを共有する簡単でシンプルな方法です。以下は、crc4 の例で見つかるコード スニペットです。

Comms API を使用するには、まず Sandbox2 オブジェクトから Comms オブジェクトを取得する必要があります。

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

通信オブジェクトが使用可能になると、Send* ファミリーの関数のいずれかを使用して、データを Sandboxee に送信できます。comms API の使用例については、crc4 の例をご覧ください。次のコード スニペットは、その例の抜粋を示しています。実行者は 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 */
}

バッファとのデータの共有

もう 1 つのデータ共有機能は、バッファ 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 */