บทนำ
เมื่อใช้ไลบรารี C/C++ ที่ไม่ได้อยู่ในแซนด์บ็อกซ์ Linker จะตรวจสอบว่ามีฟังก์ชันที่จำเป็นทั้งหมด หลังจากการคอมไพล์ จึงไม่ต้องกังวลว่าการเรียก API อาจล้มเหลวในรันไทม์ หรือไม่
อย่างไรก็ตาม เมื่อใช้ไลบรารีที่อยู่ในแซนด์บ็อกซ์ การดำเนินการของไลบรารีจะอยู่ในกระบวนการแยกต่างหาก การเรียก API ที่ล้มเหลวต้องตรวจสอบปัญหาทุกประเภทที่เกี่ยวข้องกับการส่งการเรียกผ่านเลเยอร์ RPC บางครั้งข้อผิดพลาดของเลเยอร์ RPC อาจไม่เกี่ยวข้อง เช่น เมื่อทำการประมวลผลแบบเป็นกลุ่ม และเพิ่งรีสตาร์ทแซนด์บ็อกซ์
อย่างไรก็ตาม ด้วยเหตุผลที่กล่าวไว้ข้างต้น การขยายการตรวจสอบข้อผิดพลาดปกติของค่าที่ส่งคืนจากการเรียก API ใน Sandbox ให้รวมการตรวจสอบว่ามีการส่งคืนข้อผิดพลาดในเลเยอร์ RPC หรือไม่จึงเป็นสิ่งสำคัญ ด้วยเหตุนี้ ต้นแบบฟังก์ชันไลบรารีทั้งหมดจึงแสดงผล ::sapi::StatusOr<T>
แทน T ในกรณีที่การเรียกใช้ฟังก์ชันไลบรารีล้มเหลว (เช่น เนื่องจากการละเมิดแซนด์บ็อกซ์) ค่าที่ส่งคืนจะมีรายละเอียดเกี่ยวกับข้อผิดพลาดที่เกิดขึ้น
การจัดการข้อผิดพลาดของเลเยอร์ RPC จะหมายความว่าการเรียกใช้ Sandboxed
Library แต่ละครั้งจะตามด้วยการตรวจสอบเพิ่มเติมของเลเยอร์ RPC ของ SAPI SAPI มีโมดูลธุรกรรม SAPI
(transaction.h) เพื่อรับมือกับสถานการณ์พิเศษเหล่านั้น
โมดูลนี้มีคลาส ::sapi::Transaction
และตรวจสอบว่าการเรียกฟังก์ชันทั้งหมดไปยังไลบรารีแซนด์บ็อกซ์เสร็จสมบูรณ์โดยไม่มีปัญหาที่ระดับ RPC หรือแสดงข้อผิดพลาดที่เกี่ยวข้อง
ธุรกรรม SAPI
SAPI จะแยกโค้ดโฮสต์ออกจากไลบรารีแซนด์บ็อกซ์ และให้ความสามารถแก่ผู้เรียกในการรีสตาร์ทหรือยกเลิกคำขอประมวลผลข้อมูลที่มีปัญหา SAPI Transaction จะก้าวไปอีกขั้นและทำกระบวนการที่ไม่สำเร็จซ้ำโดยอัตโนมัติ
ธุรกรรม SAPI สามารถใช้ได้ 2 วิธี ได้แก่ สืบทอดโดยตรงจาก ::sapi::Transaction
หรือใช้ตัวชี้ฟังก์ชันที่ส่งไปยัง ::sapi::BasicTransaction
ธุรกรรม SAPI จะกำหนดโดยการลบล้างฟังก์ชัน 3 อย่างต่อไปนี้
วิธีการทำธุรกรรม SAPI | |
---|---|
::sapi::Transaction::Init() |
ซึ่งคล้ายกับการเรียกใช้เมธอดการเริ่มต้นของไลบรารี C/C++ ทั่วไป ระบบจะเรียกใช้เมธอดนี้เพียงครั้งเดียวในระหว่างธุรกรรมแต่ละรายการไปยังไลบรารีที่แซนด์บ็อกซ์ เว้นแต่จะมีการรีสตาร์ทธุรกรรม ในกรณีที่มีการรีสตาร์ท ระบบจะเรียกใช้เมธอดอีกครั้ง ไม่ว่าก่อนหน้านี้จะมีการรีสตาร์ทกี่ครั้งก็ตาม |
::sapi::Transaction::Main() |
ระบบจะเรียกใช้เมธอดสำหรับการเรียกใช้ ::sapi::Transaction::Run() แต่ละครั้ง |
::sapi::Transaction::Finish() |
ซึ่งคล้ายกับการเรียกใช้เมธอดการล้างข้อมูลของไลบรารี C/C++ ทั่วไป ระบบจะเรียกใช้เมธอดนี้เพียงครั้งเดียวในระหว่างการทำลายออบเจ็กต์ธุรกรรม SAPI |
การใช้งานห้องสมุดตามปกติ
ในโปรเจ็กต์ที่ไม่มีไลบรารีแซนด์บ็อกซ์ รูปแบบปกติเมื่อต้องจัดการกับไลบรารีจะมีลักษณะดังนี้
LibInit();
while (data = NextDataToProcess()) {
result += LibProcessData(data);
}
LibClose();
ระบบจะเริ่มต้นไลบรารี จากนั้นจะใช้ฟังก์ชันที่ส่งออกจากไลบรารี และ สุดท้ายจะเรียกฟังก์ชันสิ้นสุด/ปิดเพื่อล้างข้อมูลสภาพแวดล้อม
การใช้ไลบรารีแซนด์บ็อกซ์
ในโปรเจ็กต์ที่มีไลบรารีที่แซนด์บ็อกซ์ไว้ โค้ดจากNormal Library Use จะแปลเป็นข้อมูลโค้ดต่อไปนี้เมื่อใช้ ธุรกรรมที่มีการเรียกกลับ
// Overwrite the Init method, passed to BasicTransaction initialization
::absl::Status Init(::sapi::Sandbox* sandbox) {
// Instantiate the SAPI Object
LibraryAPI lib(sandbox);
// Instantiate the Sandboxed Library
SAPI_RETURN_IF_ERROR(lib.LibInit());
return ::absl::OkStatus();
}
// Overwrite the Finish method, passed to BasicTransaction initialization
::absl::Status Finish(::sapi::Sandbox *sandbox) {
// Instantiate the SAPI Object
LibraryAPI lib(sandbox);
// Clean-up sandboxed library instance
SAPI_RETURN_IF_ERROR(lib.LibClose());
return ::absl::OkStatus();
}
// Wrapper function to process data, passed to Run method call
::absl::Status HandleData(::sapi::Sandbox *sandbox, Data data_to_process,
Result *out) {
// Instantiate the SAPI Object
LibraryAPI lib(sandbox);
// Call the sandboxed function LibProcessData
SAPI_ASSIGN_OR_RETURN(*out, lib.LibProcessData(data_to_process));
return ::absl::OkStatus();
}
void Handle() {
// Use SAPI Transactions by passing function pointers to ::sapi::BasicTransaction
::sapi::BasicTransaction transaction(Init, Finish);
while (data = NextDataToProcess()) {
::sandbox2::Result result;
// call the ::sapi::Transaction::Run() method
transaction.Run(HandleData, data, &result);
// ...
}
// ...
}
คลาสธุรกรรมจะตรวจสอบว่าได้เริ่มต้นไลบรารีอีกครั้งในกรณีที่เกิดข้อผิดพลาด
ระหว่างการเรียกใช้ handle_data
ซึ่งจะกล่าวถึงเพิ่มเติมในส่วนต่อไปนี้
การรีสตาร์ทธุรกรรม
หากการเรียก API ของไลบรารีแซนด์บ็อกซ์ทำให้เกิดข้อผิดพลาดระหว่างการเรียกใช้เมธอด SAPI
Transaction (ดูตารางด้านบน) ระบบจะรีสตาร์ทธุรกรรม
จำนวนการรีสตาร์ทเริ่มต้นกำหนดโดย kDefaultRetryCnt
ใน
transaction.h
ตัวอย่างข้อผิดพลาดที่เกิดขึ้นซึ่งจะทําให้เกิดการรีสตาร์ทมีดังนี้
- เกิดการละเมิดแซนด์บ็อกซ์
- กระบวนการแซนด์บ็อกซ์ขัดข้อง
- ฟังก์ชันแซนด์บ็อกซ์แสดงรหัสข้อผิดพลาดเนื่องจากข้อผิดพลาดของไลบรารี
กระบวนการรีสตาร์ทจะสังเกตโฟลว์ปกติของ Init()
และ Main()
และหากการเรียกใช้เมธอด ::sapi::Transaction::Run()
ซ้ำๆ แสดงข้อผิดพลาด เมธอดทั้งหมดจะแสดงข้อผิดพลาดต่อผู้เรียก
การจัดการข้อผิดพลาดในแซนด์บ็อกซ์หรือ RPC
อินเทอร์เฟซไลบรารี Sandbox ที่สร้างขึ้นโดยอัตโนมัติพยายาม ให้ใกล้เคียงกับต้นแบบฟังก์ชันไลบรารี C/C++ เดิมมากที่สุด อย่างไรก็ตาม ไลบรารีที่อยู่ในแซนด์บ็อกซ์ต้องสามารถส่งสัญญาณข้อผิดพลาดของแซนด์บ็อกซ์หรือ RPC ได้
ซึ่งทำได้โดยใช้ประเภทการแสดงผล ::sapi::StatusOr<T>
(หรือ ::sapi::Status
สำหรับฟังก์ชันที่แสดงผล void
) แทนที่จะแสดงผลค่าการแสดงผลของฟังก์ชันแซนด์บ็อกซ์โดยตรง
นอกจากนี้ SAPI ยังมีมาโครที่สะดวกบางอย่างเพื่อตรวจสอบและตอบสนองต่อออบเจ็กต์สถานะ SAPI มาโครเหล่านี้กำหนดไว้ในไฟล์ส่วนหัว status_macro.h
ข้อมูลโค้ดต่อไปนี้เป็นข้อความที่ตัดตอนมาจากตัวอย่าง sum และแสดงให้เห็นการใช้ สถานะ SAPI และมาโคร
// Instead of void, use ::sapi::Status
::sapi::Status SumTransaction::Main() {
// Instantiate the SAPI Object
SumApi f(sandbox());
// ::sapi::StatusOr<int> sum(int a, int b)
SAPI_ASSIGN_OR_RETURN(int v, f.sum(1000, 337));
// ...
// ::sapi::Status sums(sapi::v::Ptr* params)
SumParams params;
params.mutable_data()->a = 1111;
params.mutable_data()->b = 222;
params.mutable_data()->ret = 0;
SAPI_RETURN_IF_ERROR(f.sums(params.PtrBoth()));
// ...
// Gets symbol address and prints its value
int *ssaddr;
SAPI_RETURN_IF_ERROR(sandbox()->Symbol(
"sumsymbol", reinterpret_cast<void**>(&ssaddr)));
::sapi::v::Int sumsymbol;
sumsymbol.SetRemote(ssaddr);
SAPI_RETURN_IF_ERROR(sandbox()->TransferFromSandboxee(&sumsymbol));
// ...
return ::sapi::OkStatus();
}
การรีสตาร์ทแซนด์บ็อกซ์
ไลบรารีที่อยู่ในแซนด์บ็อกซ์จำนวนมากจัดการอินพุตของผู้ใช้ที่มีความละเอียดอ่อน หากไลบรารีแซนด์บ็อกซ์ เสียหายในบางจุดและจัดเก็บข้อมูลระหว่างการเรียกใช้ ข้อมูลที่ละเอียดอ่อนนี้จะมีความเสี่ยง เช่น หากเวอร์ชันแซนด์บ็อกซ์ของไลบรารี Imagemagick เริ่ม ส่งรูปภาพของการเรียกใช้ก่อนหน้า
เพื่อหลีกเลี่ยงสถานการณ์ดังกล่าว คุณไม่ควรนำแซนด์บ็อกซ์มาใช้ซ้ำหลายครั้ง หากต้องการ
หยุดการนำแซนด์บ็อกซ์กลับมาใช้ใหม่ โค้ดโฮสต์จะเริ่มการรีสตาร์ทกระบวนการไลบรารีแซนด์บ็อกซ์ได้โดยใช้ ::sapi::Sandbox::Restart()
หรือ
::sapi::Transaction::Restart()
เมื่อใช้ธุรกรรม SAPI
การรีสตาร์ทจะทำให้การอ้างอิงกระบวนการไลบรารีแซนด์บ็อกซ์ทั้งหมดไม่ถูกต้อง ซึ่ง หมายความว่าตัวอธิบายไฟล์ที่ส่งผ่านหรือหน่วยความจำที่จัดสรรจะไม่มีอยู่อีกต่อไป