Skip to content

Commit

Permalink
RUN-882: Avoid traps in the ic0.call_perform System API
Browse files Browse the repository at this point in the history
  • Loading branch information
ulan committed Jan 10, 2024
1 parent fed4316 commit 19533e6
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 20 deletions.
6 changes: 2 additions & 4 deletions rs/execution_environment/src/hypervisor/tests.rs
Expand Up @@ -5951,13 +5951,11 @@ fn call_perform_checks_freezing_threshold_in_update() {
.build();
let err = test.ingress(canister_id, "update", body).unwrap_err();
assert!(
err.description().contains(
"Canister cannot grow message memory by 2097168 bytes due to insufficient cycles"
),
err.description().contains("call_perform failed"),
"Unexpected error: {}",
err.description()
);
assert_eq!(err.code(), ErrorCode::InsufficientCyclesInMessageMemoryGrow);
assert_eq!(err.code(), ErrorCode::CanisterCalledTrap);
}

#[test]
Expand Down
13 changes: 4 additions & 9 deletions rs/system_api/src/lib.rs
Expand Up @@ -1280,20 +1280,15 @@ impl SystemApiImpl {
} else {
(memory_required_to_push_request(&req) as u64).into()
};
if let Err(err) = self.memory_usage.allocate_message_memory(
if let Err(_err) = self.memory_usage.allocate_message_memory(
reservation_bytes,
&self.api_type,
&self.sandbox_safe_system_state,
) {
abort(req, &mut self.sandbox_safe_system_state);
match err {
err @ HypervisorError::InsufficientCyclesInMessageMemoryGrow { .. } => {
// Return an the out-of-cycles error in this case for a better
// error message to be relayed to the caller.
return Err(err);
}
_ => return Ok(RejectCode::SysTransient as i32),
}
// Return an error code instead of trapping here in order to allow
// the user code to handle the error gracefully.
return Ok(RejectCode::SysTransient as i32);
}

match self.sandbox_safe_system_state.push_output_request(
Expand Down
13 changes: 6 additions & 7 deletions rs/system_api/tests/system_api.rs
Expand Up @@ -1422,9 +1422,10 @@ fn msg_cycles_accept_all_cycles_in_call_context_when_more_asked() {
}

/// If call call_perform() fails because canister does not have enough
/// cycles to send the message, then the state is reset.
/// cycles to send the message, then it does not trap, but returns
/// a transient error reject code.
#[test]
fn call_perform_not_enough_cycles_resets_state() {
fn call_perform_not_enough_cycles_does_not_trap() {
let cycles_account_manager = CyclesAccountManagerBuilder::new()
.with_subnet_type(SubnetType::Application)
.build();
Expand Down Expand Up @@ -1453,11 +1454,9 @@ fn call_perform_not_enough_cycles_resets_state() {
api.ic0_call_cycles_add128(Cycles::new(100)).unwrap();
let res = api.ic0_call_perform();
match res {
Err(HypervisorError::InsufficientCyclesInMessageMemoryGrow {
bytes: _,
available: _,
threshold: _,
}) => {}
Ok(code) => {
assert_eq!(code, RejectCode::SysTransient as i32);
}
_ => panic!(
"expected to get an InsufficientCyclesInMessageMemoryGrow error, got {:?}",
res
Expand Down

0 comments on commit 19533e6

Please sign in to comment.