5. การสื่อสารกับแซนด์บ็อกซ์

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

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

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

การใช้ Inter-Process Communication API (ดูที่ ipc.h) คุณจะใช้ MapFd() หรือ ReceiveFd() ได้ดังนี้

  • ใช้ MapFd() เพื่อจับคู่ข้อบ่งชี้ไฟล์จากผู้ดำเนินการไปยังแซนด์บ็อกซ์ ซึ่งอาจใช้ในการแชร์ไฟล์ที่เปิดจากผู้ดำเนินการเพื่อใช้ในแซนด์บ็อกซ์ ตัวอย่างการใช้งานสามารถดูได้ในแบบคงที่

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • ใช้ ReceiveFd() เพื่อสร้างปลายทาง Socketpair สามารถใช้เพื่ออ่านเอาต์พุตมาตรฐานหรือข้อผิดพลาดมาตรฐานของแซนด์บ็อกซ์ ดูตัวอย่างการใช้งานได้ในเครื่องมือ

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

การใช้ Comms API

Sandbox2 มี Comms API ที่ใช้งานสะดวก นี่เป็นวิธีง่ายๆ ในการแชร์จำนวนเต็ม สตริง หรือบัฟเฟอร์ไบต์ระหว่างตัวดำเนินการและแซนด์บ็อกซ์ ด้านล่างคือข้อมูลโค้ดบางส่วนที่คุณจะพบในตัวอย่าง crc4

หากต้องการเริ่มต้นใช้งาน comms API ก่อนอื่นคุณต้องรับออบเจ็กต์ comms จากออบเจ็กต์ Sandbox2

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

เมื่อออบเจ็กต์การสื่อสารพร้อมใช้งาน ระบบจะส่งข้อมูลไปยังแซนด์บ็อกซ์โดยใช้ฟังก์ชันในตระกูล Send* ได้ ดูตัวอย่างการใช้ comms API ได้ในตัวอย่าง crc4 ข้อมูลโค้ดด้านล่างแสดงตัวอย่างบางส่วน ผู้ดำเนินการส่ง unsigned char buf[size] พร้อม SendBytes(buf, size):

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

หากต้องการรับข้อมูลจากแซนด์บ็อกซ์ ให้ใช้ฟังก์ชัน Recv* อย่างใดอย่างหนึ่ง ข้อมูลโค้ดด้านล่างเป็นข้อความที่ตัดตอนมาจากตัวอย่าง crc4 ผู้ดำเนินการจะได้รับการตรวจสอบข้อผิดพลาดในจำนวนเต็มที่ไม่มีเครื่องหมาย 32 บิต: uint32_t crc4;

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

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

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

ผู้ดำเนินการสร้างบัฟเฟอร์ ไม่ว่าจะตามขนาดและข้อมูลที่จะส่ง หรือโดยตรงจากคำอธิบายไฟล์ และส่งไปยังแซนด์บ็อกซ์โดยใช้ comms->SendFD() ในตัวดำเนินการและ comms->RecvFD() ในแซนด์บ็อกซ์

คุณจะเห็นด้านของผู้ดำเนินการในข้อมูลโค้ดด้านล่าง แซนด์บ็อกซ์ทำงานแบบไม่พร้อมกันและแชร์ข้อมูลผ่านบัฟเฟอร์กับแซนด์บ็อกซ์:

// 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());

ในฝั่งของแซนด์บ็อกซ์ คุณจะต้องสร้างออบเจ็กต์บัฟเฟอร์และอ่านข้อมูลจากตัวบอกไฟล์จากผู้ปฏิบัติการด้วย โดยทำดังนี้

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