5. การสื่อสารกับ Sandboxee

โดยค่าเริ่มต้น ตัวดำเนินการจะสื่อสารกับ Sandboxee ผ่านตัวอธิบายไฟล์ ซึ่งอาจเป็นสิ่งที่คุณต้องการทั้งหมด เช่น หากคุณเพียงต้องการแชร์ ไฟล์กับ Sandboxee หรืออ่านเอาต์พุตมาตรฐานของ Sandboxee

อย่างไรก็ตาม คุณอาจต้องใช้ตรรกะการสื่อสารที่ซับซ้อนมากขึ้น ระหว่าง Executor กับ Sandboxee คุณใช้ Comms API (ดูไฟล์ส่วนหัว comms.h) เพื่อส่งจำนวนเต็ม สตริง บัฟเฟอร์ไบต์ Protobuf หรือตัวอธิบายไฟล์ได้

การแชร์ตัวบอกไฟล์

เมื่อใช้ Inter-Process Communication API (ดู ipc.h) คุณจะใช้ MapFd() หรือ ReceiveFd() ได้

  • ใช้ MapFd() เพื่อแมปตัวอธิบายไฟล์จาก Executor ไปยัง 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 มี comms API ที่สะดวก นี่เป็นวิธีที่ง่ายและสะดวกในการแชร์จำนวนเต็ม สตริง หรือบัฟเฟอร์ไบต์ ระหว่างตัวดำเนินการกับ Sandboxee ด้านล่างนี้คือตัวอย่างโค้ดบางส่วนที่คุณดูได้ในตัวอย่าง crc4

หากต้องการเริ่มต้นใช้งาน Comms API คุณต้องรับออบเจ็กต์ Comms จากออบเจ็กต์ Sandbox2 ก่อน โดยทำดังนี้

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

เมื่อออบเจ็กต์ comms พร้อมใช้งานแล้ว คุณจะส่งข้อมูลไปยัง Sandboxee ได้โดยใช้ฟังก์ชันตระกูล Send* คุณดูตัวอย่างการใช้ Comms API ได้ในตัวอย่าง crc4 ข้อมูลโค้ด ด้านล่างแสดงตัวอย่างจากตัวอย่างดังกล่าว ผู้ดำเนินการจะส่ง unsigned char buf[size] พร้อม SendBytes(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 */
}

การแชร์ข้อมูลกับบัฟเฟอร์

ฟังก์ชันการแชร์ข้อมูลอีกอย่างคือการใช้ buffer API เพื่อแชร์ข้อมูลจำนวนมากและหลีกเลี่ยงการคัดลอกที่มีค่าใช้จ่ายสูงซึ่งส่งกลับไปกลับมาระหว่างตัวดำเนินการและ Sandboxee

โดยจะสร้างบัฟเฟอร์ตามขนาดและข้อมูลที่จะส่ง หรือสร้างจากตัวอธิบายไฟล์โดยตรง แล้วส่งไปยัง Sandboxee โดยใช้ comms->SendFD() ใน Executor และ 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 */