5. サンドボックス担当者とのやり取り

デフォルトでは、エグゼキュータはファイル記述子を介して 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() を使用して、Socketpair エンドポイントを作成します。これを使用して、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();

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 の間でコストの高いコピーが送受信されることを回避することです。

エグゼキュータは、サイズとデータによって、またはファイル記述子から直接 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 */