Skip to content

Commit 2509e52

Browse files
author
Eduardo Leegwater Simões
committed
piecrust: one instance per contract function call
Changes the instance reclamation code to allow for exactly one module instance per contract function call. This changes the way in which the node processes the state, leading to diverging state roots on erroring calls. Simplifies the instance code by using a boolean to appropriately drop the instance. This allows the data in the struct to be passed around the `Env`, whilst fixing some bad dereferences previously occuring under certain situations. Resolves: #325
1 parent bb2f3fc commit 2509e52

File tree

5 files changed

+155
-176
lines changed

5 files changed

+155
-176
lines changed

piecrust/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16+
- Change to have one instance per contract function call [#325]
1617
- Upgrade `dusk-wasmtime` to version `17`
1718

1819
### Fixed
1920

21+
- Fix bad dereferences in caused by instance reclamation [#325]
2022
- Fix overflow in gas limit calculation in inter-contract call
2123

2224
## [0.15.0] - 2024-01-24
@@ -357,6 +359,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
357359
[#234]: https://github.com/dusk-network/piecrust/pull/234
358360

359361
<!-- ISSUES -->
362+
[#325]: https://github.com/dusk-network/piecrust/issues/325
360363
[#301]: https://github.com/dusk-network/piecrust/issues/313
361364
[#301]: https://github.com/dusk-network/piecrust/issues/301
362365
[#296]: https://github.com/dusk-network/piecrust/issues/296

piecrust/src/call_tree.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::mem;
1010
use piecrust_uplink::ContractId;
1111

1212
/// An element of the call tree.
13-
#[derive(Debug, Clone, Copy)]
13+
#[derive(Clone)]
1414
pub struct CallTreeElem {
1515
pub contract_id: ContractId,
1616
pub limit: u64,
@@ -47,7 +47,7 @@ impl CallTree {
4747
pub(crate) fn move_up(&mut self, spent: u64) -> Option<CallTreeElem> {
4848
self.0.map(|inner| unsafe {
4949
(*inner).elem.spent = spent;
50-
let elem = (*inner).elem;
50+
let elem = (*inner).elem.clone();
5151

5252
let parent = (*inner).parent;
5353
if parent.is_none() {
@@ -63,7 +63,7 @@ impl CallTree {
6363
/// current element.
6464
pub(crate) fn move_up_prune(&mut self) -> Option<CallTreeElem> {
6565
self.0.map(|inner| unsafe {
66-
let elem = (*inner).elem;
66+
let elem = (*inner).elem.clone();
6767

6868
let parent = (*inner).parent;
6969
if let Some(parent) = parent {
@@ -98,7 +98,7 @@ impl CallTree {
9898
i += 1;
9999
}
100100

101-
current.map(|inner| unsafe { (*inner).elem })
101+
current.map(|inner| unsafe { (*inner).elem.clone() })
102102
}
103103

104104
/// Clears the call tree of all elements.

piecrust/src/imports.rs

+19-23
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,12 @@ pub(crate) fn hq(
138138
) -> WasmtimeResult<u32> {
139139
let env = fenv.data_mut();
140140

141-
let instance = env.self_instance();
141+
let mut instance = env.self_instance();
142142

143143
let name_len = name_len as usize;
144144

145-
check_ptr(instance, name_ofs, name_len)?;
146-
check_arg(instance, arg_len)?;
145+
check_ptr(&instance, name_ofs, name_len)?;
146+
check_arg(&instance, arg_len)?;
147147

148148
let name = instance.with_memory(|buf| {
149149
// performance: use a dedicated buffer here?
@@ -163,11 +163,11 @@ pub(crate) fn hd(
163163
) -> WasmtimeResult<u32> {
164164
let env = fenv.data_mut();
165165

166-
let instance = env.self_instance();
166+
let mut instance = env.self_instance();
167167

168168
let name_len = name_len as usize;
169169

170-
check_ptr(instance, name_ofs, name_len)?;
170+
check_ptr(&instance, name_ofs, name_len)?;
171171

172172
let name = instance.with_memory(|buf| {
173173
// performance: use a dedicated buffer here?
@@ -194,13 +194,13 @@ pub(crate) fn c(
194194
) -> WasmtimeResult<i32> {
195195
let env = fenv.data_mut();
196196

197-
let instance = env.self_instance();
197+
let mut instance = env.self_instance();
198198

199199
let name_len = name_len as usize;
200200

201-
check_ptr(instance, mod_id_ofs, CONTRACT_ID_BYTES)?;
202-
check_ptr(instance, name_ofs, name_len)?;
203-
check_arg(instance, arg_len)?;
201+
check_ptr(&instance, mod_id_ofs, CONTRACT_ID_BYTES)?;
202+
check_ptr(&instance, name_ofs, name_len)?;
203+
check_arg(&instance, arg_len)?;
204204

205205
let argbuf_ofs = instance.arg_buffer_offset();
206206

@@ -222,12 +222,8 @@ pub(crate) fn c(
222222
&memory[mod_id_ofs..][..std::mem::size_of::<ContractId>()],
223223
);
224224

225-
let callee_stack_element = env
226-
.push_callstack(mod_id, callee_limit)
227-
.expect("pushing to the callstack should succeed");
228-
let callee = env
229-
.instance(&callee_stack_element.contract_id)
230-
.expect("callee instance should exist");
225+
let mut callee = env.instance(mod_id)?;
226+
env.push_callstack(mod_id, callee_limit, callee.mem_len());
231227

232228
callee.snap().map_err(|err| Error::MemorySnapshotFailure {
233229
reason: None,
@@ -242,7 +238,7 @@ pub(crate) fn c(
242238
let ret_len = callee
243239
.call(name, arg.len() as u32, callee_limit)
244240
.map_err(Error::normalize)?;
245-
check_arg(callee, ret_len as u32)?;
241+
check_arg(&callee, ret_len as u32)?;
246242

247243
// copy back result
248244
callee.read_argument(&mut memory[argbuf_ofs..][..ret_len as usize]);
@@ -291,8 +287,8 @@ pub(crate) fn emit(
291287

292288
let topic_len = topic_len as usize;
293289

294-
check_ptr(instance, topic_ofs, topic_len)?;
295-
check_arg(instance, arg_len)?;
290+
check_ptr(&instance, topic_ofs, topic_len)?;
291+
check_arg(&instance, arg_len)?;
296292

297293
let data = instance.with_arg_buf(|buf| {
298294
let arg_len = arg_len as usize;
@@ -327,7 +323,7 @@ fn feed(mut fenv: Caller<Env>, arg_len: u32) -> WasmtimeResult<()> {
327323
let env = fenv.data_mut();
328324
let instance = env.self_instance();
329325

330-
check_arg(instance, arg_len)?;
326+
check_arg(&instance, arg_len)?;
331327

332328
let data = instance.with_arg_buf(|buf| {
333329
let arg_len = arg_len as usize;
@@ -342,7 +338,7 @@ fn hdebug(mut fenv: Caller<Env>, msg_len: u32) -> WasmtimeResult<()> {
342338
let env = fenv.data_mut();
343339
let instance = env.self_instance();
344340

345-
check_arg(instance, msg_len)?;
341+
check_arg(&instance, msg_len)?;
346342

347343
Ok(instance.with_arg_buf(|buf| {
348344
let slice = &buf[..msg_len as usize];
@@ -365,7 +361,7 @@ fn limit(fenv: Caller<Env>) -> u64 {
365361

366362
fn spent(fenv: Caller<Env>) -> u64 {
367363
let env = fenv.data();
368-
let instance = env.self_instance();
364+
let mut instance = env.self_instance();
369365

370366
let limit = env.limit();
371367
let remaining = instance.get_remaining_gas();
@@ -377,7 +373,7 @@ fn panic(fenv: Caller<Env>, arg_len: u32) -> WasmtimeResult<()> {
377373
let env = fenv.data();
378374
let instance = env.self_instance();
379375

380-
check_arg(instance, arg_len)?;
376+
check_arg(&instance, arg_len)?;
381377

382378
Ok(instance.with_arg_buf(|buf| {
383379
let slice = &buf[..arg_len as usize];
@@ -392,7 +388,7 @@ fn panic(fenv: Caller<Env>, arg_len: u32) -> WasmtimeResult<()> {
392388
}
393389

394390
fn owner(fenv: Caller<Env>, mod_id_ofs: usize) -> WasmtimeResult<i32> {
395-
check_ptr(fenv.data().self_instance(), mod_id_ofs, CONTRACT_ID_BYTES)?;
391+
check_ptr(&fenv.data().self_instance(), mod_id_ofs, CONTRACT_ID_BYTES)?;
396392

397393
let env = fenv.data();
398394

piecrust/src/instance.rs

+60-36
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Copyright (c) DUSK NETWORK. All rights reserved.
66

77
use std::io;
8+
use std::mem::MaybeUninit;
89
use std::ops::{Deref, DerefMut};
910

1011
use dusk_wasmtime::{Instance, Module, Mutability, Store, ValType};
@@ -17,15 +18,47 @@ use crate::store::Memory;
1718
use crate::Error;
1819

1920
pub struct WrappedInstance {
20-
instance: Instance,
21-
arg_buf_ofs: usize,
22-
store: Store<Env>,
23-
memory: Memory,
21+
inner: *mut WrappedInstanceInner,
22+
original: bool,
23+
}
24+
25+
impl Clone for WrappedInstance {
26+
fn clone(&self) -> Self {
27+
Self {
28+
inner: self.inner,
29+
original: false,
30+
}
31+
}
32+
}
33+
34+
impl Drop for WrappedInstance {
35+
fn drop(&mut self) {
36+
if self.original {
37+
unsafe {
38+
let _ = Box::from_raw(self.inner);
39+
}
40+
}
41+
}
42+
}
43+
44+
impl Deref for WrappedInstance {
45+
type Target = WrappedInstanceInner;
46+
47+
fn deref(&self) -> &Self::Target {
48+
unsafe { &*self.inner }
49+
}
50+
}
51+
52+
impl DerefMut for WrappedInstance {
53+
fn deref_mut(&mut self) -> &mut Self::Target {
54+
unsafe { &mut *self.inner }
55+
}
2456
}
2557

2658
pub(crate) struct Env {
2759
self_id: ContractId,
2860
session: Session,
61+
instance: MaybeUninit<WrappedInstance>,
2962
}
3063

3164
impl Deref for Env {
@@ -43,20 +76,8 @@ impl DerefMut for Env {
4376
}
4477

4578
impl Env {
46-
pub fn self_instance<'b>(&self) -> &'b mut WrappedInstance {
47-
let stack_element = self
48-
.session
49-
.nth_from_top(0)
50-
.expect("there should be at least one element in the call stack");
51-
self.instance(&stack_element.contract_id)
52-
.expect("instance should exist")
53-
}
54-
55-
pub fn instance<'b>(
56-
&self,
57-
contract_id: &ContractId,
58-
) -> Option<&'b mut WrappedInstance> {
59-
self.session.instance(contract_id)
79+
pub fn self_instance(&self) -> WrappedInstance {
80+
unsafe { self.instance.assume_init_ref().clone() }
6081
}
6182

6283
pub fn limit(&self) -> u64 {
@@ -94,6 +115,7 @@ impl WrappedInstance {
94115
let env = Env {
95116
self_id: contract_id,
96117
session,
118+
instance: MaybeUninit::uninit(),
97119
};
98120

99121
let module =
@@ -177,28 +199,35 @@ impl WrappedInstance {
177199
// A memory is no longer new after one instantiation
178200
memory.is_new = false;
179201

180-
let wrapped = WrappedInstance {
202+
let inner = WrappedInstanceInner {
181203
store,
182204
instance,
183205
arg_buf_ofs,
184206
memory,
185207
};
186208

187-
Ok(wrapped)
188-
}
209+
let mut instance = WrappedInstance {
210+
inner: Box::into_raw(Box::new(inner)),
211+
original: true,
212+
};
213+
let instance_clone = instance.clone();
189214

190-
pub(crate) fn snap(&mut self) -> io::Result<()> {
191-
self.memory.snap()?;
192-
Ok(())
193-
}
215+
instance.store.data_mut().instance = MaybeUninit::new(instance_clone);
194216

195-
pub(crate) fn revert(&mut self) -> io::Result<()> {
196-
self.memory.revert()?;
197-
Ok(())
217+
Ok(instance)
198218
}
219+
}
199220

200-
pub(crate) fn apply(&mut self) -> io::Result<()> {
201-
self.memory.apply()?;
221+
pub struct WrappedInstanceInner {
222+
instance: Instance,
223+
arg_buf_ofs: usize,
224+
store: Store<Env>,
225+
memory: Memory,
226+
}
227+
228+
impl WrappedInstanceInner {
229+
pub(crate) fn snap(&mut self) -> io::Result<()> {
230+
self.memory.snap()?;
202231
Ok(())
203232
}
204233

@@ -238,11 +267,6 @@ impl WrappedInstance {
238267
self.memory.current_len
239268
}
240269

241-
/// Sets the length of the memory.
242-
pub(crate) fn set_len(&mut self, len: usize) {
243-
self.memory.current_len = len;
244-
}
245-
246270
pub(crate) fn with_arg_buf<F, R>(&self, f: F) -> R
247271
where
248272
F: FnOnce(&[u8]) -> R,
@@ -342,7 +366,7 @@ impl WrappedInstance {
342366
}
343367

344368
fn map_call_err(
345-
instance: &mut WrappedInstance,
369+
instance: &mut WrappedInstanceInner,
346370
err: dusk_wasmtime::Error,
347371
) -> Error {
348372
if instance.get_remaining_gas() == 0 {

0 commit comments

Comments
 (0)