Skip to content

Commit 109c5e9

Browse files
committed
detect re-entrent upgrade in syscall
1 parent 53aa0d5 commit 109c5e9

File tree

9 files changed

+48
-6
lines changed

9 files changed

+48
-6
lines changed

fvm/src/call_manager/default.rs

Lines changed: 7 additions & 0 deletions
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,7 @@ pub struct InnerDefaultCallManager<M: Machine> {
7576
limits: M::Limiter,
7677
/// Accumulator for events emitted in this call stack.
7778
events: EventsAccumulator,
79+
code_upgrade_by_actor: HashMap<ActorID, i32>,
7880
}
7981

8082
#[doc(hidden)]
@@ -159,6 +161,7 @@ where
159161
limits,
160162
events: Default::default(),
161163
state_access_tracker,
164+
code_upgrade_by_actor: HashMap::new(),
162165
})))
163166
}
164167

@@ -327,6 +330,10 @@ where
327330
self.nonce
328331
}
329332

333+
fn get_actor_upgrade_stack(&mut self) -> &mut HashMap<ActorID, i32> {
334+
&mut self.code_upgrade_by_actor
335+
}
336+
330337
fn next_actor_address(&self) -> Address {
331338
// Base the next address on the address specified as the message origin. This lets us use,
332339
// e.g., an f2 address even if we can't look it up anywhere.

fvm/src/call_manager/mod.rs

Lines changed: 3 additions & 0 deletions
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,8 @@ pub trait CallManager: 'static {
119120
delegated_address: Option<Address>,
120121
) -> Result<()>;
121122

123+
fn get_actor_upgrade_stack(&mut self) -> &mut HashMap<ActorID, i32>;
124+
122125
/// Resolve an address into an actor ID, charging gas as appropriate.
123126
fn resolve_address(&self, address: &Address) -> Result<Option<ActorID>>;
124127

fvm/src/kernel/default.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,14 @@ where
873873
.create_actor(code_id, actor_id, delegated_address)
874874
}
875875

876+
fn actor_on_upgrade_stack(&mut self) -> bool {
877+
self.call_manager
878+
.get_actor_upgrade_stack()
879+
.get(&self.actor_id)
880+
.map(|count| *count > 0)
881+
.unwrap_or(false)
882+
}
883+
876884
fn upgrade_actor<K: Kernel>(
877885
&mut self,
878886
new_code_cid: Cid,
@@ -896,6 +904,12 @@ where
896904
return Err(syscall_error!(LimitExceeded; "cannot store return block").into());
897905
}
898906

907+
self.call_manager
908+
.get_actor_upgrade_stack()
909+
.entry(self.actor_id)
910+
.and_modify(|count| *count += 1)
911+
.or_insert(1);
912+
899913
let result = self.call_manager.with_transaction(|cm| {
900914
let origin = cm.origin();
901915

fvm/src/kernel/mod.rs

Lines changed: 2 additions & 0 deletions
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 actor_on_upgrade_stack(&mut 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

Lines changed: 8 additions & 0 deletions
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+
// Check if this actor is already being upgraded.
125+
if context.kernel.actor_on_upgrade_stack() {
126+
return ControlFlow::Error(syscall_error!(
127+
Forbidden;
128+
"re-entrant upgrade detected"
129+
));
130+
};
131+
124132
match context.kernel.upgrade_actor::<K>(cid, params_id) {
125133
Ok(SendResult {
126134
block_id,

fvm/tests/dummy.rs

Lines changed: 5 additions & 0 deletions
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,10 @@ impl CallManager for DummyCallManager {
358359
todo!()
359360
}
360361

362+
fn get_actor_upgrade_stack(&mut self) -> &mut HashMap<ActorID, i32> {
363+
todo!()
364+
}
365+
361366
fn invocation_count(&self) -> u64 {
362367
todo!()
363368
}

testing/conformance/src/vm.rs

Lines changed: 4 additions & 0 deletions
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 actor_on_upgrade_stack(&mut self) -> bool {
307+
self.0.actor_on_upgrade_stack()
308+
}
305309
}
306310

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

testing/integration/tests/main.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,9 +1188,6 @@ fn upgrade_actor_test() {
11881188
.execute_message(message, ApplyKind::Explicit, 100)
11891189
.unwrap();
11901190

1191-
let val: i64 = res.msg_receipt.return_data.deserialize().unwrap();
1192-
assert_eq!(val, 444);
1193-
11941191
assert!(
11951192
res.msg_receipt.exit_code.is_success(),
11961193
"{:?}",

testing/test_actors/actors/fil-upgrade-actor/src/actor.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use fvm_ipld_encoding::ipld_block::IpldBlock;
44
use fvm_ipld_encoding::{to_vec, CBOR};
55
use fvm_sdk as sdk;
66
use fvm_shared::address::Address;
7+
use fvm_shared::error::ErrorNumber;
78
use fvm_shared::upgrade::UpgradeInfo;
89
use serde_tuple::*;
910
#[derive(Serialize_tuple, Deserialize_tuple, PartialEq, Eq, Clone, Debug)]
@@ -48,8 +49,9 @@ pub fn upgrade(params_id: u32, upgrade_info_id: u32) -> u32 {
4849
sdk::debug::log("[upgrade] params:3, calling upgrade within an upgrade".to_string());
4950
let new_code_cid = sdk::actor::get_actor_code_cid(&Address::new_id(10000)).unwrap();
5051
let params = IpldBlock::serialize_cbor(&SomeStruct { value: 4 }).unwrap();
51-
let _ = sdk::actor::upgrade_actor(new_code_cid, params);
52-
unreachable!("we should never return from a successful upgrade");
52+
let res = sdk::actor::upgrade_actor(new_code_cid, params);
53+
assert_eq!(res, Err(ErrorNumber::Forbidden));
54+
0
5355
}
5456
4 => {
5557
let block_id = sdk::ipld::put_block(CBOR, &to_vec(&444).unwrap()).unwrap();
@@ -88,7 +90,7 @@ pub fn invoke(_: u32) -> u32 {
8890
"invalid exit code returned from upgrade_actor"
8991
);
9092
}
91-
// test recursive update
93+
// test that calling recursive update on the same actor fails
9294
3 => {
9395
let new_code_cid = sdk::actor::get_actor_code_cid(&Address::new_id(10000)).unwrap();
9496
let params = IpldBlock::serialize_cbor(&SomeStruct { value: 3 }).unwrap();

0 commit comments

Comments
 (0)