Skip to content

Commit 6e354e7

Browse files
committed
feat: enforce syscall type-safety
This ensures nobody accidentally adds unsafe types to the syscall ABI.
1 parent 2428dd6 commit 6e354e7

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

fvm/src/syscalls/bind.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::mem;
22

33
use fvm_shared::error::ErrorNumber;
4+
use fvm_shared::sys::SyscallSafe;
45
use wasmtime::{Caller, Linker, Trap, WasmTy};
56

67
use super::context::Memory;
@@ -50,14 +51,14 @@ pub(super) trait BindSyscall<Args, Ret, Func> {
5051
/// results that can be handled by wasmtime. See the documentation on `BindSyscall` for details.
5152
#[doc(hidden)]
5253
pub trait IntoSyscallResult: Sized {
53-
type Value: Copy + Sized + 'static;
54+
type Value: SyscallSafe;
5455
fn into(self) -> Result<Result<Self::Value, SyscallError>, Abort>;
5556
}
5657

5758
// Implementations for syscalls that abort on error.
5859
impl<T> IntoSyscallResult for Result<T, Abort>
5960
where
60-
T: Copy + Sized + 'static,
61+
T: SyscallSafe,
6162
{
6263
type Value = T;
6364
fn into(self) -> Result<Result<Self::Value, SyscallError>, Abort> {
@@ -68,7 +69,7 @@ where
6869
// Implementations for normal syscalls.
6970
impl<T> IntoSyscallResult for kernel::Result<T>
7071
where
71-
T: Copy + Sized + 'static,
72+
T: SyscallSafe,
7273
{
7374
type Value = T;
7475
fn into(self) -> Result<Result<Self::Value, SyscallError>, Abort> {
@@ -135,7 +136,7 @@ macro_rules! impl_bind_syscalls {
135136
K: Kernel,
136137
Func: Fn(Context<'_, K> $(, $t)*) -> Ret + Send + Sync + 'static,
137138
Ret: IntoSyscallResult,
138-
$($t: WasmTy,)*
139+
$($t: WasmTy+SyscallSafe,)*
139140
{
140141
fn bind(
141142
&mut self,

fvm/src/syscalls/vm.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use fvm_shared::error::ExitCode;
2+
use fvm_shared::sys::SyscallSafe;
23
use fvm_shared::version::NetworkVersion;
4+
use num_traits::FromPrimitive;
35

46
use super::error::Abort;
57
use super::Context;
@@ -11,6 +13,8 @@ use crate::Kernel;
1113
#[derive(Copy, Clone)]
1214
pub enum Never {}
1315

16+
unsafe impl SyscallSafe for Never {}
17+
1418
// NOTE: this won't clobber the last syscall error because it directly returns a "trap".
1519
pub fn abort(
1620
context: Context<'_, impl Kernel>,

shared/src/sys/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,38 @@ impl<'a> TryFrom<&'a crate::econ::TokenAmount> for TokenAmount {
4343
})
4444
}
4545
}
46+
47+
/// An unsafe trait to mark "syscall safe" types. These types must be safe to memcpy to and from
48+
/// WASM. This means:
49+
///
50+
/// 1. Repr C & packed alignment (no reordering, no padding).
51+
/// 2. Copy, Sized, and no pointers.
52+
/// 3. No floats (non-determinism).
53+
///
54+
/// # Safety
55+
///
56+
/// Incorrectly implementing this could lead to undefined behavior in types passed between wasm and
57+
/// rust.
58+
pub unsafe trait SyscallSafe: Copy + Sized + 'static {}
59+
60+
macro_rules! assert_syscall_safe {
61+
($($t:ty,)*) => {
62+
$(unsafe impl SyscallSafe for $t {})*
63+
}
64+
}
65+
66+
assert_syscall_safe! {
67+
(),
68+
69+
u8, u16, u32, u64,
70+
i8, i16, i32, i64,
71+
72+
TokenAmount,
73+
out::actor::ResolveAddress,
74+
out::ipld::IpldOpen,
75+
out::ipld::IpldStat,
76+
out::send::Send,
77+
out::crypto::VerifyConsensusFault,
78+
}
79+
80+
unsafe impl<T, const N: usize> SyscallSafe for [T; N] where T: SyscallSafe {}

0 commit comments

Comments
 (0)