## Introduction

When using an unsandboxed C/C++ library, the linker ensures all necessary
functions are available after compilation and thus there is no need to worry
whether an API call may fail at runtime.

When using a Sandboxed Library however, the execution of the library lives in a
separate process. A failure in an API call requires checking for all kinds of
problems related to passing the call over the RPC layer. Sometimes, the RPC
layer errors might not be of interest, for example when doing bulk processing
and the sandbox just got restarted.

Nevertheless, for the reasons mentioned above, it is important to extend regular
error checking of the sandboxed API call's return value to include checking if
an error was returned on the RPC layer. This is why all library function
prototypes return `::sapi::StatusOr<T>` instead of T. In the event that the
library function invocation fails (e.g. because of a sandbox violation), the
return value will contain details about the error that occurred.

Handling of the RPC layer errors would mean that each call to a Sandboxed
Library is followed by an additional check of the RPC layer of SAPI. In order to
deal with those exceptional situations, SAPI provides the SAPI Transaction
module
([transaction.h](https://github.com/google/sandboxed-api/blob/main/sandboxed_api/transaction.h)).
This module contains the `::sapi::Transaction` class and makes sure that all
function calls to the Sandboxed Library were completed without any RPC-level
problems, or return a relevant error.

## SAPI Transaction

SAPI isolates the Host Code from the Sandboxed Library and gives the caller the
ability to restart or abort the problematic data processing request. SAPI
Transaction goes one step further and automatically repeats failed processes.

SAPI Transactions can be used in two different ways: either directly inheriting
from `::sapi::Transaction`, or using function pointers passed to
`::sapi::BasicTransaction`.

SAPI Transactions are defined by overriding the following three functions:

| SAPI Transaction Methods ||
|---|---|
| `::sapi::Transaction::Init()` | This is similar to calling an initialization method of a typical C/C++ library. The method is called only once during each transaction to the Sandboxed Library, unless the transaction is restarted. In the case of a restart, the method is called again, regardless of how many restarts have happened before. |
| `::sapi::Transaction::Main()` | The method is called for each call to `::sapi::Transaction::Run() `. |
| `::sapi::Transaction::Finish()` | This is similar to calling a clean-up method of a typical C/C++ library. The method is called only once during SAPI Transaction object destruction. |

### Normal Library Use

In a project without sandboxed libraries, the usual pattern when dealing with
libraries looks something like this:

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

The library is initialized, then exported functions of the library are used, and
finally an end/close function is called to clean-up the environment.

### Sandboxed Library Use

In a project with sandboxed libraries, the code from [Normal Library
Use](https://developers.google.com/code-sandboxing/sandboxed-api/transactions#normal_library_use) translates into the following code snippet when using
transactions with callbacks:

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

The transaction class makes sure to reinitialize the library in case an error
occurred during the `handle_data` invocation -- more on this in the following
section.

### Transaction Restarts

If a sandboxed library API call raises an error during the execution of the SAPI
Transaction methods (see table above), the transaction will be restarted. The
default number of restarts is defined by `kDefaultRetryCnt` in
[transaction.h](https://github.com/google/sandboxed-api/blob/master/sandboxed_api/transaction.h).

Examples of raised errors that will trigger a restart are:

- A sandbox violation occurred
- The sandboxed process crashed
- A sandboxed function returned an error code due to a library error

The restart procedure observes the normal `Init()` and `Main()` flow, and if
repeated calls to the `::sapi::Transaction::Run()` method return errors, then
the whole method returns an error to its caller

### Sandbox or RPC Error Handling

The auto-generated [Sandboxed Library
interface](https://developers.google.com/code-sandboxing/sandboxed-api/build-rules#sapi_library) attempts to
be as close to the original C/C++ library function prototype as possible.
However, the Sandboxed Library needs to be able to signal any sandbox or RPC
errors.

This is achieved by making use of `::sapi::StatusOr<T>` return types (or
`::sapi::Status` for functions returning `void`), instead of returning the
sandboxed functions' return value directly.

SAPI further provides some convenient macros to check and react to a SAPI Status
object. These macros are defined in the
[status_macro.h](https://github.com/google/sandboxed-api/blob/main/sandboxed_api/util/status_macros.h)
header file.

The following code snippet is an excerpt from the [sum
example](https://developers.google.com/code-sandboxing/sandboxed-api/examples#sum) and demonstrates the use
of SAPI Status and the macros:

    // 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)
      ABSL_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;
      ABSL_RETURN_IF_ERROR(f.sums(params.PtrBoth()));
      // ...
      // Gets symbol address and prints its value
      int *ssaddr;
      ABSL_RETURN_IF_ERROR(sandbox()->Symbol(
          "sumsymbol", reinterpret_cast<void**>(&ssaddr)));
      ::sapi::v::Int sumsymbol;
      sumsymbol.SetRemote(ssaddr);
      ABSL_RETURN_IF_ERROR(sandbox()->TransferFromSandboxee(&sumsymbol));
      // ...
      return ::sapi::OkStatus();
    }

## Sandbox Restarts

Many sandboxed libraries handle sensitive user input. If the sandboxed library
is corrupted at some point and stores data between runs, this sensitive data is
at risk. For example, if a sandboxed version of the Imagemagick library starts
sending out pictures of the previous run.

To avoid such a scenario, the sandbox should not be reused for multiple runs. To
stop the reuse of sandboxes, the Host Code can initiate a restart of the
sandboxed library process using `::sapi::Sandbox::Restart()` or
`::sapi::Transaction::Restart()` when using SAPI Transactions.

A restart will invalidate any reference to the sandboxed library process. This
means that passed file descriptors, or allocated memory will no longer exist.

> [!NOTE]
> **Note:** Restarting the sandboxed process takes some time, about 75-80 ms on modern machines (more if network namespaces are used).