Skip to content

Commit 1913868

Browse files
committed
Reject upgrades if actor already on call stack
1 parent 53aa0d5 commit 1913868

File tree

7 files changed

+53
-0
lines changed

7 files changed

+53
-0
lines changed

fvm/src/call_manager/default.rs

+12
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use fvm_shared::event::StampedEvent;
1414
use fvm_shared::sys::BlockId;
1515
use fvm_shared::{ActorID, METHOD_SEND};
1616
use num_traits::Zero;
17+
use std::collections::HashMap;
1718

1819
use super::state_access_tracker::{ActorAccessState, StateAccessTracker};
1920
use super::{Backtrace, CallManager, Entrypoint, InvocationResult, NO_DATA_BLOCK_ID};
@@ -75,6 +76,8 @@ pub struct InnerDefaultCallManager<M: Machine> {
7576
limits: M::Limiter,
7677
/// Accumulator for events emitted in this call stack.
7778
events: EventsAccumulator,
79+
/// A map of ActorID and how often they appear on the call stack.
80+
actor_call_stack: HashMap<ActorID, i32>,
7881
}
7982

8083
#[doc(hidden)]
@@ -159,6 +162,7 @@ where
159162
limits,
160163
events: Default::default(),
161164
state_access_tracker,
165+
actor_call_stack: HashMap::new(),
162166
})))
163167
}
164168

@@ -327,6 +331,14 @@ where
327331
self.nonce
328332
}
329333

334+
fn get_actor_call_stack(&self) -> &HashMap<ActorID, i32> {
335+
&self.actor_call_stack
336+
}
337+
338+
fn get_actor_call_stack_mut(&mut self) -> &mut HashMap<ActorID, i32> {
339+
&mut self.actor_call_stack
340+
}
341+
330342
fn next_actor_address(&self) -> Address {
331343
// Base the next address on the address specified as the message origin. This lets us use,
332344
// e.g., an f2 address even if we can't look it up anywhere.

fvm/src/call_manager/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use fvm_shared::econ::TokenAmount;
77
use fvm_shared::error::ExitCode;
88
use fvm_shared::upgrade::UpgradeInfo;
99
use fvm_shared::{ActorID, MethodNum};
10+
use std::collections::HashMap;
1011

1112
use crate::engine::Engine;
1213
use crate::gas::{Gas, GasCharge, GasTimer, GasTracker, PriceList};
@@ -119,6 +120,9 @@ pub trait CallManager: 'static {
119120
delegated_address: Option<Address>,
120121
) -> Result<()>;
121122

123+
fn get_actor_call_stack(&self) -> &HashMap<ActorID, i32>;
124+
fn get_actor_call_stack_mut(&mut self) -> &mut HashMap<ActorID, i32>;
125+
122126
/// Resolve an address into an actor ID, charging gas as appropriate.
123127
fn resolve_address(&self, address: &Address) -> Result<Option<ActorID>>;
124128

fvm/src/kernel/default.rs

+14
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ where
137137
return Err(syscall_error!(LimitExceeded; "cannot store return block").into());
138138
}
139139

140+
self.call_manager
141+
.get_actor_call_stack_mut()
142+
.entry(self.actor_id)
143+
.and_modify(|count| *count += 1)
144+
.or_insert(1);
145+
140146
// Send.
141147
let result = self.call_manager.with_transaction(|cm| {
142148
cm.call_actor::<K>(
@@ -873,6 +879,14 @@ where
873879
.create_actor(code_id, actor_id, delegated_address)
874880
}
875881

882+
fn is_actor_on_call_stack(&self) -> bool {
883+
self.call_manager
884+
.get_actor_call_stack()
885+
.get(&self.actor_id)
886+
.map(|count| *count > 0)
887+
.unwrap_or(false)
888+
}
889+
876890
fn upgrade_actor<K: Kernel>(
877891
&mut self,
878892
new_code_cid: Cid,

fvm/src/kernel/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ pub trait ActorOps {
215215
params_id: BlockId,
216216
) -> Result<SendResult>;
217217

218+
fn is_actor_on_call_stack(&self) -> bool;
219+
218220
/// Installs actor code pointed by cid
219221
#[cfg(feature = "m2-native")]
220222
fn install_actor(&mut self, code_cid: Cid) -> Result<()>;

fvm/src/syscalls/actor.rs

+8
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ pub fn upgrade_actor<K: Kernel>(
121121
Err(err) => return err.into(),
122122
};
123123

124+
// actor cannot be upgraded if its already on the call stack
125+
if context.kernel.is_actor_on_call_stack() {
126+
return ControlFlow::Error(syscall_error!(
127+
Forbidden;
128+
"cannot upgrade actor if it is already on the call stack"
129+
));
130+
};
131+
124132
match context.kernel.upgrade_actor::<K>(cid, params_id) {
125133
Ok(SendResult {
126134
block_id,

fvm/tests/dummy.rs

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0, MIT
33
use std::borrow::Borrow;
44
use std::cell::RefCell;
5+
use std::collections::HashMap;
56
use std::rc::Rc;
67

78
use anyhow::Context;
@@ -358,6 +359,14 @@ impl CallManager for DummyCallManager {
358359
todo!()
359360
}
360361

362+
fn get_actor_call_stack(&self) -> &HashMap<ActorID, i32> {
363+
todo!()
364+
}
365+
366+
fn get_actor_call_stack_mut(&mut self) -> &mut HashMap<ActorID, i32> {
367+
todo!()
368+
}
369+
361370
fn invocation_count(&self) -> u64 {
362371
todo!()
363372
}

testing/conformance/src/vm.rs

+4
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@ where
302302
fn upgrade_actor<KK>(&mut self, new_code_cid: Cid, params_id: BlockId) -> Result<SendResult> {
303303
self.0.upgrade_actor::<Self>(new_code_cid, params_id)
304304
}
305+
306+
fn is_actor_on_call_stack(&self) -> bool {
307+
self.0.is_actor_on_call_stack()
308+
}
305309
}
306310

307311
impl<M, C, K> IpldBlockOps for TestKernel<K>

0 commit comments

Comments
 (0)