5. Comunicación con el 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 el Sandboxee o leer su salida estándar.

Sin embargo, es muy probable que necesites una lógica de comunicación más compleja entre el ejecutor y Sandboxee. La API de comms (consulta el archivo de encabezado comms.h) se puede usar para enviar números enteros, cadenas, 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 al Sandboxee. Se puede usar para compartir un archivo abierto desde el ejecutor para usarlo en el Sandboxee. Puedes ver un ejemplo de uso en static.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • UsaReceiveFd()para crear un extremo de socketpair. Se puede usar para leer la salida estándar o los errores estándar de Sandboxee. 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 Comms

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

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

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

Una vez que el objeto de comunicación está disponible, se pueden enviar datos a Sandboxee con una de las funciones de la familia Send*. Puedes encontrar un ejemplo de uso de la API de Comms en el ejemplo de crc4. El siguiente fragmento de código muestra un fragmento 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 de 32 bits sin firma: uint32_t crc4;

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

Cómo compartir datos con Buffers

Otra función de uso compartido de datos es usar la API de búfer para compartir grandes cantidades de datos y evitar copias costosas que se envían de un lado a otro entre el ejecutor y el Sandboxee.

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

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

En el lado de Sandboxee, también debes crear un objeto de búfer y leer los datos del descriptor de archivo que envía 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 */