diff --git a/gsys/src/lib.rs b/gsys/src/lib.rs index 6cb4cdeb15c..daf6b517c75 100644 --- a/gsys/src/lib.rs +++ b/gsys/src/lib.rs @@ -474,7 +474,7 @@ extern "C" { err_mid_pid: *mut ErrorWithTwoHashes, ); - /// Fallible `gr_reply_deposit` syscall. + /// Fallible `gr_reply_deposit` control syscall. /// /// Arguments type: /// - `message_id`: `const ptr` for message id. diff --git a/pallets/gear/src/benchmarking/code.rs b/pallets/gear/src/benchmarking/code.rs index 90821ae1a37..9d4384e6377 100644 --- a/pallets/gear/src/benchmarking/code.rs +++ b/pallets/gear/src/benchmarking/code.rs @@ -236,10 +236,13 @@ where // Import supervisor functions. They start with idx 0. for name in def.imported_functions { let sign = name.signature(); - let sig = builder::signature() - .with_params(sign.params.into_iter().map(Into::into)) - .with_results(sign.results.into_iter()) - .build_sig(); + let sig_builder = + builder::signature().with_params(sign.params().iter().copied().map(Into::into)); + let results = sign + .results() + .map(|results| results.to_vec()) + .unwrap_or_default(); + let sig = sig_builder.with_results(results.into_iter()).build_sig(); let sig = program.push_signature(sig); program = program .import() diff --git a/pallets/gear/src/benchmarking/syscalls.rs b/pallets/gear/src/benchmarking/syscalls.rs index 411b1a55476..0dd8a93499f 100644 --- a/pallets/gear/src/benchmarking/syscalls.rs +++ b/pallets/gear/src/benchmarking/syscalls.rs @@ -1385,10 +1385,10 @@ where assert!(repetitions <= 1); let params = if let Some(c) = param { - assert!(name.signature().params.len() == 1); + assert!(name.signature().params().len() == 1); vec![InstrI32Const(c)] } else { - assert!(name.signature().params.is_empty()); + assert!(name.signature().params().is_empty()); vec![] }; diff --git a/utils/node-loader/src/utils.rs b/utils/node-loader/src/utils.rs index c155c61a1b4..3cf66ccdeaa 100644 --- a/utils/node-loader/src/utils.rs +++ b/utils/node-loader/src/utils.rs @@ -7,8 +7,8 @@ use gear_core::ids::{MessageId, ProgramId}; use gear_core_errors::ReplyCode; use gear_utils::NonEmpty; use gear_wasm_gen::{ - EntryPointsSet, InvocableSyscall, ParamType, StandardGearWasmConfigsBundle, SyscallName, - SyscallsInjectionTypes, SyscallsParamsConfig, + EntryPointsSet, InvocableSyscall, ParamType, RegularParamType, StandardGearWasmConfigsBundle, + SyscallName, SyscallsInjectionTypes, SyscallsParamsConfig, }; use gsdk::metadata::runtime_types::{ gear_common::event::DispatchStatus as GenDispatchStatus, @@ -230,8 +230,11 @@ pub fn get_wasm_gen_config( ); let mut params_config = SyscallsParamsConfig::default(); - params_config.add_rule(ParamType::Alloc, (1..=10).into()); - params_config.add_rule(ParamType::Free, (initial_pages..=initial_pages + 50).into()); + params_config.add_rule(ParamType::Regular(RegularParamType::Alloc), (1..=10).into()); + params_config.add_rule( + ParamType::Regular(RegularParamType::Free), + (initial_pages..=initial_pages + 50).into(), + ); StandardGearWasmConfigsBundle { log_info: Some(format!("Gear program seed = '{seed}'")), diff --git a/utils/runtime-fuzzer/src/gear_calls.rs b/utils/runtime-fuzzer/src/gear_calls.rs index cf241af7040..1e57f8d81a4 100644 --- a/utils/runtime-fuzzer/src/gear_calls.rs +++ b/utils/runtime-fuzzer/src/gear_calls.rs @@ -35,8 +35,8 @@ use gear_call_gen::{ClaimValueArgs, SendReplyArgs}; use gear_core::ids::{CodeId, MessageId, ProgramId}; use gear_utils::NonEmpty; use gear_wasm_gen::{ - EntryPointsSet, InvocableSyscall, ParamType, StandardGearWasmConfigsBundle, SyscallName, - SyscallsInjectionTypes, SyscallsParamsConfig, + EntryPointsSet, InvocableSyscall, ParamType, RegularParamType, StandardGearWasmConfigsBundle, + SyscallName, SyscallsInjectionTypes, SyscallsParamsConfig, }; use std::mem; @@ -425,8 +425,14 @@ fn config( ); let mut params_config = SyscallsParamsConfig::default(); - params_config.add_rule(ParamType::Alloc, (10..=20).into()); - params_config.add_rule(ParamType::Free, (initial_pages..=initial_pages + 35).into()); + params_config.add_rule( + ParamType::Regular(RegularParamType::Alloc), + (10..=20).into(), + ); + params_config.add_rule( + ParamType::Regular(RegularParamType::Free), + (initial_pages..=initial_pages + 35).into(), + ); let existing_addresses = NonEmpty::collect( programs diff --git a/utils/wasm-gen/src/config/syscalls.rs b/utils/wasm-gen/src/config/syscalls.rs index ede17d5def5..531a0889cec 100644 --- a/utils/wasm-gen/src/config/syscalls.rs +++ b/utils/wasm-gen/src/config/syscalls.rs @@ -22,15 +22,16 @@ mod injection; mod param; mod precise; +mod process_errors; use gear_utils::NonEmpty; use gear_wasm_instrument::syscalls::SyscallName; use gsys::{Hash, HashWithValue}; -use std::collections::HashSet; pub use injection::*; pub use param::*; pub use precise::*; +pub use process_errors::*; use crate::InvocableSyscall; @@ -117,30 +118,6 @@ impl SyscallsConfigBuilder { } } -#[derive(Debug, Clone, Default)] -pub enum ErrorProcessingConfig { - /// Process errors on all the fallible syscalls. - All, - /// Process only errors on provided syscalls. - Whitelist(HashSet), - /// Process errors on all the syscalls excluding provided. - Blacklist(HashSet), - /// Don't process syscall errors at all. - #[default] - None, -} - -impl ErrorProcessingConfig { - pub fn error_should_be_processed(&self, syscall: &InvocableSyscall) -> bool { - match self { - Self::All => true, - Self::Whitelist(wl) => wl.contains(syscall), - Self::Blacklist(bl) => !bl.contains(syscall), - Self::None => false, - } - } -} - /// United config for all entities in syscalls generator module. #[derive(Debug, Clone, Default)] pub struct SyscallsConfig { diff --git a/utils/wasm-gen/src/config/syscalls/injection.rs b/utils/wasm-gen/src/config/syscalls/injection.rs index 681c77496d2..52b08befb1b 100644 --- a/utils/wasm-gen/src/config/syscalls/injection.rs +++ b/utils/wasm-gen/src/config/syscalls/injection.rs @@ -109,7 +109,7 @@ impl SyscallsInjectionTypes { } } - /// Imports the given syscall, if necessary. + /// Imports the given syscall, if possible. pub(crate) fn enable_syscall_import(&mut self, name: InvocableSyscall) { if let Some(injection_type @ SyscallInjectionType::None) = self.0.get_mut(&name) { *injection_type = SyscallInjectionType::Import; diff --git a/utils/wasm-gen/src/config/syscalls/param.rs b/utils/wasm-gen/src/config/syscalls/param.rs index 6e2352e41ae..8b0815530ff 100644 --- a/utils/wasm-gen/src/config/syscalls/param.rs +++ b/utils/wasm-gen/src/config/syscalls/param.rs @@ -24,7 +24,7 @@ use crate::DEFAULT_INITIAL_SIZE; use arbitrary::{Result, Unstructured}; use std::{collections::HashMap, ops::RangeInclusive}; -pub use gear_wasm_instrument::syscalls::ParamType; +pub use gear_wasm_instrument::syscalls::{ParamType, RegularParamType}; /// Syscalls params config. /// @@ -50,17 +50,21 @@ impl SyscallsParamsConfig { /// New [`SyscallsParamsConfig`] with all rules set to produce one constant value. pub fn all_constant_value(value: i64) -> Self { + use ParamType::*; + use RegularParamType::*; + let allowed_values: SyscallParamAllowedValues = (value..=value).into(); Self( [ - ParamType::Length, - ParamType::Gas, - ParamType::Offset, - ParamType::DurationBlockNumber, - ParamType::DelayBlockNumber, - ParamType::Handler, - ParamType::Free, - ParamType::Version, + Regular(Length), + Regular(Gas), + Regular(Offset), + Regular(DurationBlockNumber), + Regular(DelayBlockNumber), + Regular(Handler), + Regular(Free), + Regular(FreeUpperBound), + Regular(Version), ] .into_iter() .map(|param_type| (param_type, allowed_values.clone())) @@ -75,7 +79,7 @@ impl SyscallsParamsConfig { /// Set allowed values for the `param`. pub fn add_rule(&mut self, param: ParamType, allowed_values: SyscallParamAllowedValues) { - matches!(param, ParamType::Ptr(..)) + matches!(param, ParamType::Regular(RegularParamType::Pointer(_))) .then(|| panic!("ParamType::Ptr(..) isn't supported in SyscallsParamsConfig")); self.0.insert(param, allowed_values); @@ -84,21 +88,24 @@ impl SyscallsParamsConfig { impl Default for SyscallsParamsConfig { fn default() -> Self { + use ParamType::*; + use RegularParamType::*; + let free_start = DEFAULT_INITIAL_SIZE as i64; let free_end = free_start + 5; Self( [ - (ParamType::Length, (0..=0x10000).into()), + (Regular(Length), (0..=0x10000).into()), // There are no rules for memory arrays and pointers as they are chosen // in accordance to memory pages config. - (ParamType::Gas, (0..=250_000_000_000).into()), - (ParamType::Offset, (0..=10).into()), - (ParamType::DurationBlockNumber, (1..=8).into()), - (ParamType::DelayBlockNumber, (0..=4).into()), - (ParamType::Handler, (0..=100).into()), - (ParamType::Free, (free_start..=free_end).into()), - (ParamType::FreeUpperBound, (0..=10).into()), - (ParamType::Version, (1..=1).into()), + (Regular(Gas), (0..=250_000_000_000).into()), + (Regular(Offset), (0..=10).into()), + (Regular(DurationBlockNumber), (1..=8).into()), + (Regular(DelayBlockNumber), (0..=4).into()), + (Regular(Handler), (0..=100).into()), + (Regular(Free), (free_start..=free_end).into()), + (Regular(Version), (1..=1).into()), + (Regular(FreeUpperBound), (0..=10).into()), ] .into_iter() .collect(), diff --git a/utils/wasm-gen/src/config/syscalls/process_errors.rs b/utils/wasm-gen/src/config/syscalls/process_errors.rs new file mode 100644 index 00000000000..5ad2d8c83f2 --- /dev/null +++ b/utils/wasm-gen/src/config/syscalls/process_errors.rs @@ -0,0 +1,84 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Processing syscalls errors config. + +use std::collections::HashSet; + +use crate::InvocableSyscall; + +#[derive(Debug, Clone, Default)] +pub enum ErrorProcessingConfig { + /// Process errors on all the fallible syscalls. + All, + /// Process only errors on provided syscalls. + Whitelist(ErrorProneSyscalls), + /// Process errors on all the syscalls excluding provided. + Blacklist(ErrorProneSyscalls), + /// Don't process syscall errors at all. + #[default] + None, +} + +impl ErrorProcessingConfig { + pub fn error_should_be_processed(&self, syscall: InvocableSyscall) -> bool { + match self { + Self::All => true, + Self::Whitelist(wl) => wl.contains(syscall), + Self::Blacklist(bl) => { + if syscall.returns_error() { + !bl.contains(syscall) + } else { + false + } + } + Self::None => false, + } + } +} + +/// Set of syscalls that return an error. +/// +/// Basically, it's a wrapper over a hash set of [`InvocableSysCall`], +/// that controls types of inserted syscalls. +#[derive(Debug, Clone, Default)] +pub struct ErrorProneSyscalls(HashSet); + +impl ErrorProneSyscalls { + /// Create an empty set of returning error syscalls. + pub fn new() -> Self { + Self(HashSet::new()) + } + + /// Insert an error-prone syscall into the set. + pub fn insert(&mut self, syscall: InvocableSyscall) { + if syscall.returns_error() { + self.0.insert(syscall); + } else { + panic!( + "{syscall_str} is neither fallible, nor returns error value.", + syscall_str = syscall.to_str() + ); + } + } + + /// Check if the `syscall` is in the set. + pub fn contains(&self, syscall: InvocableSyscall) -> bool { + self.0.contains(&syscall) + } +} diff --git a/utils/wasm-gen/src/generator/syscalls.rs b/utils/wasm-gen/src/generator/syscalls.rs index d6cf7979352..8a29acd61cc 100644 --- a/utils/wasm-gen/src/generator/syscalls.rs +++ b/utils/wasm-gen/src/generator/syscalls.rs @@ -43,7 +43,7 @@ pub use imports::*; pub use invocator::*; use gear_wasm_instrument::syscalls::{ - HashType, ParamType, PtrInfo, PtrType, SyscallName, SyscallSignature, + ErrPtr, HashType, Ptr, RegularParamType, SyscallName, SyscallSignature, }; /// Type of invocable syscall. @@ -81,95 +81,94 @@ impl InvocableSyscall { match self { InvocableSyscall::Loose(name) => name.signature(), InvocableSyscall::Precise(name) => match name { - SyscallName::ReservationSend => SyscallSignature::gr([ - // Address of recipient and value (HashWithValue struct) - ParamType::Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - // Pointer to payload - ParamType::Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - // Length of the payload - ParamType::Length, - // Number of blocks to delay the sending for - ParamType::DelayBlockNumber, - // Amount of gas to reserve - ParamType::Gas, - // Duration of the reservation - ParamType::DurationBlockNumber, + SyscallName::ReservationSend => SyscallSignature::gr_fallible(( + [ + // Address of recipient and value (HashWithValue struct) + Ptr::HashWithValue(HashType::ActorId).into(), + // Pointer to payload + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + // Length of the payload + RegularParamType::Length, + // Number of blocks to delay the sending for + RegularParamType::DelayBlockNumber, + // Amount of gas to reserve + RegularParamType::Gas, + // Duration of the reservation + RegularParamType::DurationBlockNumber, + ], // Address of error returned - ParamType::Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - SyscallName::ReservationReply => SyscallSignature::gr([ - // Address of value - ParamType::Ptr(PtrInfo::new_immutable(PtrType::Value)), - // Pointer to payload - ParamType::Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - // Length of the payload - ParamType::Length, - // Amount of gas to reserve - ParamType::Gas, - // Duration of the reservation - ParamType::DurationBlockNumber, + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + SyscallName::ReservationReply => SyscallSignature::gr_fallible(( + [ + // Address of value + Ptr::Value.into(), + // Pointer to payload + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + // Length of the payload + RegularParamType::Length, + // Amount of gas to reserve + RegularParamType::Gas, + // Duration of the reservation + RegularParamType::DurationBlockNumber, + ], // Address of error returned - ParamType::Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - SyscallName::SendCommit => SyscallSignature::gr([ - // Address of recipient and value (HashWithValue struct) - ParamType::Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - // Pointer to payload - ParamType::Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - // Length of the payload - ParamType::Length, - // Number of blocks to delay the sending for - ParamType::DelayBlockNumber, + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + SyscallName::SendCommit => SyscallSignature::gr_fallible(( + [ + // Address of recipient and value (HashWithValue struct) + Ptr::HashWithValue(HashType::ActorId).into(), + // Pointer to payload + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + // Length of the payload + RegularParamType::Length, + // Number of blocks to delay the sending for + RegularParamType::DelayBlockNumber, + ], // Address of error returned, `ErrorCode` here because underlying syscalls have different error types - ParamType::Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - SyscallName::SendCommitWGas => SyscallSignature::gr([ - // Address of recipient and value (HashWithValue struct) - ParamType::Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - // Number of blocks to delay the sending for - ParamType::DelayBlockNumber, - // Amount of gas to reserve - ParamType::Gas, + ErrPtr::ErrorCode, + )), + SyscallName::SendCommitWGas => SyscallSignature::gr_fallible(( + [ + // Address of recipient and value (HashWithValue struct) + Ptr::HashWithValue(HashType::ActorId).into(), + // Number of blocks to delay the sending for + RegularParamType::DelayBlockNumber, + // Amount of gas to reserve + RegularParamType::Gas, + ], // Address of error returned, `ErrorCode` here because underlying syscalls have different error types - ParamType::Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - SyscallName::ReplyDeposit => SyscallSignature::gr([ - // Address of recipient and value (HashWithValue struct). That's needed - // because first `gr_send_input` is invoked and resulting message id is - // used as an input to `gr_reply_deposit`. - ParamType::Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - // An offset defining starting index in the received payload (related to `gr_send_input`). - ParamType::Offset, - // Length of the slice of the received message payload (related to `gr_send_input`). - ParamType::Length, - // Delay (related to `gr_send_input`). - ParamType::DelayBlockNumber, - // Amount of gas deposited for a message id got from `gr_send_input`. - // That's an actual input for `gr_reply_deposit` - ParamType::Gas, + ErrPtr::ErrorCode, + )), + SyscallName::ReplyDeposit => SyscallSignature::gr_fallible(( + [ + // Address of recipient and value (HashWithValue struct). That's needed + // because first `gr_send_input` is invoked and resulting message id is + // used as an input to `gr_reply_deposit`. + Ptr::HashWithValue(HashType::ActorId).into(), + // An offset defining starting index in the received payload (related to `gr_send_input`). + RegularParamType::Offset, + // Length of the slice of the received message payload (related to `gr_send_input`). + RegularParamType::Length, + // Delay (related to `gr_send_input`). + RegularParamType::DelayBlockNumber, + // Amount of gas deposited for a message id got from `gr_send_input`. + // That's an actual input for `gr_reply_deposit` + RegularParamType::Gas, + ], // Error pointer - ParamType::Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), + ErrPtr::ErrorWithHash(HashType::MessageId), + )), _ => unimplemented!(), }, } @@ -236,8 +235,21 @@ impl InvocableSyscall { && !matches!(self, InvocableSyscall::Loose(SyscallName::Exit)) } - // If syscall changes from fallible into infallible or vice versa in future, - // we'll see it by analyzing code coverage stats produced by fuzzer. + /// Checks whether syscall is error-prone either by returning error indicating value + /// or by providing error pointer as a syscall param. + /// + /// There are only 2 syscalls returning error value: `Alloc` and `Free`. + /// + /// If syscall changes from fallible into infallible or vice versa in future, + /// we'll see it by analyzing code coverage stats produced by fuzzer. + pub(crate) fn returns_error(&self) -> bool { + match self { + InvocableSyscall::Loose(syscall) => syscall.returns_error(), + InvocableSyscall::Precise(syscall) => syscall.returns_error(), + } + } + + #[cfg(test)] pub(crate) fn is_fallible(&self) -> bool { match self { InvocableSyscall::Loose(syscall) => syscall.is_fallible(), diff --git a/utils/wasm-gen/src/generator/syscalls/invocator.rs b/utils/wasm-gen/src/generator/syscalls/invocator.rs index 52318d268dd..6f94384477e 100644 --- a/utils/wasm-gen/src/generator/syscalls/invocator.rs +++ b/utils/wasm-gen/src/generator/syscalls/invocator.rs @@ -29,7 +29,10 @@ use crate::{ use arbitrary::{Result, Unstructured}; use gear_wasm_instrument::{ parity_wasm::elements::{BlockType, Instruction, Internal, ValueType}, - syscalls::{ParamType, PtrInfo, PtrType, SyscallName, SyscallSignature}, + syscalls::{ + FallibleSyscallSignature, ParamType, Ptr, RegularParamType, SyscallName, SyscallSignature, + SystemSyscallSignature, + }, }; use gsys::Hash; use std::{ @@ -50,7 +53,7 @@ pub(crate) enum ProcessedSyscallParams { value_type: ValueType, allowed_values: Option, }, - MemoryArraySize, + MemoryArrayLength, MemoryArrayPtr, MemoryPtrValue, } @@ -59,13 +62,16 @@ pub(crate) fn process_syscall_params( params: &[ParamType], params_config: &SyscallsParamsConfig, ) -> Vec { + use ParamType::*; + use RegularParamType::*; + let length_param_indexes = params .iter() .filter_map(|¶m| match param { - ParamType::Ptr(PtrInfo { - ty: PtrType::SizedBufferStart { length_param_idx }, - .. - }) => Some(length_param_idx), + Regular(Pointer( + Ptr::SizedBufferStart { length_param_idx } + | Ptr::MutSizedBufferStart { length_param_idx }, + )) => Some(length_param_idx), _ => None, }) .collect::>(); @@ -73,25 +79,25 @@ pub(crate) fn process_syscall_params( let mut res = Vec::with_capacity(params.len()); for (param_idx, ¶m) in params.iter().enumerate() { let processed_param = match param { - ParamType::Alloc => ProcessedSyscallParams::Alloc { + Regular(Alloc) => ProcessedSyscallParams::Alloc { allowed_values: params_config.get_rule(¶m), }, - ParamType::Length if length_param_indexes.contains(¶m_idx) => { + Regular(Length) if length_param_indexes.contains(¶m_idx) => { // Due to match guard `ParamType::Size` can be processed in two ways: // 1. The function will return `ProcessedSyscallParams::MemoryArraySize` // if this parameter is associated with PtrType::BufferStart { .. }`. // 2. Otherwise, `ProcessedSyscallParams::Value` will be returned from the function. - ProcessedSyscallParams::MemoryArraySize + ProcessedSyscallParams::MemoryArrayLength } - ParamType::Ptr(PtrInfo { - ty: PtrType::SizedBufferStart { .. }, - .. - }) => ProcessedSyscallParams::MemoryArrayPtr, - ParamType::FreeUpperBound => { + Regular(Pointer(Ptr::SizedBufferStart { .. })) => { + ProcessedSyscallParams::MemoryArrayPtr + } + // It's guaranteed that fallible syscall has error pointer as a last param. + Regular(Pointer(_)) | Error(_) => ProcessedSyscallParams::MemoryPtrValue, + Regular(FreeUpperBound) => { let allowed_values = params_config.get_rule(¶m); ProcessedSyscallParams::FreeUpperBound { allowed_values } } - ParamType::Ptr(_) => ProcessedSyscallParams::MemoryPtrValue, _ => ProcessedSyscallParams::Value { value_type: param.into(), allowed_values: params_config.get_rule(¶m), @@ -290,11 +296,8 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { .get(&syscall) .map(|(_, call_indexes_handle)| *call_indexes_handle) .expect("Syscall presented in syscall_imports"); - let instructions = self.build_syscall_invoke_instructions( - syscall, - syscall.into_signature(), - call_indexes_handle, - )?; + let instructions = + self.build_syscall_invoke_instructions(syscall, call_indexes_handle)?; log::trace!( " -- Inserting syscall `{}` into function with index {insert_into_fn} at position {pos}", @@ -324,7 +327,6 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { fn build_syscall_invoke_instructions( &mut self, invocable: InvocableSyscall, - signature: SyscallSignature, call_indexes_handle: CallIndexesHandle, ) -> Result { log::trace!( @@ -339,33 +341,26 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { invocable.to_str() ); - self.build_call_with_destination( - invocable, - signature, - call_indexes_handle, - argument_index, - ) + self.build_call_with_destination(invocable, call_indexes_handle, argument_index) } else { log::trace!( " -- Building call for a common syscall `{}`", invocable.to_str() ); - self.build_call(invocable, signature, call_indexes_handle) + self.build_call(invocable, call_indexes_handle) } } fn build_call_with_destination( &mut self, invocable: InvocableSyscall, - signature: SyscallSignature, call_indexes_handle: CallIndexesHandle, destination_arg_idx: usize, ) -> Result> { // The value for the destination param is chosen from config. // It's either the result of `gr_source`, some existing address (set in the data section) or a completely random value. - let mut original_instructions = - self.build_call(invocable, signature, call_indexes_handle)?; + let mut original_instructions = self.build_call(invocable, call_indexes_handle)?; let destination_instructions = if self.config.syscall_destination().is_source() { log::trace!(" --- Syscall destination is result of `gr_source`"); @@ -442,10 +437,10 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { fn build_call( &mut self, invocable: InvocableSyscall, - signature: SyscallSignature, call_indexes_handle: CallIndexesHandle, ) -> Result> { - let param_setters = self.build_param_setters(&signature.params)?; + let signature = invocable.into_signature(); + let param_setters = self.build_param_setters(signature.params())?; let mut instructions: Vec<_> = param_setters .iter() .cloned() @@ -454,17 +449,26 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { instructions.push(Instruction::Call(call_indexes_handle as u32)); - let insert_error_processing = self + let process_error = self .config .error_processing_config() - .error_should_be_processed(&invocable); + .error_should_be_processed(invocable); - let mut result_processing = if !insert_error_processing { - Self::build_result_processing_ignored(signature) - } else if invocable.is_fallible() { - Self::build_result_processing_fallible(signature, ¶m_setters) - } else { - Self::build_result_processing_infallible(signature) + let mut result_processing = match signature { + SyscallSignature::Infallible(_) => { + // It's guaranteed here that infallible has no errors to process + // as it has not mut err pointers or error indicating values returned. + Vec::new() + } + signature @ (SyscallSignature::Fallible(_) | SyscallSignature::System(_)) => { + // It's guaranteed by definition that these variants return an error either by returning + // error indicating value or by having err mut pointer in params. + if process_error { + Self::build_error_processing(signature, param_setters) + } else { + Self::build_error_processing_ignored(signature) + } + } }; instructions.append(&mut result_processing); @@ -525,11 +529,11 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { ParamSetter::new_i64(self.unstructured.arbitrary()?) }; - log::trace!(" ---- Pointer value - {}", setter.get_value()); + log::trace!(" ---- Value - {}", setter.get_value()); setters.push(setter); } - ProcessedSyscallParams::MemoryArraySize => { + ProcessedSyscallParams::MemoryArrayLength => { let length; let upper_limit = mem_size.saturating_sub(1) as i32; @@ -573,21 +577,21 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { setters.push(setter); } ProcessedSyscallParams::FreeUpperBound { allowed_values } => { + // This is the case only for `free_range` syscall. let previous_param = setters .last() - .expect("expected Free parameter before FreeUpperBound") + .expect("free_range syscall has at least 2 params") .as_i32() .expect("referenced param should evaluate to I32Const"); - let size = allowed_values + let delta = allowed_values .expect("allowed_values should be set for FreeUpperBound") .get_i32(self.unstructured)?; + let param = previous_param.saturating_add(delta); - let value = previous_param.saturating_add(size); + log::trace!(" ---- Free upper bound - {param}, and delta - {delta}"); - log::trace!(" ---- Add value - {}", value); - - setters.push(ParamSetter::new_i32(value)) + setters.push(ParamSetter::new_i32(param)) } } } @@ -602,15 +606,27 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { Ok(setters) } - fn build_result_processing_ignored(signature: SyscallSignature) -> Vec { - iter::repeat(Instruction::Drop) - .take(signature.results.len()) - .collect() + fn build_error_processing( + signature: SyscallSignature, + param_setters: Vec, + ) -> Vec + where + 'a: 'b, + { + match signature { + SyscallSignature::Fallible(fallible) => { + Self::build_fallible_syscall_error_processing(fallible, param_setters) + } + SyscallSignature::System(system) => Self::build_system_syscall_error_processing(system), + SyscallSignature::Infallible(_) => unreachable!( + "Invalid implementation. This function is called only for returning errors syscall" + ), + } } - fn build_result_processing_fallible( - signature: SyscallSignature, - param_setters: &[ParamSetter], + fn build_fallible_syscall_error_processing( + fallible_signature: FallibleSyscallSignature, + param_setters: Vec, ) -> Vec { // TODO: #3129. // Assume here that: @@ -618,60 +634,48 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { // 2. All the errors contain `ErrorCode` in the start of memory where pointer points. static_assertions::assert_eq_size!(gsys::ErrorCode, u32); - assert_eq!(gsys::ErrorCode::default(), 0); - - let params = signature.params; - assert!(matches!( - params - .last() - .expect("The last argument of fallible syscall must be pointer to error code"), - ParamType::Ptr(_) - )); - assert_eq!(params.len(), param_setters.len()); - - if let Some(ptr) = param_setters + let no_error_val = gsys::ErrorCode::default() as i32; + + assert_eq!( + fallible_signature.params().len(), + param_setters.len(), + "ParamsSetter is inconsistent with syscall params." + ); + let res_ptr = param_setters .last() .expect("At least one argument in fallible syscall") .as_i32() - { - vec![ - Instruction::I32Const(ptr), - Instruction::I32Load(2, 0), - Instruction::I32Const(0), - Instruction::I32Ne, - Instruction::If(BlockType::NoResult), - Instruction::Unreachable, - Instruction::End, - ] - } else { - panic!("Incorrect last parameter type: expected pointer"); - } - } + .expect("Incorrect last parameter type: expected i32 pointer"); - fn build_result_processing_infallible(signature: SyscallSignature) -> Vec { - // TODO: #3129 - // For now we don't check anywhere that `alloc` and `free` return - // error codes as described here. Also we don't assert that only `alloc` and `free` - // will have their first arguments equal to `ParamType::Alloc` and `ParamType::Free`. - let results_len = signature.results.len(); - - if results_len == 0 { - return vec![]; - } + vec![ + Instruction::I32Const(res_ptr), + Instruction::I32Load(2, 0), + Instruction::I32Const(no_error_val), + Instruction::I32Ne, + Instruction::If(BlockType::NoResult), + Instruction::Unreachable, + Instruction::End, + ] + } - assert_eq!(results_len, 1); + fn build_system_syscall_error_processing( + system_signature: SystemSyscallSignature, + ) -> Vec { + // That's basically those syscalls, that doesn't have an error pointer, + // but return value indicating error. These are currently `Alloc`, `Free` and `FreeRange`. + assert_eq!(system_signature.results().len(), 1); - let error_code = match signature.params[0] { - ParamType::Alloc => { + let error_code = match system_signature.params()[0] { + ParamType::Regular(RegularParamType::Alloc) => { // Alloc syscall: returns u32::MAX (= -1i32) in case of error. -1 } - ParamType::Free | ParamType::FreeUpperBound => { - // free/free_range syscall: returns 1 in case of error. + ParamType::Regular(RegularParamType::Free | RegularParamType::FreeUpperBound) => { + // Free/FreeRange syscall: returns 1 in case of error. 1 } _ => { - unimplemented!("Only alloc and free/free_range are supported for now") + unimplemented!("Only alloc, free and free_range are supported for now") } }; @@ -684,6 +688,18 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { ] } + fn build_error_processing_ignored(signature: SyscallSignature) -> Vec { + match signature { + SyscallSignature::System(system) => iter::repeat(Instruction::Drop) + .take(system.results().len()) + .collect(), + SyscallSignature::Fallible(_) => Vec::new(), + SyscallSignature::Infallible(_) => unreachable!( + "Invalid implementation. This function is called only for returning errors syscall" + ), + } + } + fn resolves_calls_indexes(&mut self) { log::trace!("Resolving calls indexes"); diff --git a/utils/wasm-gen/src/tests.rs b/utils/wasm-gen/src/tests.rs index 8db5c816e40..567133a208a 100644 --- a/utils/wasm-gen/src/tests.rs +++ b/utils/wasm-gen/src/tests.rs @@ -302,7 +302,7 @@ fn precise_syscalls_works() { injection_types.set(syscall, INJECTED_SYSCALLS, INJECTED_SYSCALLS); let mut param_config = SyscallsParamsConfig::default(); - param_config.add_rule(ParamType::Gas, (0..=0).into()); + param_config.add_rule(ParamType::Regular(RegularParamType::Gas), (0..=0).into()); // Assert that syscalls results will be processed. let termination_reason = execute_wasm_with_custom_configs( diff --git a/utils/wasm-instrument/src/syscalls.rs b/utils/wasm-instrument/src/syscalls.rs index 3a3dd2a7cbe..874cf8869a4 100644 --- a/utils/wasm-instrument/src/syscalls.rs +++ b/utils/wasm-instrument/src/syscalls.rs @@ -19,8 +19,10 @@ //! Gear syscalls for smart contracts execution signatures. use crate::parity_wasm::elements::{FunctionType, ValueType}; -use alloc::{collections::BTreeSet, vec::Vec}; +use alloc::{borrow::ToOwned, collections::BTreeSet, vec::Vec}; +use core::iter; use enum_iterator::{self, Sequence}; +pub use pointers::*; /// All available syscalls. /// @@ -240,348 +242,293 @@ impl SyscallName { /// Returns signature for syscall by name. pub fn signature(self) -> SyscallSignature { - use ParamType::*; - use ValueType::I32; + use RegularParamType::*; + match self { - Self::Alloc => SyscallSignature::system([Alloc], [I32]), - Self::Free => SyscallSignature::system([Free], [I32]), - Self::FreeRange => SyscallSignature::system([Free, FreeUpperBound], [I32]), - Self::Debug => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { + Self::Alloc => SyscallSignature::system(([Alloc], [ValueType::I32])), + Self::Free => SyscallSignature::system(([Free], [ValueType::I32])), + Self::FreeRange => SyscallSignature::system(([Free, FreeUpperBound], [ValueType::I32])), + Self::Debug => SyscallSignature::gr_infallible([ + Ptr::SizedBufferStart { length_param_idx: 1, - })), + } + .into(), Length, ]), - Self::Panic => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { + Self::Panic => SyscallSignature::gr_infallible([ + Ptr::SizedBufferStart { length_param_idx: 1, - })), + } + .into(), Length, ]), - Self::OomPanic => SyscallSignature::gr([]), - Self::BlockHeight => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::BlockNumber))]) - } + Self::OomPanic => SyscallSignature::gr_infallible([]), + Self::BlockHeight => SyscallSignature::gr_infallible([Ptr::MutBlockNumber.into()]), Self::BlockTimestamp => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::BlockTimestamp))]) + SyscallSignature::gr_infallible([Ptr::MutBlockTimestamp.into()]) } - Self::Exit => SyscallSignature::gr([Ptr(PtrInfo::new_immutable(PtrType::Hash( - HashType::ActorId, - )))]), - Self::GasAvailable => SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::Gas))]), - Self::PayProgramRent => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithBlockNumberAndValue)), - ]), + Self::Exit => SyscallSignature::gr_infallible([Ptr::Hash(HashType::ActorId).into()]), + Self::GasAvailable => SyscallSignature::gr_infallible([Ptr::MutGas.into()]), + Self::PayProgramRent => SyscallSignature::gr_fallible(( + [Ptr::HashWithValue(HashType::ActorId).into()], + ErrPtr::ErrorWithBlockNumberAndValue, + )), Self::ProgramId => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::Hash(HashType::ActorId)))]) + SyscallSignature::gr_infallible([Ptr::MutHash(HashType::ActorId).into()]) } - Self::Leave => SyscallSignature::gr([]), - Self::ValueAvailable => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::Value))]) + Self::Leave => SyscallSignature::gr_infallible([]), + Self::ValueAvailable => SyscallSignature::gr_infallible([Ptr::MutValue.into()]), + Self::Wait => SyscallSignature::gr_infallible([]), + Self::WaitUpTo => SyscallSignature::gr_infallible([DurationBlockNumber]), + Self::WaitFor => SyscallSignature::gr_infallible([DurationBlockNumber]), + Self::Wake => SyscallSignature::gr_fallible(( + [Ptr::Hash(HashType::MessageId).into(), DelayBlockNumber], + ErrPtr::ErrorCode, + )), + Self::ReplyCode => SyscallSignature::gr_fallible(ErrPtr::ErrorWithReplyCode), + Self::SignalCode => SyscallSignature::gr_fallible(ErrPtr::ErrorWithSignalCode), + Self::MessageId => { + SyscallSignature::gr_infallible([Ptr::MutHash(HashType::MessageId).into()]) } - Self::Wait => SyscallSignature::gr([]), - Self::WaitUpTo => SyscallSignature::gr([DurationBlockNumber]), - Self::WaitFor => SyscallSignature::gr([DurationBlockNumber]), - Self::Wake => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::Hash(HashType::MessageId))), - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - Self::ReplyCode => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::ErrorWithReplyCode))]) + Self::EnvVars => SyscallSignature::gr_infallible([Version, Ptr::MutBufferStart.into()]), + Self::Read => SyscallSignature::gr_fallible(( + [ + Offset, + Length, + Ptr::MutSizedBufferStart { + length_param_idx: 1, + } + .into(), + ], + ErrPtr::ErrorCode, + )), + Self::Reply => SyscallSignature::gr_fallible(( + [ + Ptr::SizedBufferStart { + length_param_idx: 1, + } + .into(), + Length, + Ptr::Value.into(), + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReplyInput => SyscallSignature::gr_fallible(( + [Offset, Length, Ptr::Value.into()], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReplyWGas => SyscallSignature::gr_fallible(( + [ + Ptr::SizedBufferStart { + length_param_idx: 1, + } + .into(), + Length, + Gas, + Ptr::Value.into(), + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReplyInputWGas => SyscallSignature::gr_fallible(( + [Offset, Length, Gas, Ptr::Value.into()], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReplyCommit => SyscallSignature::gr_fallible(( + [Ptr::Value.into()], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReplyCommitWGas => SyscallSignature::gr_fallible(( + [Gas, Ptr::Value.into()], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReservationReply => SyscallSignature::gr_fallible(( + [ + Ptr::HashWithValue(HashType::ReservationId).into(), + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + Length, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReservationReplyCommit => SyscallSignature::gr_fallible(( + [Ptr::HashWithValue(HashType::ReservationId).into()], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReplyPush => SyscallSignature::gr_fallible(( + [ + Ptr::SizedBufferStart { + length_param_idx: 1, + } + .into(), + Length, + ], + ErrPtr::ErrorCode, + )), + Self::ReplyPushInput => { + SyscallSignature::gr_fallible(([Offset, Length], ErrPtr::ErrorCode)) } - Self::SignalCode => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::ErrorWithSignalCode))]) + Self::ReplyTo => { + SyscallSignature::gr_fallible(ErrPtr::ErrorWithHash(HashType::MessageId)) } - Self::MessageId => SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::Hash( - HashType::MessageId, - )))]), - Self::EnvVars => { - SyscallSignature::gr([Version, Ptr(PtrInfo::new_mutable(PtrType::BufferStart))]) + Self::SignalFrom => { + SyscallSignature::gr_fallible(ErrPtr::ErrorWithHash(HashType::MessageId)) } - Self::Read => SyscallSignature::gr([ - Offset, - Length, - Ptr(PtrInfo::new_mutable(PtrType::SizedBufferStart { - length_param_idx: 1, - })), - Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - Self::Reply => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 1, - })), - Length, - Ptr(PtrInfo::new_immutable(PtrType::Value)), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReplyInput => SyscallSignature::gr([ - Offset, - Length, - Ptr(PtrInfo::new_immutable(PtrType::Value)), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReplyWGas => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 1, - })), - Length, - Gas, - Ptr(PtrInfo::new_immutable(PtrType::Value)), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReplyInputWGas => SyscallSignature::gr([ - Offset, - Length, - Gas, - Ptr(PtrInfo::new_immutable(PtrType::Value)), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReplyCommit => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::Value)), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReplyCommitWGas => SyscallSignature::gr([ - Gas, - Ptr(PtrInfo::new_immutable(PtrType::Value)), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReservationReply => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ReservationId, - ))), - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - Length, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReservationReplyCommit => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ReservationId, - ))), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReplyPush => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 1, - })), - Length, - Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - Self::ReplyPushInput => SyscallSignature::gr([ - Offset, - Length, - Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - Self::ReplyTo => SyscallSignature::gr([Ptr(PtrInfo::new_mutable( - PtrType::ErrorWithHash(HashType::MessageId), - ))]), - Self::SignalFrom => SyscallSignature::gr([Ptr(PtrInfo::new_mutable( - PtrType::ErrorWithHash(HashType::MessageId), - ))]), - Self::Send => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - Length, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::SendInput => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - Offset, - Length, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::SendWGas => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - Length, - Gas, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::SendInputWGas => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - Offset, - Length, - Gas, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::SendCommit => SyscallSignature::gr([ - Handler, - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::SendCommitWGas => SyscallSignature::gr([ - Handler, - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::ActorId, - ))), - Gas, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::SendInit => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHandle))]) + Self::Send => SyscallSignature::gr_fallible(( + [ + Ptr::HashWithValue(HashType::ActorId).into(), + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + Length, + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::SendInput => SyscallSignature::gr_fallible(( + [ + Ptr::HashWithValue(HashType::ActorId).into(), + Offset, + Length, + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::SendWGas => SyscallSignature::gr_fallible(( + [ + Ptr::HashWithValue(HashType::ActorId).into(), + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + Length, + Gas, + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::SendInputWGas => SyscallSignature::gr_fallible(( + [ + Ptr::HashWithValue(HashType::ActorId).into(), + Offset, + Length, + Gas, + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::SendCommit => SyscallSignature::gr_fallible(( + [ + Handler, + Ptr::HashWithValue(HashType::ActorId).into(), + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::SendCommitWGas => SyscallSignature::gr_fallible(( + [ + Handler, + Ptr::HashWithValue(HashType::ActorId).into(), + Gas, + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::SendInit => SyscallSignature::gr_fallible(ErrPtr::ErrorWithHandle), + Self::SendPush => SyscallSignature::gr_fallible(( + [ + Handler, + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + Length, + ], + ErrPtr::ErrorCode, + )), + Self::SendPushInput => { + SyscallSignature::gr_fallible(([Handler, Offset, Length], ErrPtr::ErrorCode)) } - Self::SendPush => SyscallSignature::gr([ - Handler, - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - Length, - Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - Self::SendPushInput => SyscallSignature::gr([ - Handler, - Offset, - Length, - Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - Self::ReservationSend => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::TwoHashesWithValue( - HashType::ReservationId, - HashType::ActorId, - ))), - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - Length, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::ReservationSendCommit => SyscallSignature::gr([ - Handler, - Ptr(PtrInfo::new_immutable(PtrType::TwoHashesWithValue( - HashType::ReservationId, - HashType::ActorId, - ))), - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::MessageId, - ))), - ]), - Self::Size => SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::Length))]), + Self::ReservationSend => SyscallSignature::gr_fallible(( + [ + Ptr::TwoHashesWithValue(HashType::ReservationId, HashType::ActorId).into(), + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + Length, + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::ReservationSendCommit => SyscallSignature::gr_fallible(( + [ + Handler, + Ptr::TwoHashesWithValue(HashType::ReservationId, HashType::ActorId).into(), + DelayBlockNumber, + ], + ErrPtr::ErrorWithHash(HashType::MessageId), + )), + Self::Size => SyscallSignature::gr_infallible([Ptr::MutLength.into()]), Self::Source => { - SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::Hash(HashType::ActorId)))]) + SyscallSignature::gr_infallible([Ptr::MutHash(HashType::ActorId).into()]) } - Self::Value => SyscallSignature::gr([Ptr(PtrInfo::new_mutable(PtrType::Value))]), - Self::CreateProgram => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::CodeId, - ))), - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - Length, - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 4, - })), - Length, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithTwoHashes( - HashType::MessageId, - HashType::ActorId, - ))), + Self::Value => SyscallSignature::gr_infallible([Ptr::MutValue.into()]), + Self::CreateProgram => SyscallSignature::gr_fallible(( + [ + Ptr::HashWithValue(HashType::CodeId).into(), + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + Length, + Ptr::SizedBufferStart { + length_param_idx: 4, + } + .into(), + Length, + DelayBlockNumber, + ], + ErrPtr::ErrorWithTwoHashes(HashType::MessageId, HashType::ActorId), + )), + Self::CreateProgramWGas => SyscallSignature::gr_fallible(( + [ + Ptr::HashWithValue(HashType::CodeId).into(), + Ptr::SizedBufferStart { + length_param_idx: 2, + } + .into(), + Length, + Ptr::SizedBufferStart { + length_param_idx: 4, + } + .into(), + Length, + Gas, + DelayBlockNumber, + ], + ErrPtr::ErrorWithTwoHashes(HashType::MessageId, HashType::ActorId), + )), + Self::ReplyDeposit => SyscallSignature::gr_fallible(( + [Ptr::Hash(HashType::MessageId).into(), Gas], + ErrPtr::ErrorCode, + )), + Self::ReserveGas => SyscallSignature::gr_fallible(( + [Gas, DurationBlockNumber], + ErrPtr::ErrorWithHash(HashType::ReservationId), + )), + Self::UnreserveGas => SyscallSignature::gr_fallible(( + [Ptr::Hash(HashType::ReservationId).into()], + ErrPtr::ErrorWithGas, + )), + Self::SystemReserveGas => SyscallSignature::gr_fallible(([Gas], ErrPtr::ErrorCode)), + Self::Random => SyscallSignature::gr_infallible([ + Ptr::Hash(HashType::SubjectId).into(), + Ptr::MutBlockNumberWithHash(HashType::SubjectId).into(), ]), - Self::CreateProgramWGas => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::HashWithValue( - HashType::CodeId, - ))), - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 2, - })), - Length, - Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { - length_param_idx: 4, - })), - Length, - Gas, - DelayBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithTwoHashes( - HashType::MessageId, - HashType::ActorId, - ))), - ]), - Self::ReplyDeposit => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::Hash(HashType::MessageId))), - Gas, - Ptr(PtrInfo::new_mutable(PtrType::ErrorCode)), - ]), - Self::ReserveGas => SyscallSignature::gr([ - Gas, - DurationBlockNumber, - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithHash( - HashType::ReservationId, - ))), - ]), - Self::UnreserveGas => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::Hash( - HashType::ReservationId, - ))), - Ptr(PtrInfo::new_mutable(PtrType::ErrorWithGas)), - ]), - Self::SystemReserveGas => { - SyscallSignature::gr([Gas, Ptr(PtrInfo::new_mutable(PtrType::ErrorCode))]) - } - Self::Random => SyscallSignature::gr([ - Ptr(PtrInfo::new_immutable(PtrType::Hash(HashType::SubjectId))), - Ptr(PtrInfo::new_mutable(PtrType::BlockNumberWithHash( - HashType::SubjectId, - ))), - ]), - other => panic!("Unknown syscall: '{:?}'", other), + Self::OutOfGas => unimplemented!("Unsupported syscall signature for out_of_gas"), } } @@ -598,13 +545,26 @@ impl SyscallName { }) } + /// Checks whether the syscall returns error either by writing to input error pointer + /// or by returning value indicating an error. + /// + /// There are only 3 syscalls returning error value: `Alloc`, `Free` & `FreeRange`. + pub fn returns_error(self) -> bool { + let signature = self.signature(); + + match &signature { + SyscallSignature::Fallible(_) | SyscallSignature::System(_) => true, + SyscallSignature::Infallible(_) => false, + } + } + /// Checks whether the syscall is fallible. /// - /// Literally checks whether syscall contains mutable error pointer. - pub fn is_fallible(&self) -> bool { - self.signature().params.into_iter().any( - |param| matches!(param, ParamType::Ptr(PtrInfo { mutable: true, ty }) if ty.is_error()), - ) + /// ### Note: + /// This differs from `SysCallName::returns_error` as fallible syscalls + /// are those last param of which is a mutable error pointer. + pub fn is_fallible(self) -> bool { + self.signature().is_fallible() } } @@ -614,35 +574,23 @@ impl SyscallName { /// belongs to. See [`PtrInfo`] and [`PtrType`] for more details. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ParamType { - Length, // i32 buffers size in memory - Ptr(PtrInfo), // i32 pointer + Regular(RegularParamType), + Error(ErrPtr), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum RegularParamType { + Length, // i32 buffers length + Pointer(Ptr), // i32 non-error pointer Gas, // i64 gas amount Offset, // i32 offset in the input buffer (message payload) - MessagePosition, // i32 message position DurationBlockNumber, // i32 duration in blocks DelayBlockNumber, // i32 delay in blocks Handler, // i32 handler number - Alloc, // i32 alloc pages - Free, // i32 free page - // i32 free upper bound for use with free_range. Should be placed after Free in fn signature - FreeUpperBound, - Version, // i32 version number of exec settings -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct PtrInfo { - pub mutable: bool, - pub ty: PtrType, -} - -impl PtrInfo { - pub fn new_immutable(ty: PtrType) -> PtrInfo { - PtrInfo { mutable: false, ty } - } - - pub fn new_mutable(ty: PtrType) -> PtrInfo { - PtrInfo { mutable: true, ty } - } + Alloc, // i32 pages to alloc + Free, // i32 page number to free + FreeUpperBound, // i32 free upper bound for use with free_range + Version, // i32 version number of exec settings } /// Hash type. @@ -658,104 +606,252 @@ pub enum HashType { SubjectId, } -/// Pointer type. -/// -/// Used to distinguish between different pointer types in the syscall signatures. -/// Basically it responds to different types from `gsys`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum PtrType { - BlockNumber, - BlockTimestamp, - SizedBufferStart { length_param_idx: usize }, - BufferStart, - Hash(HashType), - Gas, - Length, - Value, - - BlockNumberWithHash(HashType), - HashWithValue(HashType), - TwoHashes(HashType, HashType), - TwoHashesWithValue(HashType, HashType), +impl From for ValueType { + fn from(value: ParamType) -> Self { + use RegularParamType::*; - ErrorCode, + match value { + ParamType::Regular(regular_ptr) => match regular_ptr { + Length | Pointer(_) | Offset | DurationBlockNumber | DelayBlockNumber | Handler + | Alloc | Free | FreeUpperBound | Version => ValueType::I32, + Gas => ValueType::I64, + }, + ParamType::Error(_) => ValueType::I32, + } + } +} - ErrorWithReplyCode, - ErrorWithSignalCode, - ErrorWithGas, - ErrorWithHandle, - ErrorWithHash(HashType), - ErrorWithTwoHashes(HashType, HashType), - ErrorWithBlockNumberAndValue, +/// Syscall signature. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum SyscallSignature { + Fallible(FallibleSyscallSignature), + Infallible(InfallibleSyscallSignature), + System(SystemSyscallSignature), } -impl PtrType { - pub fn is_error(self) -> bool { - use PtrType::*; +impl SyscallSignature { + pub fn gr_fallible(fallible: impl Into) -> Self { + Self::Fallible(fallible.into()) + } + pub fn gr_infallible(infallible: impl Into) -> Self { + Self::Infallible(infallible.into()) + } + + pub fn system(system: impl Into) -> Self { + Self::System(system.into()) + } + + pub fn params(&self) -> &[ParamType] { match self { - ErrorCode - | ErrorWithReplyCode - | ErrorWithSignalCode - | ErrorWithGas - | ErrorWithHandle - | ErrorWithHash(_) - | ErrorWithTwoHashes(_, _) - | ErrorWithBlockNumberAndValue => true, - BlockNumber - | BlockTimestamp - | SizedBufferStart { .. } - | BufferStart - | Hash(_) - | Gas - | Length - | Value - | BlockNumberWithHash(_) - | HashWithValue(_) - | TwoHashes(_, _) - | TwoHashesWithValue(_, _) => false, + SyscallSignature::Fallible(fallible) => &fallible.0, + SyscallSignature::Infallible(infallible) => &infallible.0, + SyscallSignature::System(system) => &system.params, } } -} -impl From for ValueType { - fn from(value: ParamType) -> Self { - match value { - ParamType::Gas => ValueType::I64, - _ => ValueType::I32, + pub fn results(&self) -> Option<&[ValueType]> { + match self { + SyscallSignature::Fallible(_) | SyscallSignature::Infallible(_) => None, + SyscallSignature::System(system) => Some(&system.results), } } + + pub fn func_type(&self) -> FunctionType { + let (params, results) = match self { + SyscallSignature::Fallible(fallible) => (fallible.params(), Vec::new()), + SyscallSignature::Infallible(infallible) => (infallible.params(), Vec::new()), + SyscallSignature::System(system) => (system.params(), system.results().to_owned()), + }; + + FunctionType::new(params.iter().copied().map(Into::into).collect(), results) + } + + pub fn is_fallible(&self) -> bool { + matches!(self, SyscallSignature::Fallible(_)) + } + + pub fn is_infallible(&self) -> bool { + matches!(self, SyscallSignature::Infallible(_)) + } + + pub fn is_system(&self) -> bool { + matches!(self, SyscallSignature::System(_)) + } } -/// Syscall signature. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct SyscallSignature { - pub params: Vec, - pub results: Vec, +pub struct FallibleSyscallSignature(Vec); + +impl FallibleSyscallSignature { + pub fn new(params: [RegularParamType; N], err_ptr: ErrPtr) -> Self { + let params = params + .into_iter() + .map(ParamType::Regular) + .chain(iter::once(err_ptr.into())) + .collect(); + + FallibleSyscallSignature(params) + } + + pub fn params(&self) -> &[ParamType] { + &self.0 + } } -impl SyscallSignature { - pub fn gr(params: [ParamType; N]) -> Self { - Self { - params: params.to_vec(), - results: Default::default(), - } +impl From<([RegularParamType; N], ErrPtr)> for FallibleSyscallSignature { + fn from((params, err_ptr): ([RegularParamType; N], ErrPtr)) -> Self { + FallibleSyscallSignature::new(params, err_ptr) + } +} + +impl From for FallibleSyscallSignature { + fn from(err_ptr: ErrPtr) -> Self { + FallibleSyscallSignature::new([], err_ptr) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct InfallibleSyscallSignature(Vec); + +impl InfallibleSyscallSignature { + pub fn new(params: [RegularParamType; N]) -> Self { + InfallibleSyscallSignature(params.into_iter().map(ParamType::Regular).collect()) } - pub fn system( - params: [ParamType; N], + pub fn params(&self) -> &[ParamType] { + &self.0 + } +} + +impl From<[RegularParamType; N]> for InfallibleSyscallSignature { + fn from(params: [RegularParamType; N]) -> Self { + InfallibleSyscallSignature::new(params) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SystemSyscallSignature { + params: Vec, + results: Vec, +} + +impl SystemSyscallSignature { + pub fn new( + params: [RegularParamType; N], results: [ValueType; M], ) -> Self { - Self { - params: params.to_vec(), + SystemSyscallSignature { + params: params.into_iter().map(ParamType::Regular).collect(), results: results.to_vec(), } } - pub fn func_type(&self) -> FunctionType { - FunctionType::new( - self.params.iter().copied().map(Into::into).collect(), - self.results.clone(), - ) + pub fn params(&self) -> &[ParamType] { + &self.params + } + + pub fn results(&self) -> &[ValueType] { + &self.results + } +} + +impl From<([RegularParamType; N], [ValueType; M])> + for SystemSyscallSignature +{ + fn from((params, results): ([RegularParamType; N], [ValueType; M])) -> Self { + SystemSyscallSignature::new(params, results) + } +} + +// TODO: issue write macros +mod pointers { + use super::{HashType, ParamType, RegularParamType}; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub enum Ptr { + // Const ptrs. + BlockNumber, + BlockTimestamp, + SizedBufferStart { length_param_idx: usize }, + BufferStart, + Hash(HashType), + Gas, + Length, + Value, + BlockNumberWithHash(HashType), + HashWithValue(HashType), + TwoHashes(HashType, HashType), + TwoHashesWithValue(HashType, HashType), + // Mutable ptrs. + MutBlockNumber, + MutBlockTimestamp, + MutSizedBufferStart { length_param_idx: usize }, + MutBufferStart, + MutHash(HashType), + MutGas, + MutLength, + MutValue, + MutBlockNumberWithHash(HashType), + MutHashWithValue(HashType), + MutTwoHashes(HashType, HashType), + MutTwoHashesWithValue(HashType, HashType), + } + + impl Ptr { + pub fn is_mutable(self) -> bool { + use Ptr::*; + + match self { + BlockNumber + | BlockTimestamp + | SizedBufferStart { .. } + | BufferStart + | Hash(_) + | Gas + | Length + | Value + | BlockNumberWithHash(_) + | HashWithValue(_) + | TwoHashes(_, _) + | TwoHashesWithValue(_, _) => false, + MutBlockNumber + | MutBlockTimestamp + | MutSizedBufferStart { .. } + | MutBufferStart + | MutHash(_) + | MutGas + | MutLength + | MutValue + | MutBlockNumberWithHash(_) + | MutHashWithValue(_) + | MutTwoHashes(_, _) + | MutTwoHashesWithValue(_, _) => true, + } + } + } + + impl From for RegularParamType { + fn from(ptr: Ptr) -> RegularParamType { + RegularParamType::Pointer(ptr) + } + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub enum ErrPtr { + ErrorCode, + ErrorWithReplyCode, + ErrorWithSignalCode, + ErrorWithGas, + ErrorWithHandle, + ErrorWithHash(HashType), + ErrorWithTwoHashes(HashType, HashType), + ErrorWithBlockNumberAndValue, + } + + impl From for ParamType { + fn from(err_ptr: ErrPtr) -> ParamType { + ParamType::Error(err_ptr) + } } } diff --git a/utils/wasm-instrument/src/tests.rs b/utils/wasm-instrument/src/tests.rs index de674939ed2..e654144e439 100644 --- a/utils/wasm-instrument/src/tests.rs +++ b/utils/wasm-instrument/src/tests.rs @@ -19,7 +19,7 @@ use super::*; use crate::{ rules::CustomConstantCostRules, - syscalls::{ParamType, PtrInfo, PtrType, SyscallName}, + syscalls::{ParamType::*, Ptr, RegularParamType::*, SyscallName}, }; use alloc::format; use elements::Instruction::*; @@ -624,35 +624,33 @@ fn check_memory_array_pointers_definition_correctness() { for syscall in syscalls { let signature = syscall.signature(); let size_param_indexes = signature - .params + .params() .iter() .filter_map(|param_ty| match param_ty { - ParamType::Ptr(PtrInfo { - ty: PtrType::SizedBufferStart { length_param_idx }, - .. - }) => Some(*length_param_idx), + Regular(Pointer(Ptr::SizedBufferStart { length_param_idx })) => { + Some(*length_param_idx) + } _ => None, }); for idx in size_param_indexes { - assert_eq!(signature.params.get(idx), Some(&ParamType::Length)); + assert_eq!(signature.params().get(idx), Some(&Regular(Length))); } } } // Basically checks that mutable error pointer is always last in every fallible syscall params set. +// WARNING: this test must never fail, unless a huge redesign in syscalls signatures has occurred. #[test] fn check_syscall_err_ptr_position() { for syscall in SyscallName::instrumentable() { if syscall.is_fallible() { let signature = syscall.signature(); let err_ptr = signature - .params + .params() .last() .expect("fallible syscall has at least err ptr"); - assert!( - matches!(err_ptr, ParamType::Ptr(PtrInfo { mutable: true, ty }) if ty.is_error()) - ); + assert!(matches!(err_ptr, Error(_))); } } }