Enforcing isolation with the LFI sandbox
Note: this section is written as a continuation of the tutorial as an alternate to using RLBox with wasm2c. It's best if you follow the tutorial upto that point and continue reading below.
The noop
backend makes it easy to add security checks. However, it does not
enforce isolation. To finish sandboxing your library, we will need to:
-
Update the application
main.cpp
to use thelfi
sandbox backend instead ofnoop
. -
Compile our library e.g.
mylib.c
using the LFI compiler to native sandboxed object, and link this into our application.
We will look at each these steps next and end with instructions on how you can try this out.
Modifying the application to use the lfi RLBox plugin/backend
Making this change is very simple with RLBox. In fact, it can be done
exclusively in the boilerplate. Here is the boilerplate to use the lfi
backend.
// We're going to use RLBox in a single-threaded environment.
#define RLBOX_SINGLE_THREADED_INVOCATIONS
#include "rlbox.hpp"
#include "rlbox_lfi_sandbox.hpp"
using namespace rlbox;
extern "C" {
extern uint8_t mylib_start[];
extern uint8_t mylib_end[];
};
// Define base type for mylib using the noop sandbox
RLBOX_DEFINE_BASE_TYPES_FOR(mylib, lfi);
You'll probably notice that there are only a handful of changes.
- We now use
"rlbox_lfi_sandbox.hpp
instead ofrlbox_noop_sandbox.hpp
- The sandbox type which is the second parameter to the macro
RLBOX_DEFINE_BASE_TYPES_FOR
has now changed tolfi
fromnoop
- The boilerplate for
RLBOX_USE_STATIC_CALLS
has been removed as lfi backend doesn't need this - The lfi backend/plugin requires an extra piece of boilerplate which is to
define the beginning and end of the lfi library that has been linked into the
application as a datablob. This is shown as
mylib_start
andmylib_end
here. We will discuss how to generate these variables in the next section.
These are mostly mechanical changes and are straightforward. Modifying the build is perhaps slightly more challenging as building wasm libraries involves multiple steps.
Modifying the build to produce to lfi sandboxed library
To show how we will update the build, we will use two CMakeLists.txt files as a reference
- The CMakeLists used by the noop sandbox
- The CMakeLists used by the lfi sandbox
As you can see the lfi CMakeLists is quite a bit longer. Below, we will give a high-level overview of the steps, so you can follow what is happening in the LFI build.
To build and use the LFI sandboxed library, we need several additional repos/tools
- We will need the rlbox lfi plugin/backend
- We will need a version of clang that can produce LFI files. We will used the prebuilt binaries of LFI clang available in the official repo which brings the compiler, the custom libc and so on.
- Finally, we need the lfi-runtime, which supports the loading and execution of LFI runtimes.
After we download these repos, we can then take the following steps
-
Build the lfi runtime. This is a project that can be built using meson. You can read more about how to build lfi in their readme
-
We need to compile our
mylib.c
tomylib.wasm
using lfi's clang. The command in theCMakeLists.txt
that does this isPATH=$ENV{PATH}:${lficlang_SOURCE_DIR}/lfi-bin ${lficlang_SOURCE_DIR}/bin/clang ${LFI_SBX_BUILD_TYPE_FLAGS} -Wl,--export-dynamic -static-pie -o ${MYLIB_ELF} ${CMAKE_SOURCE_DIR}/mylib.c -L ${LFI_SYSROOT_PATH} -lboxrt
This commands starts by addinf the lfi clang folder to the $PATH. ${LFI_SBX_BUILD_TYPE_FLAGS} is just going to be
-O0
or-O3
. LFI requires that the-Wl,--export-dynamic
and-static-pie
flags are present in the compilation so that the produced code is position independent executable that has as a symbol table. Finally the produced elf is linked with LFI's in-sandbox runtimelibboxrt
. -
Next we will create a simple assembly file that just embeds the produced lfi binary in a blob. This can be done easily with a file like this which includes the binary as a blob between two symbols
${INCSTUB_FILENAME}_start
and${INCSTUB_FILENAME}_end
. We define${INCSTUB_FILENAME}
to bemylib
in our example, so we get the requiredmylib_start
andmylib_end
symbols. -
Finally, we can now build our application, linking in binary embedding file, which points to our lfi binary. We also have link in the lfi-runtime's liblfi which is needed to instantiate and destroy sandboxes.
Building and running the lfi backend
To build this example on your machine, run the following commands
cd rlbox-book/src/examples/lfi-hello-example
cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Debug
cmake --build ./build --config Debug --parallel
Then run it to make sure everything is working.
./build/main
You should see the following output:
Hello from mylib
Got array value 7
echo: hi hi!
hello_cb: hi again!