Skip to content

Commit

Permalink
feat(wasm-gen): avoid infinite wait-wake for loose syscalls (#3615)
Browse files Browse the repository at this point in the history
  • Loading branch information
StackOverflowExcept1on authored and breathx committed Feb 27, 2024
1 parent 6c750ca commit b7741a5
Show file tree
Hide file tree
Showing 13 changed files with 269 additions and 96 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions utils/wasm-gen/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
//! There's a pre-defined one - [`StandardGearWasmConfigsBundle`], usage of which will result
//! in generation of valid (always) gear-wasm module.
use std::num::NonZeroU32;

mod generator;
mod module;
mod syscalls;
Expand Down Expand Up @@ -131,6 +133,11 @@ impl ConfigsBundle for (GearWasmGeneratorConfig, SelectableParams) {
pub struct StandardGearWasmConfigsBundle {
/// Externalities to be logged.
pub log_info: Option<String>,
/// Probability of wait syscalls.
///
/// For example, if this parameter is 4, wait syscalls will be invoked
/// with probability 1/4.
pub waiting_probability: Option<NonZeroU32>,
/// Flag which signals whether recursions must be removed.
pub remove_recursion: bool,
/// If the limit is set to `Some(_)`, programs will try to stop execution
Expand All @@ -156,6 +163,7 @@ impl Default for StandardGearWasmConfigsBundle {
fn default() -> Self {
Self {
log_info: Some("StandardGearWasmConfigsBundle".into()),
waiting_probability: NonZeroU32::new(4),
remove_recursion: false,
critical_gas_limit: Some(1_000_000),
injection_types: SyscallsInjectionTypes::all_once(),
Expand All @@ -171,6 +179,7 @@ impl ConfigsBundle for StandardGearWasmConfigsBundle {
fn into_parts(self) -> (GearWasmGeneratorConfig, SelectableParams) {
let StandardGearWasmConfigsBundle {
log_info,
waiting_probability,
remove_recursion,
critical_gas_limit,
injection_types,
Expand All @@ -186,6 +195,10 @@ impl ConfigsBundle for StandardGearWasmConfigsBundle {
if let Some(log_info) = log_info {
syscalls_config_builder = syscalls_config_builder.with_log_info(log_info);
}
if let Some(waiting_probability) = waiting_probability {
syscalls_config_builder =
syscalls_config_builder.with_waiting_probability(waiting_probability);
}
syscalls_config_builder = syscalls_config_builder.with_params_config(params_config);

let memory_pages_config = MemoryPagesConfig {
Expand Down
12 changes: 8 additions & 4 deletions utils/wasm-gen/src/config/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use std::num::NonZeroUsize;

use crate::MemoryLayout;
use arbitrary::{Arbitrary, Result, Unstructured};
pub use wasm_smith::InstructionKind;
use wasm_smith::{InstructionKind::*, InstructionKinds, SwarmConfig};
Expand Down Expand Up @@ -81,6 +82,8 @@ impl From<(SelectableParams, ArbitraryParams)> for WasmModuleConfig {
min_data_segments,
max_types,
min_types,
memory_offset_choices,
reserved_memory_size,
} = ConstantParams::default();

let SelectableParams {
Expand Down Expand Up @@ -109,7 +112,6 @@ impl From<(SelectableParams, ArbitraryParams)> for WasmModuleConfig {
max_type_size,
max_values,
memory_max_size_required,
memory_offset_choices,
min_element_segments,
min_elements,
min_globals,
Expand Down Expand Up @@ -169,6 +171,7 @@ impl From<(SelectableParams, ArbitraryParams)> for WasmModuleConfig {
min_uleb_size,
multi_value_enabled,
reference_types_enabled,
reserved_memory_size,
tail_call_enabled,
relaxed_simd_enabled,
saturating_float_to_int_enabled,
Expand Down Expand Up @@ -205,7 +208,6 @@ pub struct ArbitraryParams {
max_type_size: u32,
max_values: usize,
memory_max_size_required: bool,
memory_offset_choices: (u32, u32, u32),
min_element_segments: usize,
min_elements: usize,
min_globals: usize,
Expand Down Expand Up @@ -237,7 +239,6 @@ impl Arbitrary<'_> for ArbitraryParams {
max_type_size,
max_values,
memory_max_size_required,
memory_offset_choices,
min_element_segments,
min_elements,
min_globals,
Expand Down Expand Up @@ -267,7 +268,6 @@ impl Arbitrary<'_> for ArbitraryParams {
max_type_size,
max_values,
memory_max_size_required,
memory_offset_choices,
min_element_segments,
min_elements,
min_globals,
Expand Down Expand Up @@ -312,6 +312,8 @@ pub struct ConstantParams {
float_enabled: bool,
memory_grow_enabled: bool,
min_types: usize,
memory_offset_choices: (u32, u32, u32),
reserved_memory_size: Option<u64>,
}

impl Default for ConstantParams {
Expand Down Expand Up @@ -343,6 +345,8 @@ impl Default for ConstantParams {
min_data_segments: 0,
max_types: 100,
min_types: 5,
memory_offset_choices: (75, 25, 0),
reserved_memory_size: Some(MemoryLayout::RESERVED_MEMORY_SIZE as u64),
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions utils/wasm-gen/src/config/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod precise;
mod process_errors;

use gear_wasm_instrument::syscalls::SyscallName;
use std::num::NonZeroU32;

pub use injection::*;
pub use param::*;
Expand All @@ -46,6 +47,7 @@ impl SyscallsConfigBuilder {
precise_syscalls_config: PreciseSyscallsConfig::default(),
error_processing_config: ErrorProcessingConfig::None,
log_info: None,
waiting_probability: None,
})
}

Expand Down Expand Up @@ -93,6 +95,13 @@ impl SyscallsConfigBuilder {
self
}

/// Set probability of wait syscalls.
pub fn with_waiting_probability(mut self, waiting_probability: NonZeroU32) -> Self {
self.0.waiting_probability = Some(waiting_probability);

self
}

/// Setup fallible syscalls error processing options.
pub fn with_error_processing_config(mut self, config: ErrorProcessingConfig) -> Self {
self.0.error_processing_config = config;
Expand All @@ -114,6 +123,7 @@ pub struct SyscallsConfig {
precise_syscalls_config: PreciseSyscallsConfig,
error_processing_config: ErrorProcessingConfig,
log_info: Option<String>,
waiting_probability: Option<NonZeroU32>,
}

impl SyscallsConfig {
Expand Down Expand Up @@ -143,4 +153,9 @@ impl SyscallsConfig {
pub fn error_processing_config(&self) -> &ErrorProcessingConfig {
&self.error_processing_config
}

/// Get probability of wait syscalls.
pub fn waiting_probability(&self) -> Option<NonZeroU32> {
self.waiting_probability
}
}
21 changes: 8 additions & 13 deletions utils/wasm-gen/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,14 @@
//!
//! # First generators nesting level
//! GearWasmGenerator--->MemoryGenerator--->DisabledMemoryGenerator--->ModuleWithCallIndexes--->WasmModule
//! GearWasmGenerator--->EntryPointsGenerator--->DisabledEntryPointsGenerator--->ModuleWithCallIndexes--->WasmModule
//!
//! # Second generators nesting level
//! GearWasmGenerator--->MemoryGenerator--(DisabledMemoryGenerator, FrozenGearWasmGenerator)---\
//! |--->GearWasmGenerator--->EntryPointsGenerator--->DisabledEntryPointsGenerator--->ModuleWithCallIndexes--->
//!
//! GearWasmGenerator--->EntryPointsGenerator--(DisabledEntryPointsGenerator, FrozenGearWasmGenerator)---\
//! |--->GearWasmGenerator--->MemoryGenerator--->DisabledMemoryGenerator--->ModuleWithCallIndexes--->WasmModule
//!
//! # Third generators nesting level
//! GearWasmGenerator--->MemoryGenerator--(DisabledMemoryGenerator, FrozenGearWasmGenerator)---\
//! |--->GearWasmGenerator--->EntryPointsGenerator--->DisabledEntryPointsGenerator--(MemoryImportGenerationProof, GearEntryPointGenerationProof)-->(syscalls-module-state-machine)
//!
//! GearWasmGenerator--->EntryPointsGenerator--(DisabledEntryPointsGenerator, FrozenGearWasmGenerator)---\
//! |--->GearWasmGenerator--->MemoryGenerator--->DisabledMemoryGenerator--(MemoryImportGenerationProof, GearEntryPointGenerationProof)-->(syscalls-module-state-machine)
//! ```
//!
//! State machine named `(syscalls-module-state-machine)` can be started only with having proof of work from `MemoryGenerator` and `EntryPointsGenerator`.
Expand Down Expand Up @@ -125,11 +118,11 @@ impl<'a, 'b> GearWasmGenerator<'a, 'b> {
self.generate_memory_export();

let (disabled_ep_gen, frozen_gear_wasm_gen, ep_gen_proof) =
Self::from((disabled_mem_gen, frozen_gear_wasm_gen)).generate_entry_points()?;
Self::from((disabled_mem_gen, frozen_gear_wasm_gen))
.generate_entry_points(mem_imports_gen_proof)?;

let (disabled_syscalls_invocator, frozen_gear_wasm_gen) =
Self::from((disabled_ep_gen, frozen_gear_wasm_gen))
.generate_syscalls(mem_imports_gen_proof, ep_gen_proof)?;
Self::from((disabled_ep_gen, frozen_gear_wasm_gen)).generate_syscalls(ep_gen_proof)?;

let config = frozen_gear_wasm_gen.melt();
let module = ModuleWithCallIndexes::from(disabled_syscalls_invocator)
Expand Down Expand Up @@ -171,13 +164,16 @@ impl<'a, 'b> GearWasmGenerator<'a, 'b> {
/// Generate gear wasm gentry points using entry points generator.
pub fn generate_entry_points(
self,
mem_import_gen_proof: MemoryImportGenerationProof,
) -> Result<(
DisabledEntryPointsGenerator<'a, 'b>,
FrozenGearWasmGenerator<'a, 'b>,
GearEntryPointGenerationProof,
)> {
let entry_points_gen_instantiator =
EntryPointsGeneratorInstantiator::from((self, mem_import_gen_proof));
let (ep_gen, frozen_gear_wasm_gen): (EntryPointsGenerator, FrozenGearWasmGenerator) =
self.into();
entry_points_gen_instantiator.into();
let (disabled_ep_gen, ep_gen_proof) = ep_gen.generate_entry_points()?;

Ok((disabled_ep_gen, frozen_gear_wasm_gen, ep_gen_proof))
Expand All @@ -186,11 +182,10 @@ impl<'a, 'b> GearWasmGenerator<'a, 'b> {
/// Generate syscalls using syscalls module generators.
pub fn generate_syscalls(
self,
mem_import_gen_proof: MemoryImportGenerationProof,
ep_gen_proof: GearEntryPointGenerationProof,
) -> Result<(DisabledSyscallsInvocator, FrozenGearWasmGenerator<'a, 'b>)> {
let syscalls_imports_gen_instantiator =
SyscallsImportsGeneratorInstantiator::from((self, mem_import_gen_proof, ep_gen_proof));
SyscallsImportsGeneratorInstantiator::from((self, ep_gen_proof));
let (syscalls_imports_gen, frozen_gear_wasm_gen) = syscalls_imports_gen_instantiator.into();
let syscalls_imports_gen_res = syscalls_imports_gen.generate()?;

Expand Down
53 changes: 48 additions & 5 deletions utils/wasm-gen/src/generator/entry_points.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
//! Gear wasm entry points generator module.
use crate::{
generator::{CallIndexes, FrozenGearWasmGenerator, GearWasmGenerator, ModuleWithCallIndexes},
EntryPointsSet, WasmModule,
generator::{
CallIndexes, FrozenGearWasmGenerator, GearWasmGenerator, MemoryImportGenerationProof,
ModuleWithCallIndexes,
},
wasm::{PageCount as WasmPageCount, WasmModule},
EntryPointsSet, MemoryLayout,
};
use arbitrary::{Result, Unstructured};
use gear_wasm_instrument::parity_wasm::{
Expand All @@ -38,13 +42,29 @@ pub struct EntryPointsGenerator<'a, 'b> {
call_indexes: CallIndexes,
}

impl<'a, 'b> From<GearWasmGenerator<'a, 'b>>
/// Entry points generator instantiator.
///
/// Serves as a new type in order to create the generator from gear wasm generator and memory import proof.
pub struct EntryPointsGeneratorInstantiator<'a, 'b>(
(GearWasmGenerator<'a, 'b>, MemoryImportGenerationProof),
);

impl<'a, 'b> From<(GearWasmGenerator<'a, 'b>, MemoryImportGenerationProof)>
for EntryPointsGeneratorInstantiator<'a, 'b>
{
fn from(inner: (GearWasmGenerator<'a, 'b>, MemoryImportGenerationProof)) -> Self {
Self(inner)
}
}

impl<'a, 'b> From<EntryPointsGeneratorInstantiator<'a, 'b>>
for (
EntryPointsGenerator<'a, 'b>,
FrozenGearWasmGenerator<'a, 'b>,
)
{
fn from(generator: GearWasmGenerator<'a, 'b>) -> Self {
fn from(instantiator: EntryPointsGeneratorInstantiator<'a, 'b>) -> Self {
let EntryPointsGeneratorInstantiator((generator, _mem_import_gen_proof)) = instantiator;
let ep_generator = EntryPointsGenerator {
unstructured: generator.unstructured,
module: generator.module,
Expand Down Expand Up @@ -165,7 +185,8 @@ impl<'a, 'b> EntryPointsGenerator<'a, 'b> {
});

let export_body_instructions =
self.generate_export_body(export_body_call_idx, export_body_call_func_type)?;
self.generate_export_body(name, export_body_call_idx, export_body_call_func_type)?;

self.module.with(|module| {
let module = builder::from_module(module)
.function()
Expand Down Expand Up @@ -194,6 +215,7 @@ impl<'a, 'b> EntryPointsGenerator<'a, 'b> {
/// Generates body of the export function.
fn generate_export_body(
&mut self,
name: &str,
export_body_call_idx: usize,
export_body_call_func_type: FunctionType,
) -> Result<Vec<Instruction>> {
Expand All @@ -211,6 +233,27 @@ impl<'a, 'b> EntryPointsGenerator<'a, 'b> {
}
res.push(Instruction::Call(export_body_call_idx as u32));
res.extend(results.iter().map(|_| Instruction::Drop));

// after initializing the program, we will write about this in a special pointer
if name == "init" {
let memory_size_pages = self
.module
.initial_mem_size()
.expect("generator is instantiated with a mem import generation proof");
let mem_size = Into::<WasmPageCount>::into(memory_size_pages).memory_size();

let MemoryLayout {
init_called_ptr, ..
} = MemoryLayout::from(mem_size);

res.extend_from_slice(&[
// *init_called_ptr = true
Instruction::I32Const(init_called_ptr),
Instruction::I32Const(1),
Instruction::I32Store8(0, 0),
]);
}

res.push(Instruction::End);

Ok(res)
Expand Down
8 changes: 8 additions & 0 deletions utils/wasm-gen/src/generator/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@ impl InvocableSyscall {
})
}

/// Returns `true` for wait syscalls.
fn is_wait_syscall(&self) -> bool {
use InvocableSyscall::*;
use SyscallName::*;

matches!(self, Loose(Wait | WaitFor | WaitUpTo))
}

/// Checks whether syscall is error-prone either by returning error indicating value
/// or by providing error pointer as a syscall param.
///
Expand Down
Loading

0 comments on commit b7741a5

Please sign in to comment.