Guida alle transazioni

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Introduzione

Quando utilizzi una libreria C/C++ senza sandbox, il linker garantisce che tutte le funzioni necessarie siano disponibili dopo la compilazione e, quindi, non devi preoccuparti di eventuali chiamate API non riuscite nel runtime.

Quando utilizzi una libreria con sandbox, tuttavia, l'esecuzione della libreria risiede in un processo separato. Un errore in una chiamata API richiede il controllo di tutti i tipi di problemi relativi al trasferimento della chiamata sul livello RPC. A volte, gli errori del livello RPC potrebbero non interessarti, ad esempio durante l'elaborazione in blocco e la sandbox appena riavviata.

Tuttavia, per i motivi menzionati sopra, è importante estendere il controllo regolare degli errori del valore di ritorno della chiamata API con sandbox in modo da includere se è stato restituito un errore nel livello RPC. Ecco perché tutti i prototipi delle funzioni di libreria restituiscono ::sapi::StatusOr<T> anziché T. Nel caso in cui la chiamata della funzione libreria non funzioni (ad esempio a causa di una violazione della sandbox), il valore restituito conterrà i dettagli dell'errore che si è verificato.

La gestione degli errori del livello RPC significherebbe che ogni chiamata a una libreria con sandbox sia seguita da un controllo aggiuntivo del livello RPC di SAPI. Per gestire queste situazioni eccezionali, SAPI fornisce il modulo Transazione SAPI (transaction.h). Questo modulo contiene la classe ::sapi::Transaction e si assicura che tutte le chiamate a funzioni della libreria con sandbox siano state completate senza problemi a livello di RPC o restituiscano un errore pertinente.

Transazione SAPI

SAPI isola il codice host dalla libreria con sandbox e offre al chiamante la possibilità di riavviare o interrompere la richiesta di elaborazione dei dati problematica. La transazione SAPI va oltre e ripete automaticamente i processi non riusciti.

Le transazioni SAPI possono essere utilizzate in due modi diversi: ereditano direttamente da ::sapi::Transaction o usano puntatori di funzione trasmessi a ::sapi::BasicTransaction.

Le transazioni SAPI vengono definite sostituendo le seguenti tre funzioni:

Metodi di transazione SAPI
::sapi::Transaction::Init() Si tratta di una procedura simile a quella per chiamare un metodo di inizializzazione di una tipica libreria C/C++. Il metodo viene chiamato una sola volta durante ogni transazione alla libreria con sandbox, a meno che la transazione non venga riavviata. In caso di riavvio, il metodo viene richiamato nuovamente, indipendentemente dal numero di riavvii che si sono verificati in precedenza.
::sapi::Transaction::Main() Il metodo viene chiamato per ogni chiamata al numero ::sapi::Transaction::Run() .
::sapi::Transaction::Finish() Si tratta di una procedura simile a quella di un metodo di pulizia di una tipica libreria C/C++. Il metodo viene chiamato solo una volta durante l'eliminazione di oggetti Transaction SAPI.

Utilizzo normale della raccolta

In un progetto senza librerie con sandbox, il pattern consueto per la gestione delle librerie è il seguente:

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

La libreria viene inizializzata, quindi vengono utilizzate le funzioni esportate della libreria e, infine, una funzione di fine/chiusura viene chiamata per ripulire l'ambiente.

Utilizzo della sandbox con sandbox

In un progetto con librerie sandbox, il codice di Normal Library Use si traduce nel seguente snippet di codice quando si utilizzano transazioni con 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);
    // ...
  }
  // ...
}

La classe della transazione si assicura di reinizializzare la libreria nel caso in cui si sia verificato un errore durante la chiamata handle_data; scopri di più nella sezione seguente.

Riavvii delle transazioni

Se una chiamata API della libreria sandbox presenta un errore durante l'esecuzione dei metodi di transazione SAPI (vedi la tabella precedente), la transazione verrà riavviata. Il numero predefinito di riavvii è definito da kDefaultRetryCnt in transaction.h.

Esempi di errori generati che attivano un riavvio sono:

  • Si è verificata una violazione della sandbox
  • Si è verificato un arresto anomalo del processo con sandbox
  • Una funzione con sandbox ha restituito un codice di errore a causa di un errore della libreria

La procedura di riavvio osserva il normale flusso di Init() e Main() e, se le chiamate ripetute al metodo ::sapi::Transaction::Run() restituiscono errori, l'intero metodo restituisce un errore al chiamante.

Gestione degli errori Sandbox o RPC

L'interfaccia Libreria con sandbox generata automaticamente cerca di avvicinarsi il più possibile al prototipo della funzione della libreria C/C++ originale. Tuttavia, la libreria sandbox deve essere in grado di segnalare eventuali errori sandbox o RPC.

Ciò si ottiene utilizzando i tipi di reso ::sapi::StatusOr<T> (o ::sapi::Status per le funzioni che restituiscono void), invece di restituire direttamente il valore di ritorno delle funzioni con sandbox.

SAPI fornisce inoltre alcune utili macro per verificare e reagire a un oggetto di stato SAPI. Queste macro sono definite nel file di intestazione status_macro.h.

Il seguente snippet di codice è un estratto dell'esempio di somma e mostra l'uso dello stato SAPI e delle macro:

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

Riavvii sandbox

Molte librerie con sandbox gestiscono gli input sensibili degli utenti. Se a un certo punto la libreria con sandbox è danneggiata e archivia i dati tra un'esecuzione e l'altra, questi dati sensibili sono a rischio. Ad esempio, se una versione con sandbox della libreria Imagemagick inizia a inviare immagini dell'esecuzione precedente,

Per evitare questo scenario, la sandbox non deve essere riutilizzata per più esecuzioni. Per interrompere il riutilizzo delle sandbox, il codice host può avviare un riavvio del processo della libreria con sandbox utilizzando ::sapi::Sandbox::Restart() o ::sapi::Transaction::Restart() quando si utilizzano le transazioni SAPI.

Un riavvio invaliderà qualsiasi riferimento al processo della libreria con sandbox. Ciò significa che i descrittori dei file trasmessi o la memoria allocata non esisteranno più.