diff --git a/soroban-env-host/src/auth.rs b/soroban-env-host/src/auth.rs index af25ebfa9..4bef0ac3a 100644 --- a/soroban-env-host/src/auth.rs +++ b/soroban-env-host/src/auth.rs @@ -938,7 +938,7 @@ impl AuthorizationManager { // This should be called for every `Host` `push_frame`. pub(crate) fn push_frame(&self, host: &Host, frame: &Frame) -> Result<(), HostError> { let (contract_id, function_name) = match frame { - Frame::ContractVM(vm, fn_name, ..) => { + Frame::ContractVM { vm, fn_name, .. } => { (vm.contract_id.metered_clone(host.budget_ref())?, *fn_name) } // Skip the top-level host function stack frames as they don't diff --git a/soroban-env-host/src/events/diagnostic.rs b/soroban-env-host/src/events/diagnostic.rs index 032a839a4..5866d3a78 100644 --- a/soroban-env-host/src/events/diagnostic.rs +++ b/soroban-env-host/src/events/diagnostic.rs @@ -64,7 +64,7 @@ impl Host { // Will not return error if frame is missing pub(crate) fn get_current_contract_id_unmetered(&self) -> Result, HostError> { self.with_current_frame_opt(|frame| match frame { - Some(Frame::ContractVM(vm, ..)) => Ok(Some(vm.contract_id.clone())), + Some(Frame::ContractVM { vm, .. }) => Ok(Some(vm.contract_id.clone())), Some(Frame::HostFunction(_)) => Ok(None), Some(Frame::Token(id, ..)) => Ok(Some(id.clone())), #[cfg(any(test, feature = "testutils"))] diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index d58f8ea6a..647b28986 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -1126,7 +1126,7 @@ impl VmCallerEnv for Host { &vm, pos, vals.as_mut_slice(), - |buf| Val::from_payload(u64::from_le_bytes(*buf)), + |buf| self.relative_to_absolute(Val::from_payload(u64::from_le_bytes(*buf))), )?; self.log_diagnostics(&msg, &vals) @@ -1665,7 +1665,7 @@ impl VmCallerEnv for Host { &vm, vals_pos, vals.as_mut_slice(), - |buf| Val::from_payload(u64::from_le_bytes(*buf)), + |buf| self.relative_to_absolute(Val::from_payload(u64::from_le_bytes(*buf))), )?; for v in vals.iter() { self.check_val_integrity(*v)?; @@ -1726,7 +1726,11 @@ impl VmCallerEnv for Host { &vm, vals_pos.into(), mapobj.map.as_slice(), - |pair| u64::to_le_bytes(pair.1.get_payload()), + |pair| { + Ok(u64::to_le_bytes( + self.absolute_to_relative(pair.1)?.get_payload(), + )) + }, )?; Ok(()) })?; @@ -1955,7 +1959,7 @@ impl VmCallerEnv for Host { &vm, pos, vals.as_mut_slice(), - |buf| Val::from_payload(u64::from_le_bytes(*buf)), + |buf| self.relative_to_absolute(Val::from_payload(u64::from_le_bytes(*buf))), )?; for v in vals.iter() { self.check_val_integrity(*v)?; @@ -1977,7 +1981,11 @@ impl VmCallerEnv for Host { &vm, vals_pos.into(), vecobj.as_slice(), - |x| u64::to_le_bytes(x.get_payload()), + |x| { + Ok(u64::to_le_bytes( + self.absolute_to_relative(*x)?.get_payload(), + )) + }, ) })?; Ok(Val::VOID) @@ -2834,8 +2842,8 @@ impl VmCallerEnv for Host { let mut outer = Vec::with_capacity(contexts.len()); for context in contexts.iter() { let vals = match &context.frame { - Frame::ContractVM(vm, function, ..) => { - get_host_val_tuple(&vm.contract_id, &function)? + Frame::ContractVM { vm, fn_name, .. } => { + get_host_val_tuple(&vm.contract_id, fn_name)? } Frame::HostFunction(_) => continue, Frame::Token(id, function, ..) => get_host_val_tuple(id, function)?, @@ -2893,7 +2901,7 @@ impl VmCallerEnv for Host { ) -> Result { let args = self.with_current_frame(|f| { let args = match f { - Frame::ContractVM(_, _, args, _) => args, + Frame::ContractVM { args, .. } => args, Frame::HostFunction(_) => { return Err(self.err( ScErrorType::Context, diff --git a/soroban-env-host/src/host/frame.rs b/soroban-env-host/src/host/frame.rs index 81ad16c87..7a865bd23 100644 --- a/soroban-env-host/src/host/frame.rs +++ b/soroban-env-host/src/host/frame.rs @@ -9,7 +9,7 @@ use crate::{ err, storage::{InstanceStorageMap, StorageMap}, xdr::{ContractCostType, ContractExecutable, Hash, HostFunction, HostFunctionType, ScVal}, - Error, Host, HostError, Symbol, SymbolStr, TryFromVal, TryIntoVal, Val, + Error, Host, HostError, Object, Symbol, SymbolStr, TryFromVal, TryIntoVal, Val, }; #[cfg(any(test, feature = "testutils"))] @@ -104,7 +104,13 @@ pub(crate) struct Context { /// commit or roll back that state when it pops the stack. #[derive(Clone)] pub(crate) enum Frame { - ContractVM(Rc, Symbol, Vec, ScContractInstance), + ContractVM { + vm: Rc, + fn_name: Symbol, + args: Vec, + instance: ScContractInstance, + relative_objects: Vec, + }, HostFunction(HostFunctionType), Token(Hash, Symbol, Vec, ScContractInstance), #[cfg(any(test, feature = "testutils"))] @@ -246,6 +252,30 @@ impl Host { } } + pub(crate) fn with_current_frame_relative_object_table( + &self, + f: F, + ) -> Result + where + F: FnOnce(&mut Vec) -> Result, + { + self.with_current_context_mut(|ctx| { + if let Frame::ContractVM { + relative_objects, .. + } = &mut ctx.frame + { + f(relative_objects) + } else { + Err(self.err( + ScErrorType::Context, + ScErrorCode::InternalError, + "accessing relative object table in non-VM frame", + &[], + )) + } + }) + } + pub(crate) fn with_current_prng(&self, f: F) -> Result where F: FnOnce(&mut Prng) -> Result, @@ -327,7 +357,7 @@ impl Host { /// frame at its top. pub(crate) fn get_current_contract_id_opt_internal(&self) -> Result, HostError> { self.with_current_frame(|frame| match frame { - Frame::ContractVM(vm, ..) => Ok(Some(vm.contract_id.metered_clone(&self.0.budget)?)), + Frame::ContractVM { vm, .. } => Ok(Some(vm.contract_id.metered_clone(&self.0.budget)?)), Frame::HostFunction(_) => Ok(None), Frame::Token(id, ..) => Ok(Some(id.metered_clone(&self.0.budget)?)), #[cfg(any(test, feature = "testutils"))] @@ -356,7 +386,7 @@ impl Host { // the previous frame must exist and must be a contract let hash = match frames.as_slice() { [.., c2, _] => match &c2.frame { - Frame::ContractVM(vm, ..) => Ok(vm.contract_id.metered_clone(&self.0.budget)?), + Frame::ContractVM { vm, .. } => Ok(vm.contract_id.metered_clone(&self.0.budget)?), Frame::HostFunction(_) => Err(self.err( ScErrorType::Context, ScErrorCode::UnexpectedType, @@ -385,7 +415,7 @@ impl Host { let st = match frames.as_slice() { // There are always two frames when WASM is executed in the VM. [.., c2, _] => match &c2.frame { - Frame::ContractVM(..) => Ok(InvokerType::Contract), + Frame::ContractVM { .. } => Ok(InvokerType::Contract), Frame::HostFunction(_) => Ok(InvokerType::Account), Frame::Token(..) => Ok(InvokerType::Contract), #[cfg(any(test, feature = "testutils"))] @@ -428,8 +458,8 @@ impl Host { fn call_contract_fn(&self, id: &Hash, func: &Symbol, args: &[Val]) -> Result { // Create key for storage let storage_key = self.contract_instance_ledger_key(id)?; - let contract_instance = self.retrieve_contract_instance_from_storage(&storage_key)?; - match &contract_instance.executable { + let instance = self.retrieve_contract_instance_from_storage(&storage_key)?; + match &instance.executable { ContractExecutable::Wasm(wasm_hash) => { let code_entry = self.retrieve_wasm_from_storage(&wasm_hash)?; let vm = Vm::new( @@ -437,13 +467,20 @@ impl Host { id.metered_clone(&self.0.budget)?, code_entry.as_slice(), )?; + let relative_objects = Vec::new(); self.with_frame( - Frame::ContractVM(vm.clone(), *func, args.to_vec(), contract_instance), + Frame::ContractVM { + vm: vm.clone(), + fn_name: *func, + args: args.to_vec(), + instance, + relative_objects, + }, || vm.invoke_function_raw(self, func, args), ) } ContractExecutable::Token => self.with_frame( - Frame::Token(id.clone(), *func, args.to_vec(), contract_instance), + Frame::Token(id.clone(), *func, args.to_vec(), instance), || { use crate::native_contract::{NativeContract, Token}; Token.call(func, self, args) @@ -480,7 +517,7 @@ impl Host { let mut is_last_non_host_frame = true; for ctx in self.try_borrow_context()?.iter().rev() { let exist_id = match &ctx.frame { - Frame::ContractVM(vm, ..) => &vm.contract_id, + Frame::ContractVM { vm, .. } => &vm.contract_id, Frame::Token(id, ..) => id, #[cfg(any(test, feature = "testutils"))] Frame::TestContract(tc) => &tc.id, @@ -683,7 +720,7 @@ impl Host { return Ok(()); } let storage_map = match &ctx.frame { - Frame::ContractVM(_, _, _, instance) => &instance.storage, + Frame::ContractVM { instance, .. } => &instance.storage, Frame::HostFunction(_) => { return Err(self.err( ScErrorType::Context, @@ -722,7 +759,7 @@ impl Host { return Ok(None); } let executable = match &ctx.frame { - Frame::ContractVM(_, _, _, instance) => { + Frame::ContractVM { instance, .. } => { instance.executable.metered_clone(self.budget_ref())? } Frame::HostFunction(_) => { diff --git a/soroban-env-host/src/host/mem_helper.rs b/soroban-env-host/src/host/mem_helper.rs index 97d19b029..fd7945deb 100644 --- a/soroban-env-host/src/host/mem_helper.rs +++ b/soroban-env-host/src/host/mem_helper.rs @@ -18,7 +18,7 @@ impl Host { let pos: u32 = pos.into(); let len: u32 = len.into(); self.with_current_frame(|frame| match frame { - Frame::ContractVM(vm, ..) => { + Frame::ContractVM { vm, .. } => { let vm = vm.clone(); Ok(VmSlice { vm, pos, len }) } @@ -67,7 +67,7 @@ impl Host { vm: &Rc, mem_pos: u32, buf: &[VAL], - to_le_bytes: impl Fn(&VAL) -> [u8; VAL_SZ], + to_le_bytes: impl Fn(&VAL) -> Result<[u8; VAL_SZ], HostError>, ) -> Result<(), HostError> { let val_sz = self.usize_to_u32(VAL_SZ)?; let len = self.usize_to_u32(buf.len())?; @@ -96,7 +96,7 @@ impl Host { &[], )); } - let tmp: [u8; VAL_SZ] = to_le_bytes(src); + let tmp: [u8; VAL_SZ] = to_le_bytes(src)?; dst.copy_from_slice(&tmp); } Ok(()) @@ -108,7 +108,7 @@ impl Host { vm: &Rc, mem_pos: u32, buf: &mut [VAL], - from_le_bytes: impl Fn(&[u8; VAL_SZ]) -> VAL, + from_le_bytes: impl Fn(&[u8; VAL_SZ]) -> Result, ) -> Result<(), HostError> { let val_sz = self.usize_to_u32(VAL_SZ)?; let len = self.usize_to_u32(buf.len())?; @@ -131,7 +131,7 @@ impl Host { for (dst, src) in buf.iter_mut().zip(mem_slice.chunks(VAL_SZ)) { if let Ok(src) = TryInto::<&[u8; VAL_SZ]>::try_into(src) { tmp.copy_from_slice(src); - *dst = from_le_bytes(&tmp); + *dst = from_le_bytes(&tmp)?; } else { // This should be impossible unless there's an error above, but just in case. return Err(self.err( diff --git a/soroban-env-host/src/host_object.rs b/soroban-env-host/src/host_object.rs index 004a707fa..a7ebb43fc 100644 --- a/soroban-env-host/src/host_object.rs +++ b/soroban-env-host/src/host_object.rs @@ -1,6 +1,9 @@ +#![allow(dead_code)] + use soroban_env_common::{ - xdr::ContractCostType, Compare, DurationSmall, I128Small, I256Small, I64Small, SymbolSmall, - SymbolStr, Tag, TimepointSmall, U128Small, U256Small, U64Small, + xdr::{ContractCostType, ScErrorCode, ScErrorType}, + Compare, DurationSmall, I128Small, I256Small, I64Small, SymbolSmall, SymbolStr, Tag, + TimepointSmall, U128Small, U256Small, U64Small, }; use crate::{ @@ -169,7 +172,98 @@ declare_mem_host_object_type!(xdr::ScString, StringObject, String); declare_mem_host_object_type!(xdr::ScSymbol, SymbolObject, Symbol); declare_host_object_type!(xdr::ScAddress, AddressObject, Address); +// Objects come in two flavors: relative and absolute. Relative objects are the +// ones we pass to and from wasm code, and are looked up indirectly through a +// per-context table to find their absolute values. Absolute objects are the +// same in all contexts (and outside contexts, eg. in host objects themselves or +// while setting-up the host). Relative-to-absolute translation is done very +// close to the VM, when marshalling. Host code should never see relative object +// handles, and if you ever try to look one up in the host object table, it will +// fail. +// +// Also note: the relative/absolute object reference translation is _not_ done +// when running in native / local-testing mode, so you will not get identical +// object numbers in that case. Since there is no real isolation between +// contracts in that mode, there's no point bothering with the translation (and +// there's no really obvious place to perform it systematically, like in the +// wasm marshalling path). + +pub fn is_relative_object_handle(handle: u32) -> bool { + handle & 1 == 0 +} + +pub fn is_relative_object(obj: Object) -> bool { + is_relative_object_handle(obj.get_handle()) +} + +pub fn is_relative_object_value(val: Val) -> bool { + if let Ok(obj) = Object::try_from(val) { + is_relative_object(obj) + } else { + false + } +} + +pub fn handle_to_index(handle: u32) -> usize { + (handle as usize) >> 1 +} + +pub fn index_to_handle(host: &Host, index: usize, relative: bool) -> Result { + if let Ok(smaller) = u32::try_from(index) { + if let Some(shifted) = smaller.checked_shl(1) { + if relative { + return Ok(shifted); + } else { + return Ok(shifted | 1); + } + } + } + Err(host.err_arith_overflow()) +} + impl Host { + pub(crate) fn relative_to_absolute(&self, val: Val) -> Result { + if let Ok(obj) = Object::try_from(val) { + let handle = obj.get_handle(); + return if is_relative_object_handle(handle) { + let index = handle_to_index(handle); + self.with_current_frame_relative_object_table(|table| match table.get(index) { + Some(abs) => Ok(abs.into()), + None => Err(self.err( + ScErrorType::Context, + ScErrorCode::InvalidInput, + "unknown relative object reference", + &[val], + )), + }) + } else { + Err(self.err( + ScErrorType::Context, + ScErrorCode::InvalidInput, + "relative_obj_to_absolute given an absolute reference", + &[val], + )) + }; + } + Ok(val) + } + + pub(crate) fn absolute_to_relative(&self, val: Val) -> Result { + if let Ok(obj) = Object::try_from(val) { + let handle = obj.get_handle(); + // FIXME: I _think_ relative inputs should actually be banned too? + if !is_relative_object_handle(handle) { + return self.with_current_frame_relative_object_table(|table| { + let index = table.len(); + let handle = index_to_handle(self, index, true)?; + table.push(obj); + Ok(Object::from_handle_and_tag(handle, val.get_tag()).into()) + }); + } + } + Ok(val) + } + /// Moves a value of some type implementing [`HostObjectType`] into the host's /// object array, returning a [`HostObj`] containing the new object's array /// index, tagged with the [`xdr::ScObjectType`]. @@ -177,15 +271,12 @@ impl Host { &self, hot: HOT, ) -> Result { - let prev_len = self.try_borrow_objects()?.len(); - if prev_len > u32::MAX as usize { - return Err(self.err_arith_overflow()); - } + let index = self.try_borrow_objects()?.len(); + let handle = index_to_handle(self, index, false)?; // charge for the new host object, which is just the amortized cost of a single // `HostObject` allocation metered_clone::charge_heap_alloc::(1, self.as_budget())?; self.try_borrow_objects_mut()?.push(HOT::inject(hot)); - let handle = prev_len as u32; Ok(HOT::new_from_handle(handle)) } @@ -203,7 +294,16 @@ impl Host { let r = self.try_borrow_objects()?; let obj: Object = obj.into(); let handle: u32 = obj.get_handle(); - f(r.get(handle as usize)) + if is_relative_object_handle(handle) { + Err(self.err( + ScErrorType::Context, + ScErrorCode::InternalError, + "looking up relative object", + &[obj.to_val()], + )) + } else { + f(r.get(handle_to_index(handle))) + } } pub(crate) fn check_val_integrity(&self, val: Val) -> Result<(), HostError> { diff --git a/soroban-env-host/src/test/basic.rs b/soroban-env-host/src/test/basic.rs index 64352090d..b3e13dc81 100644 --- a/soroban-env-host/src/test/basic.rs +++ b/soroban-env-host/src/test/basic.rs @@ -16,7 +16,7 @@ fn u64_roundtrip() -> Result<(), HostError> { let v2: Val = u2.try_into_val(&host)?; assert_eq!(v2.get_tag(), Tag::U64Object); let obj: Object = v2.try_into()?; - assert_eq!(obj.get_handle(), 0); + assert_eq!(obj.get_handle(), 1); let k = u64::try_from_val(&host, &v2)?; assert_eq!(u2, k); Ok(()) @@ -35,7 +35,7 @@ fn i64_roundtrip() -> Result<(), HostError> { let v2: Val = i2.try_into_val(&host)?; assert_eq!(v2.get_tag(), Tag::I64Object); let obj: Object = v2.try_into()?; - assert_eq!(obj.get_handle(), 0); + assert_eq!(obj.get_handle(), 1); let k = i64::try_from_val(&host, &v2)?; assert_eq!(i2, k); Ok(()) diff --git a/soroban-env-host/src/test/vec.rs b/soroban-env-host/src/test/vec.rs index 480a858be..b9d87cd2b 100644 --- a/soroban-env-host/src/test/vec.rs +++ b/soroban-env-host/src/test/vec.rs @@ -16,8 +16,8 @@ fn vec_as_seen_by_host() -> Result<(), HostError> { assert_eq!(val1.get_tag(), Tag::VecObject); let obj0: Object = val0.try_into()?; let obj1: Object = val1.try_into()?; - assert_eq!(obj0.get_handle(), 0); - assert_eq!(obj1.get_handle(), 1); + assert_eq!(obj0.get_handle(), 1); + assert_eq!(obj1.get_handle(), 3); assert_eq!(obj0.as_val().get_tag(), Tag::VecObject); assert_eq!(obj1.as_val().get_tag(), Tag::VecObject); // Check that we got 2 distinct Vec objects diff --git a/soroban-env-host/src/vm.rs b/soroban-env-host/src/vm.rs index a0de31fae..356c3d13c 100644 --- a/soroban-env-host/src/vm.rs +++ b/soroban-env-host/src/vm.rs @@ -268,9 +268,8 @@ impl Vm { } } } - Ok( - <_ as WasmiMarshal>::try_marshal_from_value(wasm_ret[0].clone()) - .ok_or(ConversionError)?, + host.relative_to_absolute( + Val::try_marshal_from_value(wasm_ret[0].clone()).ok_or(ConversionError)?, ) } @@ -283,8 +282,8 @@ impl Vm { host.charge_budget(ContractCostType::InvokeVmFunction, None)?; let wasm_args: Vec = args .iter() - .map(|i| Value::I64(i.get_payload() as i64)) - .collect(); + .map(|i| host.absolute_to_relative(*i).map(|v| v.marshal_from_self())) + .collect::, HostError>>()?; let func_ss: SymbolStr = func_sym.try_into_val(host)?; let ext = match self .instance diff --git a/soroban-env-host/src/vm/dispatch.rs b/soroban-env-host/src/vm/dispatch.rs index 505e97d67..4cd959e12 100644 --- a/soroban-env-host/src/vm/dispatch.rs +++ b/soroban-env-host/src/vm/dispatch.rs @@ -1,9 +1,9 @@ use super::FuelRefillable; use crate::{xdr::ContractCostType, Host, HostError, VmCaller, VmCallerEnv}; use crate::{ - AddressObject, BytesObject, DurationObject, Error, I128Object, I256Object, I256Val, I64Object, - MapObject, StorageType, StringObject, Symbol, SymbolObject, TimepointObject, U128Object, - U256Object, U256Val, U32Val, U64Object, Val, VecObject, + AddressObject, Bool, BytesObject, DurationObject, Error, I128Object, I256Object, I256Val, + I32Val, I64Object, MapObject, StorageType, StringObject, Symbol, SymbolObject, TimepointObject, + U128Object, U256Object, U256Val, U32Val, U64Object, U64Val, Val, VecObject, Void, }; use soroban_env_common::{call_macro_with_all_host_functions, WasmiMarshal}; use wasmi::{ @@ -11,6 +11,76 @@ use wasmi::{ Value, }; +pub(crate) trait RelativeObjectConversion: WasmiMarshal { + fn absolute_to_relative(self, _host: &Host) -> Result { + Ok(self) + } + fn relative_to_absolute(self, _host: &Host) -> Result { + Ok(self) + } + fn try_marshal_from_relative_value(v: wasmi::Value, host: &Host) -> Result { + let val = Self::try_marshal_from_value(v).ok_or(BadSignature)?; + Ok(val.relative_to_absolute(host)?) + } + fn marshal_relative_from_self(self, host: &Host) -> Result { + let rel = self.absolute_to_relative(host)?; + Ok(Self::marshal_from_self(rel)) + } +} + +macro_rules! impl_relative_object_conversion { + ($T:ty) => { + impl RelativeObjectConversion for $T { + fn absolute_to_relative(self, host: &Host) -> Result { + Ok(Self::try_from(host.absolute_to_relative(self.into())?)?) + } + + fn relative_to_absolute(self, host: &Host) -> Result { + Ok(Self::try_from(host.relative_to_absolute(self.into())?)?) + } + } + }; +} + +// Define a relative-to-absolute impl for any type that is (a) mentioned +// in a host function type signature in env and (b) might possibly carry an +// object reference. If you miss one, this file won't compile, so it's safe. +impl_relative_object_conversion!(Val); +impl_relative_object_conversion!(Symbol); + +impl_relative_object_conversion!(AddressObject); +impl_relative_object_conversion!(BytesObject); +impl_relative_object_conversion!(DurationObject); + +impl_relative_object_conversion!(TimepointObject); +impl_relative_object_conversion!(SymbolObject); +impl_relative_object_conversion!(StringObject); + +impl_relative_object_conversion!(VecObject); +impl_relative_object_conversion!(MapObject); + +impl_relative_object_conversion!(I64Object); +impl_relative_object_conversion!(I128Object); +impl_relative_object_conversion!(I256Object); + +impl_relative_object_conversion!(U64Object); +impl_relative_object_conversion!(U128Object); +impl_relative_object_conversion!(U256Object); + +impl_relative_object_conversion!(U64Val); +impl_relative_object_conversion!(U256Val); +impl_relative_object_conversion!(I256Val); + +// Trivial / non-relativizing impls are ok for types that can't carry objects. +impl RelativeObjectConversion for i64 {} +impl RelativeObjectConversion for u64 {} +impl RelativeObjectConversion for Void {} +impl RelativeObjectConversion for Bool {} +impl RelativeObjectConversion for Error {} +impl RelativeObjectConversion for StorageType {} +impl RelativeObjectConversion for U32Val {} +impl RelativeObjectConversion for I32Val {} + /////////////////////////////////////////////////////////////////////////////// /// X-macro use: dispatch functions /////////////////////////////////////////////////////////////////////////////// @@ -90,11 +160,11 @@ macro_rules! generate_dispatch_functions { // happens to be a natural switching point for that: we have // conversions to and from both Val and i64 / u64 for // wasmi::Value. - let res: Result<_, HostError> = host.$fn_id(&mut vmcaller, $(<$type>::try_marshal_from_value(Value::I64($arg)).ok_or(BadSignature)?),*); + let res: Result<_, HostError> = host.$fn_id(&mut vmcaller, $(<$type>::try_marshal_from_relative_value(Value::I64($arg), &host)?),*); let res = match res { Ok(ok) => { - let val: Value = ok.marshal_from_self(); + let val: Value = ok.marshal_relative_from_self(&host)?; if let Value::I64(v) = val { Ok((v,)) } else {