5. Communicating with the Sandboxee

By default, the executor can communicate with the Sandboxee through file descriptors. This might be all you need, for example if you just want to share a file with the Sandboxee, or read the Sandboxee's standard output.

However, you most likely have the need for more complex communication logic between the executor and Sandboxee. The comms API (see the comms.h header file) can be used to send integers, strings, byte buffers, protobufs, or file descriptors.

Using the Inter-Process Communication API (see ipc.h ), you can use MapFd() or ReceiveFd() :

  • Use MapFd() to map file descriptors from the executor to the Sandboxee. This can be used to share a file opened from the executor for use in the Sandboxee. An example use can be seen in static .

      // The executor opened /proc/version and passes it to the sandboxee as stdin 
     executor 
     - 
    > ipc 
     () 
     - 
    > MapFd 
     ( 
     proc_version_fd 
     , 
      
     STDIN_FILENO 
     ); 
     
    
  • Use ReceiveFd() to create a socketpair endpoint. This can be used to read the Sandboxee's standard output or standard errors. An example use can be seen in the tool .

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

Using the comms API

Sandbox2 provides a convenient comms API . This is a simple and easy way to share integers, strings, or byte buffers between the executor and Sandboxee. Below are some code snippets that you can find in the crc4 example.

To get started with the comms API, you first have to get the comms object from the Sandbox2 object:

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

Once the comms object is available, data can be sent to the Sandboxee using one of the Send* family of functions. You can find an example use of the comms API in the crc4 example. The code snippet below shows an excerpt from that example. The executor sends an unsigned char buf[size] with SendBytes(buf, size) :

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

To receive data from the Sandboxee, use one of the Recv* functions. The code snippet below is an excerpt from the crc4 example. The executor receives the checksum in a 32-bit unsigned integer: uint32_t crc4;

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

Another data sharing functionality is to use the buffer API to share large amounts of data and to avoid expensive copies that are sent back and forth between the executor and Sandboxee.

The executor creates a Buffer, either by size and data to be passed, or directly from a file descriptor, and passes it to the Sandboxee using comms->SendFD() in the executor and comms->RecvFD() in the Sandboxee.

In the code snippet below, you can see the executor's side. The sandbox runs asynchronously and shares data via a buffer with the 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 
 ()); 
 

On the Sandboxee's side, you also have to create a buffer object and read the data from the file descriptor sent by the 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 */ 
 
Create a Mobile Website
View Site in Mobile | Classic
Share by: