İşlem Rehberi

Giriş

Korumalı alanda olmayan bir C/C++ kitaplığı kullanırken bağlayıcı, gerekli tüm işlevlerin derlemeden sonra kullanılabilir olmasını sağlar. Böylece, çalışma zamanında bir API çağrısının başarısız olup olmayacağı konusunda endişe duymanıza gerek kalmaz.

Ancak, Korumalı Alana Sahip Kitaplık kullanılırken kitaplığın yürütülmesi ayrı bir işlemde gerçekleşir. Bir API çağrısında bulunan hata, çağrının RPC katmanı üzerinden aktarılmasıyla ilgili her türlü sorunun denetlenmesini gerektirir. Bazen (ör. toplu işlem yapılırken ve korumalı alan yeniden başlatıldığında) RPC katmanı hataları önemsiz olabilir.

Yine de, yukarıda belirtilen nedenlerden dolayı, korumalı alana alınmış API çağrısının dönüş değerinin düzenli hata kontrolünün kapsamını, RPC katmanında hata döndürülüp döndürülmediğini kontrol edecek şekilde genişletmek önemlidir. Tüm kitaplık işlevi prototiplerinin T yerine ::sapi::StatusOr<T> döndürmesinin nedeni budur. Kitaplık işlevi çağrısının başarısız olması durumunda (ör. korumalı alan ihlali nedeniyle) döndürülen değer, oluşan hatayla ilgili ayrıntıları içerir.

RPC katmanı hatalarının işlenmesi, Korumalı Alana Sahip Kitaplık'a yapılan her çağrının ardından SAPI'nin RPC katmanının ek bir kontrol edilmesi anlamına gelir. SAPI, bu istisnai durumları ele almak için SAPI İşlem modülünü (transaction.h) sunar. Bu modül ::sapi::Transaction sınıfını içerir ve Korumalı Alana Sahip Kitaplık'a yapılan tüm işlev çağrılarının RPC düzeyinde sorun olmadan tamamlanmasını veya ilgili hata döndürmesini sağlar.

SAPI İşlemi

SAPI, Ana Makine Kodunu Korumalı Alana Sahip Kitaplık'tan ayırır ve çağrı yapan kişiye sorunlu veri işleme isteğini yeniden başlatma veya iptal etme olanağı verir. SAPI İşlemi bir adım daha ileri gidip başarısız işlemleri otomatik olarak tekrarlar.

SAPI İşlemleri iki farklı şekilde kullanılabilir: Devralma işlemini doğrudan ::sapi::Transaction ürününden alma veya ::sapi::BasicTransaction öğesine iletilen işlev işaretçilerini kullanma.

SAPI İşlemleri, aşağıdaki üç işlevin geçersiz kılınmasıyla tanımlanır:

SAPI İşlem Yöntemleri
::sapi::Transaction::Init() Bu, tipik bir C/C++ kitaplığının başlatma yöntemini çağırmaya benzer. Yöntem, işlem yeniden başlatılmadığı sürece Korumalı Alana Sahip Kitaplık'a yapılan her işlem sırasında yalnızca bir kez çağrılır. Yeniden başlatma durumunda, daha önce kaç yeniden başlatma yapılmış olduğuna bakılmaksızın yöntem tekrar çağrılır.
::sapi::Transaction::Main() Yöntem, her ::sapi::Transaction::Run() çağrısı 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 Transaction nesnesi yok etme sırasında yalnızca bir kez çağrılır.

Normal Kitaplık Kullanımı

Korumalı alana sahip kitaplıkların olmadığı bir projede, kitaplıklarla çalışırken olağan kalıp şöyle görünür:

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 bitiş/kapanış işlevi çağrılır.

Korumalı Alana Sahip Kitaplık Kullanımı

Korumalı alana alınmış kitaplıkların bulunduğu bir projede, geri çağırma özellikli işlemler kullanılırken Normal Kitaplık Kullanımı'ndan alınan kod aşağıdaki kod snippet'ine çevrilir:

// 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 hata oluşması durumunda kitaplığın yeniden başlatılmasını sağlar. Bu konuda daha fazla bilgiyi aşağıdaki bölümde bulabilirsiniz.

İşlem Yeniden Başlatma Sayısı

Korumalı alana alınmış bir kitaplık API çağrısı, SAPI Transaction yöntemlerinin yürütülmesi sırasında hata verirse (yukarıdaki tabloya bakın) işlem yeniden başlatılır. Varsayılan yeniden başlatma sayısı transaction.h dosyasında kDefaultRetryCnt tarafından tanımlanır.

Yeniden başlatmayı tetikleyecek şekilde bildirilen hatalara örnekler:

  • Korumalı alan ihlali oluştu
  • Korumalı alana alınan işlem kilitlendi
  • Korumalı alana alınmış bir işlev, kitaplık hatası nedeniyle hata kodu döndürdü

Yeniden başlatma prosedürü normal Init() ve Main() akışını gözlemler ve ::sapi::Transaction::Run() yöntemine yapılan yinelenen çağrılar hata döndürürse tüm yöntem çağrısına bir hata döndürür.

Korumalı Alan veya RPC Hatalarını İşleme

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

Bu, korumalı alana alınmış işlevlerin döndürdüğü değeri doğrudan döndürmek yerine ::sapi::StatusOr<T> dönüş türleri (veya void döndüren işlevler için ::sapi::Status) kullanılarak gerçekleştirilir.

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

Aşağıdaki kod snippet'i top örneğinden bir alıntı olup 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şlatmaları

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

Böyle bir senaryodan kaçınmak için korumalı alan, birden fazla çalıştırmada yeniden kullanılmamalıdır. Korumalı alanların yeniden kullanılmasını durdurmak için Ana Makine Kodu, SAPI İşlemleri kullanılırken ::sapi::Sandbox::Restart() veya ::sapi::Transaction::Restart() kullanılarak korumalı alana alınmış kitaplık işleminin yeniden başlatılmasını başlatabilir.

Yeniden başlatma, korumalı alana alınmış kitaplık işlemine ilişkin 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.