Skip to content

Commit c56a9a5

Browse files
committed
Disallow calling random lifecycle reducers
1 parent 7d5eb15 commit c56a9a5

File tree

4 files changed

+47
-5
lines changed

4 files changed

+47
-5
lines changed

crates/core/src/host/module_host.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::wasm_common::{IDENTITY_CONNECTED_DUNDER, IDENTITY_DISCONNECTED_DUNDER};
12
use super::{ArgsTuple, InvalidReducerArguments, ReducerArgs, ReducerCallResult, ReducerId, Timestamp};
23
use crate::client::{ClientActorId, ClientConnectionSender};
34
use crate::database_logger::LogLevel;
@@ -677,9 +678,9 @@ impl ModuleHost {
677678
None,
678679
None,
679680
if connected {
680-
"__identity_connected__"
681+
IDENTITY_CONNECTED_DUNDER
681682
} else {
682-
"__identity_disconnected__"
683+
IDENTITY_DISCONNECTED_DUNDER
683684
},
684685
ReducerArgs::Nullary,
685686
)
@@ -735,6 +736,9 @@ impl ModuleHost {
735736
reducer_name: &str,
736737
args: ReducerArgs,
737738
) -> Result<ReducerCallResult, ReducerCallError> {
739+
if reducer_name.starts_with("__") && reducer_name.ends_with("__") {
740+
return Err(ReducerCallError::NoSuchReducer);
741+
}
738742
let res = self
739743
.call_reducer_inner(
740744
caller_identity,

crates/core/src/host/wasm_common.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pub const SETUP_DUNDER: &str = "__setup__";
2121
pub const INIT_DUNDER: &str = "__init__";
2222
/// the reducer with this name is invoked when updating the database
2323
pub const UPDATE_DUNDER: &str = "__update__";
24+
pub const IDENTITY_CONNECTED_DUNDER: &str = "__identity_connected__";
25+
pub const IDENTITY_DISCONNECTED_DUNDER: &str = "__identity_disconnected__";
2426

2527
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
2628
#[allow(unused)]

crates/core/src/host/wasm_common/module_host_actor.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::time::Duration;
55

66
use spacetimedb_lib::buffer::DecodeError;
77
use spacetimedb_lib::identity::AuthCtx;
8-
use spacetimedb_lib::{bsatn, Address, ModuleDef, TableDesc};
8+
use spacetimedb_lib::{bsatn, Address, ModuleDef, ModuleValidationError, TableDesc};
99
use spacetimedb_vm::expr::CrudExpr;
1010

1111
use super::instrumentation::CallTimes;
@@ -90,6 +90,8 @@ pub(crate) struct WasmModuleHostActor<T: WasmModule> {
9090
pub enum InitializationError {
9191
#[error(transparent)]
9292
Validation(#[from] ValidationError),
93+
#[error(transparent)]
94+
ModuleValidation(#[from] ModuleValidationError),
9395
#[error("setup function returned an error: {0}")]
9496
Setup(Box<str>),
9597
#[error("wasm trap while calling {func:?}")]
@@ -153,7 +155,8 @@ impl<T: WasmModule> WasmModuleHostActor<T> {
153155
)?;
154156

155157
let desc = instance.extract_descriptions()?;
156-
let desc = bsatn::from_slice(&desc).map_err(DescribeError::Decode)?;
158+
let desc: ModuleDef = bsatn::from_slice(&desc).map_err(DescribeError::Decode)?;
159+
desc.validate_reducers()?;
157160
let ModuleDef {
158161
mut typespace,
159162
mut tables,
@@ -173,7 +176,10 @@ impl<T: WasmModule> WasmModuleHostActor<T> {
173176
tables
174177
.into_iter()
175178
.map(|x| (x.schema.table_name.clone(), EntityDef::Table(x))),
176-
reducers.iter().map(|x| (x.name.clone(), EntityDef::Reducer(x.clone()))),
179+
reducers
180+
.iter()
181+
.filter(|r| !(r.name.starts_with("__") && r.name.ends_with("__")))
182+
.map(|x| (x.name.clone(), EntityDef::Reducer(x.clone()))),
177183
)
178184
.collect();
179185
let reducers = ReducersMap(reducers.into_iter().map(|x| (x.name.clone(), x)).collect());

crates/lib/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,33 @@ pub struct TypeAlias {
187187
pub name: String,
188188
pub ty: sats::AlgebraicTypeRef,
189189
}
190+
191+
impl ModuleDef {
192+
pub fn validate_reducers(&self) -> Result<(), ModuleValidationError> {
193+
for reducer in &self.reducers {
194+
match &*reducer.name {
195+
"__init__" => {}
196+
"__identity_connected__" | "__identity_disconnected__" | "__update__" | "__migrate__" => {
197+
if !reducer.args.is_empty() {
198+
return Err(ModuleValidationError::InvalidLifecycleReducer {
199+
reducer: reducer.name.clone(),
200+
});
201+
}
202+
}
203+
name if name.starts_with("__") && name.ends_with("__") => {
204+
return Err(ModuleValidationError::UnknownDunderscore)
205+
}
206+
_ => {}
207+
}
208+
}
209+
Ok(())
210+
}
211+
}
212+
213+
#[derive(thiserror::Error, Debug)]
214+
pub enum ModuleValidationError {
215+
#[error("lifecycle reducer {reducer:?} has invalid signature")]
216+
InvalidLifecycleReducer { reducer: Box<str> },
217+
#[error("reducers with double-underscores at the start and end of their names are not allowed")]
218+
UnknownDunderscore,
219+
}

0 commit comments

Comments
 (0)