Skip to content

Commit

Permalink
Update wasmtime to 12.0.1 (#720)
Browse files Browse the repository at this point in the history
We were stuck on an old version of `wasmtime` because they removed the
ability to mark the file system as read only. We now wrap the file
system implementation and simply error out on anything that would write
to it. This is potentially a little buggier, but they plan on bringing
the feature back eventually anyway, so this implementation should be
fine in the meantime. I'm doing this upgrade, because their master
branch contains a bunch of improvements that we would want, such as
`serde` being decoupled from its `derive` crate and the debug support
now finally working. Unfortunately those are not released yet, but at
least we can now easily upgrade `wasmtime` again.
  • Loading branch information
CryZe authored Sep 7, 2023
1 parent e54c2d6 commit c487b50
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 20 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
- Linux mips musl
- Linux mips64 musl
- Linux mipsel musl
- Linux mips64el musl
- Linux powerpc
- Linux powerpc64
- Linux powerpc64le
Expand Down Expand Up @@ -404,6 +405,14 @@ jobs:
# https://github.com/LiveSplit/livesplit-core/issues/308
dylib: skip

- label: Linux mips64el musl
target: mips64el-unknown-linux-muslabi64
auto_splitting: skip
networking: skip
# FIXME: Networking Tests fail due to missing OpenSSL
# https://github.com/LiveSplit/livesplit-core/issues/308
dylib: skip

- label: Linux powerpc
target: powerpc-unknown-linux-gnu
auto_splitting: skip
Expand Down
7 changes: 4 additions & 3 deletions crates/livesplit-auto-splitting/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ edition = "2021"

[dependencies]
anyhow = { version = "1.0.45", default-features = false }
async-trait = "0.1.73"
proc-maps = { version = "0.3.0", default-features = false }
read-process-memory = { version = "0.1.4", default-features = false }
slotmap = { version = "1.0.2", default-features = false }
Expand All @@ -19,12 +20,12 @@ sysinfo = { version = "0.29.0", default-features = false, features = [
"multithread",
] }
time = { version = "0.3.3", default-features = false }
wasmtime = { version = "8.0.1", default-features = false, features = [
wasmtime = { version = "12.0.1", default-features = false, features = [
"cranelift",
"parallel-compilation",
] }
wasmtime-wasi = "8.0.1"
wasi-common = "8.0.1"
wasmtime-wasi = "12.0.1"
wasi-common = "12.0.1"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48.0", features = ["Win32_Storage_FileSystem"] }
101 changes: 84 additions & 17 deletions crates/livesplit-auto-splitting/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ use std::{
time::{Duration, Instant},
};
use sysinfo::{ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
use wasi_common::{dir::DirCaps, file::FileCaps};
use wasi_common::{
dir::{OpenResult, ReaddirCursor, ReaddirEntity},
file::{FdFlags, Filestat, OFlags},
ErrorExt, WasiDir,
};
use wasmtime::{
Caller, Engine, Extern, Linker, Memory, Module, OptLevel, Store, TypedFunc,
WasmBacktraceDetails,
Expand Down Expand Up @@ -244,8 +248,11 @@ impl<T: Timer> Runtime<T> {
.any(|import| import.module() == "wasi_snapshot_preview1");

if uses_wasi {
wasmtime_wasi::add_to_linker(&mut linker, |ctx| &mut ctx.wasi)
.map_err(|source| CreationError::Wasi { source })?;
wasmtime_wasi::snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(
&mut linker,
|ctx| &mut ctx.wasi,
)
.map_err(|source| CreationError::Wasi { source })?;
}

let instance = linker
Expand Down Expand Up @@ -368,13 +375,7 @@ fn build_wasi(script_path: Option<&Path>) -> WasiCtx {
ambient_authority(),
) {
wasi.push_dir(
Box::new(wasmtime_wasi::dir::Dir::from_cap_std(path)),
DirCaps::OPEN
| DirCaps::READDIR
| DirCaps::READLINK
| DirCaps::PATH_FILESTAT_GET
| DirCaps::FILESTAT_GET,
FileCaps::READ | FileCaps::SEEK | FileCaps::TELL | FileCaps::FILESTAT_GET,
Box::new(ReadOnlyDir(wasmtime_wasi::dir::Dir::from_cap_std(path))),
PathBuf::from(str::from_utf8(&[b'/', b'm', b'n', b't', b'/', drive]).unwrap()),
)
.unwrap();
Expand All @@ -385,13 +386,7 @@ fn build_wasi(script_path: Option<&Path>) -> WasiCtx {
{
if let Ok(path) = wasmtime_wasi::Dir::open_ambient_dir("/", ambient_authority()) {
wasi.push_dir(
Box::new(wasmtime_wasi::dir::Dir::from_cap_std(path)),
DirCaps::OPEN
| DirCaps::READDIR
| DirCaps::READLINK
| DirCaps::PATH_FILESTAT_GET
| DirCaps::FILESTAT_GET,
FileCaps::READ | FileCaps::SEEK | FileCaps::TELL | FileCaps::FILESTAT_GET,
Box::new(ReadOnlyDir(wasmtime_wasi::dir::Dir::from_cap_std(path))),
PathBuf::from("/mnt"),
)
.unwrap();
Expand All @@ -400,6 +395,78 @@ fn build_wasi(script_path: Option<&Path>) -> WasiCtx {
wasi
}

struct ReadOnlyDir(wasmtime_wasi::dir::Dir);

#[async_trait::async_trait]
impl WasiDir for ReadOnlyDir {
fn as_any(&self) -> &dyn std::any::Any {
self
}

async fn open_file(
&self,
symlink_follow: bool,
path: &str,
oflags: OFlags,
read: bool,
write: bool,
fdflags: FdFlags,
) -> Result<OpenResult, wasi_common::Error> {
// We whitelist the OFlags and FdFlags to not accidentally allow
// ways to modify the file system.
const WHITELISTED_O_FLAGS: OFlags = OFlags::DIRECTORY;
const WHITELISTED_FD_FLAGS: FdFlags = FdFlags::NONBLOCK;

if write || !WHITELISTED_O_FLAGS.contains(oflags) || !WHITELISTED_FD_FLAGS.contains(fdflags)
{
return Err(wasi_common::Error::not_supported());
}

Ok(
match self
.0
.open_file_(symlink_follow, path, oflags, read, write, fdflags)?
{
wasmtime_wasi::dir::OpenResult::Dir(d) => OpenResult::Dir(Box::new(ReadOnlyDir(d))),
// We assume that wrapping the file type itself is not
// necessary, because we ensure that the open flags don't allow
// for any modifications anyway.
wasmtime_wasi::dir::OpenResult::File(f) => OpenResult::File(Box::new(f)),
},
)
}

async fn readdir(
&self,
cursor: ReaddirCursor,
) -> Result<
Box<dyn Iterator<Item = Result<ReaddirEntity, wasi_common::Error>> + Send>,
wasi_common::Error,
> {
self.0.readdir(cursor).await
}

async fn read_link(&self, path: &str) -> Result<PathBuf, wasi_common::Error> {
self.0.read_link(path).await
}

async fn get_filestat(&self) -> Result<Filestat, wasi_common::Error> {
// FIXME: Make sure this says it's readonly, if it ever contains the
// permissions.
self.0.get_filestat().await
}

async fn get_path_filestat(
&self,
path: &str,
follow_symlinks: bool,
) -> Result<Filestat, wasi_common::Error> {
// FIXME: Make sure this says it's readonly, if it ever contains the
// permissions.
self.0.get_path_filestat(path, follow_symlinks).await
}
}

fn bind_interface<T: Timer>(linker: &mut Linker<Context<T>>) -> Result<(), CreationError> {
linker
.func_wrap(
Expand Down

0 comments on commit c487b50

Please sign in to comment.