5. Comunicación con Sandboxee

De forma predeterminada, el ejecutor puede comunicarse con Sandboxee a través de descriptores de archivos. Esto podría ser todo lo que necesitas, por ejemplo, si solo quieres compartir un archivo con Sandboxee o leer el resultado estándar de Sandboxee.

Sin embargo, lo más probable es que necesites una lógica de comunicación más compleja entre el ejecutor y Sandboxee. La API de comunicaciones (consulta el archivo de encabezado comms.h) se puede usar para enviar enteros, strings, búferes de bytes, protobufs o descriptores de archivos.

Cómo compartir descriptores de archivos

Con la API de comunicación entre procesos (consulta ipc.h), puedes usar MapFd() o ReceiveFd():

  • Usa MapFd() para asignar descriptores de archivos del ejecutor a la zona de pruebas. Se puede usar para compartir un archivo abierto desde el ejecutor y usarlo en Sandboxee. Puedes ver un ejemplo de uso en estática.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • Usa ReceiveFd() para crear un extremo de par de sockets. Se puede usar para leer el resultado estándar o los errores estándar de la zona de pruebas. Puedes ver un ejemplo de uso en la herramienta.

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

Usa la API de comunicaciones

Sandbox2 proporciona una API de comunicaciones conveniente. Esta es una manera simple y fácil de compartir números enteros, strings o búferes de bytes entre el ejecutor y Sandboxee. A continuación, se muestran algunos fragmentos de código que puedes encontrar en el ejemplo de crc4.

Para comenzar a usar la API de comunicaciones, primero debes obtener el objeto de comunicaciones del objeto Sandbox2:

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

Una vez que el objeto de comunicaciones está disponible, los datos se pueden enviar a la zona de pruebas mediante una de la familia de funciones Send*. Puedes encontrar un ejemplo de uso de la API de comunicaciones en el ejemplo de crc4. El siguiente fragmento de código muestra un extracto de ese ejemplo. El ejecutor envía un unsigned char buf[size] con SendBytes(buf, size):

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

Para recibir datos de Sandboxee, usa una de las funciones Recv*. El siguiente fragmento de código es un extracto del ejemplo de crc4. El ejecutor recibe la suma de verificación en un número entero sin firma de 32 bits: uint32_t crc4;

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

Cómo compartir datos con búferes

Otra funcionalidad de uso compartido de datos consiste en usar la API de búfer para compartir grandes cantidades de datos y evitar copias costosas que se envían y reciben entre el ejecutor y Sandboxee.

El ejecutor crea un búfer, ya sea por tamaño y datos que se pasarán, o directamente desde un descriptor de archivos, y lo pasa a Sandboxee usando comms->SendFD() en el ejecutor y comms->RecvFD() en Sandboxee.

En el siguiente fragmento de código, puedes ver el lado del ejecutor. La zona de pruebas se ejecuta de forma asíncrona y comparte datos a través de un búfer con 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());

Del lado de Sandboxee, también debes crear un objeto de búfer y leer los datos del descriptor de archivos que envió el ejecutor:

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