Panduan Transaksi

Pengantar

Saat menggunakan library C/C++ yang tidak di-sandbox, linker memastikan semua fungsi yang diperlukan tersedia setelah kompilasi sehingga tidak perlu khawatir apakah panggilan API dapat gagal saat runtime.

Namun, saat menggunakan Sandboxed Library, eksekusi library berada dalam proses terpisah. Kegagalan dalam panggilan API memerlukan pemeriksaan semua jenis masalah yang terkait dengan meneruskan panggilan melalui lapisan RPC. Terkadang, error lapisan RPC mungkin tidak menarik, misalnya saat melakukan pemrosesan massal dan sandbox baru saja dimulai ulang.

Namun demikian, karena alasan yang disebutkan di atas, penting untuk memperluas pemeriksaan error rutin pada nilai yang ditampilkan dari panggilan API dengan sandbox untuk menyertakan pemeriksaan apakah error ditampilkan di lapisan RPC. Itulah sebabnya semua prototipe fungsi library menampilkan ::sapi::StatusOr<T>, bukan T. Jika pemanggilan fungsi pustaka gagal (misalnya karena pelanggaran sandbox), nilai yang ditampilkan akan berisi detail tentang error yang terjadi.

Penanganan error lapisan RPC berarti bahwa setiap panggilan ke Sandboxed Library diikuti dengan pemeriksaan tambahan pada lapisan RPC SAPI. Untuk menangani situasi luar biasa tersebut, SAPI menyediakan modul SAPI Transaction (transaction.h). Modul ini berisi class ::sapi::Transaction dan memastikan bahwa semua panggilan fungsi ke Sandboxed Library telah selesai tanpa masalah tingkat RPC, atau menampilkan error yang relevan.

Transaksi SAPI

SAPI mengisolasi Kode Host dari Sandboxed Library dan memberi pemanggil kemampuan untuk memulai ulang atau membatalkan permintaan pemrosesan data yang bermasalah. Transaksi SAPI melangkah lebih jauh dan otomatis mengulangi proses yang gagal.

Transaksi SAPI dapat digunakan dengan dua cara berbeda: mewarisi langsung dari ::sapi::Transaction, atau menggunakan pointer fungsi yang diteruskan ke ::sapi::BasicTransaction.

Transaksi SAPI ditentukan dengan mengganti tiga fungsi berikut:

Metode Transaksi SAPI
::sapi::Transaction::Init() Hal ini mirip dengan memanggil metode inisialisasi library C/C++ biasa. Metode ini hanya dipanggil sekali selama setiap transaksi ke Library Sandbox, kecuali jika transaksi dimulai ulang. Jika terjadi mulai ulang, metode ini akan dipanggil lagi, terlepas dari berapa kali mulai ulang telah terjadi sebelumnya.
::sapi::Transaction::Main() Metode ini dipanggil untuk setiap panggilan ke ::sapi::Transaction::Run() .
::sapi::Transaction::Finish() Hal ini mirip dengan memanggil metode pembersihan library C/C++ biasa. Metode ini hanya dipanggil satu kali selama penghancuran objek Transaksi SAPI.

Penggunaan Library Normal

Dalam project tanpa library sandbox, pola umum saat menangani library akan terlihat seperti ini:

LibInit();
while (data = NextDataToProcess()) {
  result += LibProcessData(data);
}
LibClose();

Library diinisialisasi, lalu fungsi yang diekspor dari library digunakan, dan terakhir, fungsi akhir/tutup dipanggil untuk membersihkan lingkungan.

Penggunaan Library yang Di-sandbox

Dalam project dengan library sandbox, kode dari Penggunaan Library Normal diterjemahkan ke dalam cuplikan kode berikut saat menggunakan transaksi dengan callback:

// 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);
    // ...
  }
  // ...
}

Class transaksi memastikan untuk menginisialisasi ulang library jika terjadi error selama pemanggilan handle_data – selengkapnya tentang hal ini di bagian berikut.

Mulai Ulang Transaksi

Jika panggilan API library dengan sandbox memunculkan error selama eksekusi metode Transaksi SAPI (lihat tabel di atas), transaksi akan dimulai ulang. Jumlah restart default ditentukan oleh kDefaultRetryCnt di transaction.h.

Contoh error yang muncul dan akan memicu mulai ulang adalah:

  • Terjadi pelanggaran sandbox
  • Proses sandbox mengalami error
  • Fungsi sandbox menampilkan kode error karena error library

Prosedur memulai ulang mengamati alur Init() dan Main() normal, dan jika panggilan berulang ke metode ::sapi::Transaction::Run() menampilkan error, maka seluruh metode menampilkan error ke pemanggilnya

Penanganan Error Sandbox atau RPC

Antarmuka Sandboxed Library yang dibuat otomatis berupaya sedekat mungkin dengan prototipe fungsi library C/C++ asli. Namun, Sandboxed Library harus dapat memberi sinyal error sandbox atau RPC.

Hal ini dicapai dengan menggunakan jenis nilai yang ditampilkan ::sapi::StatusOr<T> (atau ::sapi::Status untuk fungsi yang menampilkan void), bukan menampilkan nilai yang ditampilkan fungsi sandbox secara langsung.

SAPI selanjutnya menyediakan beberapa makro praktis untuk memeriksa dan bereaksi terhadap objek Status SAPI. Makro ini ditentukan dalam file header status_macro.h.

Cuplikan kode berikut adalah kutipan dari contoh jumlah dan menunjukkan penggunaan Status SAPI dan makro:

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

Mulai Ulang Sandbox

Banyak library dalam sandbox yang menangani input pengguna yang sensitif. Jika library sandbox rusak pada suatu saat dan menyimpan data di antara proses, data sensitif ini berisiko. Misalnya, jika versi sandbox library Imagemagick mulai mengirimkan gambar dari proses sebelumnya.

Untuk menghindari skenario tersebut, sandbox tidak boleh digunakan kembali untuk beberapa kali menjalankan. Untuk menghentikan penggunaan ulang sandbox, Kode Host dapat memulai ulang proses library sandbox menggunakan ::sapi::Sandbox::Restart() atau ::sapi::Transaction::Restart() saat menggunakan Transaksi SAPI.

Mulai ulang akan membatalkan semua referensi ke proses library sandbox. Artinya, deskriptor file yang diteruskan, atau memori yang dialokasikan tidak akan ada lagi.