Skip to content

Commit

Permalink
Describe wasi-filesystem's path sandboxing behavior (#127)
Browse files Browse the repository at this point in the history
* Describe wasi-filesystem's path sandboxing behavior

Add a new path-resolution.md document describing CloudABI-style path
resolution behavior. wit/types.wit previously had a brief description
of this behavior, however this new document presents it in much more
detail, with a description of the sandboxing properties and a
discussion of implementation techniques.

* Mention the stance on external filesystem accesses.

* Update path-resolution.md

Co-authored-by: Guy Bedford <[email protected]>

* Move host concerns into their own section, and add more context about optimizations.

---------

Co-authored-by: Guy Bedford <[email protected]>
  • Loading branch information
sunfishcode and guybedford authored Aug 22, 2023
1 parent ba8b5c5 commit 5afa325
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ Unlike many filesystem APIs, WASI filesystem is capability-oriented. Instead
of having functions that implicitly reference a filesystem namespace,
WASI filesystems' APIs are passed a directory handle along with a path, and
the path is looked up relative to the given handle, and sandboxed to be
resolved within that directory.
resolved within that directory. For more information about sandbox, see
[WASI filesystem path resolution](path-resolution.md).

WASI filesystem hides some of the surface differences between Windows and
Unix-style filesystems, however much of its behavior, indluding the
Expand Down
2 changes: 2 additions & 0 deletions example-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ function starts with <code>/</code>, or if any step of resolving a <code>path</c
<code>..</code> and symbolic link steps, reaches a directory outside of the base
directory, or reaches a symlink to an absolute or rooted path in the
underlying filesystem, the function fails with <a href="#error_code.not_permitted"><code>error-code::not-permitted</code></a>.</p>
<p>For more information about WASI path resolution and sandboxing, see
<a href="https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md">WASI filesystem path resolution</a>.</p>
<hr />
<h3>Types</h3>
<h4><a name="input_stream"><code>type input-stream</code></a></h4>
Expand Down
91 changes: 91 additions & 0 deletions path-resolution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# WASI filesystem path resolution

wasi-filesystem uses a filesystem path sandboxing scheme modeled after the
system used in [CloudABI], which is also similar to the system used in
[Capsicum].

On Linux, it corresponds to the `RESOLVE_BENEATH` behavior in
[Linux's `openat2`]. In FreeBSD, it corresponds to the `O_RESOLVE_BENEATH`
behavior in [FreeBSD's `open`]. However, path resolution can also be
implemented manually using `openat` and `readlinkat` or similar primitives.

## Sandboxing overview

All functions in wasi-filesystem which operate on filesystem paths take
a pair of values: a base directory handle, and a relative path. Absolute
paths are not permitted, and there is no global namespace. All path
accesses are relative to a base directory handle.

Path resolution is constrained to occur within the sub-filesystem referenced
by the base handle. Information about the filesystem outside of the base
directory handles is not visible. In particular, it's not permitted to use
paths that temporarily step outside the sandbox with something like
"../../../stuff/here", even if the final resolved path is back inside the
sandbox, because that would leak information about the existence of
directories outside the sandbox.

Importantly, the sandboxing is designed to be implementable even in the presence
of outside processes accessing the same filesystem, including renaming,
unlinking, and creating new files and directories.

## Symlinks

Creating a symlink with an absolute path string fails with a "not permitted"
error.

Other than that, symlinks may be created with any string, provided the
underlying filesystem implementation supports it.

Sandboxing for symlink strings is performed at the time of an access, when a
path is being resolved, and not at the time that the symlink is created or
moved. This ensures that the sandbox is respected even if there are symlinks
created or renamed by other entities with access to the filesystem.

## Host Implementation

### Implementing path resolution manually

Plain `openat` doesn't perform any sandboxing; it will readily open paths
containing ".." or starting with "/", or symlinks to paths containing ".."
or starting with "/". It has an `O_NOFOLLOW` flag, however this flag only
applies to the last component of the path (eg. the "c" in "a/b/c"). So
the strategy for using `openat` to implement sandboxing is to split paths
into components (eg. "a", "b", "c") and open them one component at a time,
so that each component can be opened with `O_NOFOLLOW`.

If the `openat` call fails, and the OS error code indicates that it *was*
a symlink (eg. `ELOOP`), then call `readlinkat` to read the link contents,
split the contents into components, and prepend these new components to the
component list. If it starts with an absolute path, that's an attempt to
jump outside the sandbox, so path resolution should fail with an
"access denied" error message.

If a path component is "..", instead of opening it, pop an item off of the
component list. If the list was empty, that represents an attempt to use
".." to step outside the sandbox, so path resolution should fail with an
"access denied" error message.

### Implementation notes

On Linux, `openat2` with `RESOLVE_BENEATH` may be used as an optimization to
implement many system calls other than just "open" by utilizing Linux's
`O_PATH` and "/proc/self/fd" features.

On Windows, the [`NtCreateFile`] function can accept a directory handle and
can behave like an `openat` function, which can be used in the
[manual algorithm](implementing-path-resolution-manually).

The Rust library [cap-std] implements WASI's filesystem sandboxing semantics,
but is otherwise independent of WASI or Wasm, so it can be reused in other
settings. It uses `openat2` and `NtCreateFile` and other optimizations.

cloudabi-utils has an [implementation of the manual technique in C], though
that repository is no longer maintained.

[implementation of the manual technique in C]: https://github.com/NuxiNL/cloudabi-utils/blob/master/src/libemulator/posix.c#L1205
[cap-std]: https://github.com/bytecodealliance/cap-std
[Linux's `openat2`]: https://man7.org/linux/man-pages/man2/openat2.2.html
[CloudABI]: https://github.com/NuxiNL/cloudabi
[Capsicum]: https://wiki.freebsd.org/Capsicum
[FreeBSD's `open`]: https://man.freebsd.org/cgi/man.cgi?sektion=2&query=open
[`NtCreateFile`]: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
5 changes: 5 additions & 0 deletions wit/types.wit
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
/// `..` and symbolic link steps, reaches a directory outside of the base
/// directory, or reaches a symlink to an absolute or rooted path in the
/// underlying filesystem, the function fails with `error-code::not-permitted`.
///
/// For more information about WASI path resolution and sandboxing, see
/// [WASI filesystem path resolution].
///
/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md
interface types {
use wasi:io/streams.{input-stream, output-stream}
use wasi:clocks/wall-clock.{datetime}
Expand Down

0 comments on commit 5afa325

Please sign in to comment.