İşlem Rehberi

Giriş

Korumalı alan içermeyen bir C/C++ kitaplığı kullanılırken bağlayıcı, derlemeden sonra gerekli tüm işlevlerin kullanılabilir olmasını sağlar. Bu nedenle, bir API çağrısının çalışma zamanında başarısız olup olmayacağı konusunda endişelenmenize gerek yoktur.

Ancak korumalı alan kitaplığı kullanıldığında kitaplığın yürütülmesi ayrı bir süreçte gerçekleşir. Bir API çağrısında hata oluştuğunda, çağrının RPC katmanına iletilmesiyle ilgili her türlü sorunun kontrol edilmesi gerekir. Bazen RPC katmanı hataları, örneğin toplu işleme yaparken ve sandbox yeni yeniden başlatıldığında önemli olmayabilir.

Bununla birlikte, yukarıda belirtilen nedenlerden dolayı, korumalı alan API çağrısının dönüş değerinin düzenli hata kontrolünü, RPC katmanında bir hata döndürülüp döndürülmediğini kontrol edecek şekilde genişletmek önemlidir. Bu nedenle, tüm kitaplık işlevi prototipleri T yerine ::sapi::StatusOr<T> değerini döndürür. Kitaplık işlevi çağrısı başarısız olursa (ör. sandbox ihlali nedeniyle) dönüş değeri, oluşan hatayla ilgili ayrıntıları içerir.

RPC katmanı hatalarının işlenmesi, SandboxedLibrary'ye yapılan her çağrının ardından SAPI'nin RPC katmanında ek bir kontrol yapılması anlamına gelir. SAPI, bu istisnai durumlarla başa çıkmak için SAPI İşlemi modülünü (transaction.h) sağlar. Bu modül, ::sapi::Transaction sınıfını içerir ve Sandboxed Library'ye yapılan tüm işlev çağrılarının RPC düzeyinde herhangi bir sorun olmadan tamamlanmasını veya alakalı bir hata döndürmesini sağlar.

SAPI İşlemi

SAPI, ana makine kodunu korumalı alan kitaplığından ayırır ve arayana sorunlu veri işleme isteğini yeniden başlatma veya iptal etme olanağı tanır. SAPI İşlemi bir adım daha ileri giderek başarısız olan işlemleri otomatik olarak tekrar eder.

SAPI işlemleri iki farklı şekilde kullanılabilir: doğrudan ::sapi::Transaction öğesinden devralarak veya ::sapi::BasicTransaction öğesine iletilen işlev işaretçilerini kullanarak.

SAPI işlemleri, aşağıdaki üç işlev geçersiz kılınarak tanımlanır:

SAPI İşlem Yöntemleri
::sapi::Transaction::Init() Bu, normal bir C/C++ kitaplığının başlatma yöntemini çağırmaya benzer. İşlem yeniden başlatılmadığı sürece, yöntem, Sandboxed Library ile yapılan her işlem sırasında yalnızca bir kez çağrılır. Yeniden başlatma durumunda, daha önce kaç kez yeniden başlatma işlemi yapıldığına bakılmaksızın yöntem tekrar çağrılır.
::sapi::Transaction::Main() Yöntem, ::sapi::Transaction::Run() öğesine yapılan her çağrı için çağrılır.
::sapi::Transaction::Finish() Bu, tipik bir C/C++ kitaplığının temizleme yöntemini çağırmaya benzer. Bu yöntem, SAPI İşlem nesnesi yok edilirken yalnızca bir kez çağrılır.

Normal Kitaplık Kullanımı

Koruma alanına alınmış kitaplıkların olmadığı bir projede, kitaplıklarla çalışırken genellikle şu şekilde bir model izlenir:

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

Kitaplık başlatılır, ardından kitaplığın dışa aktarılan işlevleri kullanılır ve son olarak ortamı temizlemek için bir sonlandırma/kapatma işlevi çağrılır.

Korumalı alan kitaplık kullanımı

Koruma alanına alınmış kitaplıkların bulunduğu bir projede, Normal Library Use kodundan, geri çağırma işlevleriyle işlemler kullanılırken aşağıdaki kod snippet'i oluşturulur:

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

İşlem sınıfı, handle_data çağrısı sırasında bir hata oluşması durumunda kitaplığın yeniden başlatılmasını sağlar. Bu konuyla ilgili daha fazla bilgiyi aşağıdaki bölümde bulabilirsiniz.

İşlemin Yeniden Başlatılması

Bir korumalı alan kitaplığı API çağrısı, SAPI Transaction yöntemlerinin yürütülmesi sırasında bir hata oluşturursa (yukarıdaki tabloya bakın) işlem yeniden başlatılır. Yeniden başlatma sayısı, transaction.h dosyasındaki kDefaultRetryCnt ile tanımlanır.

Yeniden başlatmayı tetikleyecek hatalara örnek olarak şunlar verilebilir:

  • Bir korumalı alan ihlali oluştu
  • Korumalı alan işlemi kilitlendi
  • Bir kitaplık hatası nedeniyle korumalı alan işlevi bir hata kodu döndürdü

Yeniden başlatma prosedürü normal Init() ve Main() akışını izler ve ::sapi::Transaction::Run() yöntemine yapılan tekrarlı çağrılar hata döndürürse yöntemin tamamı arayanına hata döndürür.

Korumalı alan veya TBG hatası işleme

Otomatik olarak oluşturulan Sandboxed Library arayüzü, orijinal C/C++ kitaplık işlevi prototipine mümkün olduğunca yakın olmaya çalışır. Ancak, korumalı alan kitaplığının tüm korumalı alan veya RPC hatalarını bildirebilmesi gerekir.

Bu, korumalı alan işlevlerinin dönüş değerini doğrudan döndürmek yerine ::sapi::StatusOr<T> dönüş türlerini (veya void döndüren işlevler için ::sapi::Status) kullanarak elde edilir.

SAPI, SAPI durumu nesnesini kontrol etmek ve buna tepki vermek için bazı kullanışlı makrolar da sağlar. Bu makrolar, status_macro.h başlık dosyasında tanımlanır.

Aşağıdaki kod snippet'i, sum example dosyasından alınmıştır ve SAPI durumu ile makroların kullanımını gösterir:

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

Korumalı alan yeniden başlatma sayısı

Birçok korumalı alan kitaplığı, hassas kullanıcı girişini işler. Koruma alanına alınmış kitaplık bir noktada bozulursa ve çalıştırmalar arasında veri depolarsa bu hassas veriler risk altında olur. Örneğin, Imagemagick kitaplığının korumalı alan sürümü, önceki çalıştırmanın resimlerini göndermeye başlarsa.

Böyle bir senaryoyu önlemek için korumalı alan birden fazla çalıştırma için yeniden kullanılmamalıdır. Sandbox'ların yeniden kullanılmasını önlemek için, SAPI İşlemleri kullanılırken ::sapi::Sandbox::Restart() veya ::sapi::Transaction::Restart() kullanılarak sandbox'lı kitaplık işleminin yeniden başlatılması Ana Makine Kodu tarafından başlatılabilir.

Yeniden başlatma işlemi, korumalı alan kitaplığı sürecine yapılan tüm referansları geçersiz kılar. Bu, iletilen dosya tanımlayıcılarının veya ayrılan belleğin artık mevcut olmayacağı anlamına gelir.