5. Comunicação com o Sandboxee

Por padrão, o executor pode se comunicar com o Sandboxee usando descritores de arquivo. Isso pode ser tudo o que você precisa, por exemplo, se quiser apenas compartilhar um arquivo com o Sandboxee ou ler a saída padrão dele.

No entanto, é muito provável que você precise de uma lógica de comunicação mais complexa entre o executor e o Sandboxee. A API comms (consulte o arquivo de cabeçalho comms.h) pode ser usada para enviar números inteiros, strings, buffers de bytes, protobufs ou descritores de arquivo.

Compartilhamento de descritores de arquivos

Usando a API de comunicação entre processos (consulte ipc.h), é possível usar MapFd() ou ReceiveFd():

  • Use MapFd() para mapear descritores de arquivo do executor para o Sandboxee. Isso pode ser usado para compartilhar um arquivo aberto do executor para uso no Sandboxee. Um exemplo de uso pode ser visto em static.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • Use ReceiveFd() para criar um endpoint socketpair. Isso pode ser usado para ler a saída padrão ou os erros padrão do Sandboxee. Um exemplo de uso pode ser visto na ferramenta.

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

Como usar a API Comms

O Sandbox2 oferece uma API comms conveniente. Essa é uma maneira simples e fácil de compartilhar números inteiros, strings ou buffers de bytes entre o executor e o Sandboxee. Confira abaixo alguns snippets de código que podem ser encontrados no exemplo crc4.

Para começar a usar a API Comms, primeiro você precisa extrair o objeto Comms do objeto Sandbox2:

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

Quando o objeto de comunicação estiver disponível, os dados poderão ser enviados ao Sandboxee usando uma das funções da família Send*. Confira um exemplo de uso da API de comunicações no exemplo crc4. O snippet de código abaixo mostra um trecho desse exemplo. O executor envia um unsigned char buf[size] com SendBytes(buf, size):

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

Para receber dados do Sandboxee, use uma das funções Recv*. O snippet de código abaixo é um trecho do exemplo crc4. O executor recebe a soma de verificação em um número inteiro sem assinatura de 32 bits: uint32_t crc4;

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

Compartilhamento de dados com buffers

Outra funcionalidade de compartilhamento de dados é usar a API buffer para compartilhar grandes quantidades de dados e evitar cópias caras que são enviadas entre o executor e o Sandboxee.

O executor cria um buffer, seja por tamanho e dados a serem transmitidos, ou diretamente de um descritor de arquivo, e o transmite ao Sandboxee usando comms->SendFD() no executor e comms->RecvFD() no Sandboxee.

No snippet de código abaixo, você pode ver o lado do executor. O sandbox é executado de forma assíncrona e compartilha dados por um buffer com o 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());

No lado do Sandboxee, também é necessário criar um objeto de buffer e ler os dados do descritor de arquivo enviado pelo executor:

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