Platform: Code4rena
Start Date: 01/03/2024
Pot Size: $60,500 USDC
Total HM: 4
Participants: 18
Period: 21 days
Judge: Lambda
Total Solo HM: 3
Id: 344
League: POLKADOT
Rank: 9/18
Findings: 1
Award: $391.62
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Cryptor
Also found by: 0xTheC0der, Bauchibred, Daniel526, XDZIBECX, ihtishamsudo, zhaojie
391.6228 USDC - $391.62
overlay.commit_transaction()
overlay.commit_transaction()
is expected to always succeed, as indicated by the .expect("BUG: mis-paired transaction"). If it fails, the program will panic. It might be better to handle this error gracefully
To handle the error, you can return a Result from the function. This allows the caller to decide what to do in case of an error. Mitigated code :
pub fn execute_with<R>( &self, exec_context: &ExecContext, f: impl FnOnce() -> R, - ) -> (R, ExecSideEffects, OverlayedChanges<Hashing>) { + ) -> Result<(R, ExecSideEffects, OverlayedChanges<Hashing>), &'static str> { let backend = self.backend.as_trie_backend(); let mut overlay = OverlayedChanges::default(); overlay.start_transaction(); let mut ext = Ext::new(&mut overlay, backend, None); let (rv, effects) = sp_externalities::set_and_run_with_externalities(&mut ext, move || { Timestamp::set_timestamp(exec_context.now_ms); System::set_block_number(exec_context.block_number); System::reset_events(); let result = f(); let (system_events, effects) = crate::runtime::get_side_effects(); maybe_emit_system_event_block(system_events); (result, effects) }); overlay .commit_transaction() - .expect("BUG: mis-paired transaction"); - (rv, effects, overlay) + .map_err(|_| "BUG: mis-paired transaction")?; + Ok((rv, effects, overlay)) } }
check_metadata_with_path
silently returns an empty vector if read operation failsIn the check_metadata_with_path function, the result of std::fs::read(path)
is unwrapped with unwrap_or_default(). incase of file read operation fails, this will silently return an empty vector. it might be better to handle this error explicitly instead of failing silently.
you can use a match statement to handle the Result returned by std::fs::read(path). If the operation is successful, you can proceed as normal. If it fails, you can handle the error in a way provided
fn check_metadata_with_path(path: &str) -> Result<(), String> { let storage = crate::storage::in_memory_backend::InMemoryStorage::default(); let context = pink_capi::v1::ocall::ExecContext { block_number: 1, ..Default::default() }; let metadata: Vec<u8> = storage .execute_with(&context, || PinkRuntime::metadata().into()) .0; - let old_metadata = std::fs::read(path).unwrap_or_default(); + let old_metadata = match std::fs::read(path) { + Ok(data) => data, + Err(e) => { + return Err(format!("Failed to read file: {}", e)); + }, + }; if metadata != old_metadata { let new_metadata_path = std::env::current_dir().unwrap().join(format!("{path}.new")); std::fs::write(&new_metadata_path, metadata).unwrap(); return Err(format!( "Pink runtime metadata changed. The new metadata is stored at \n {:?}", new_metadata_path.display() )); } Ok(()) }
The panic! macro is used to stop execution when a condition is not met. This is useful for testing and prototyping, but should be avoided in production code.
unsafe extern "C" fn _default_ocall( _call_id: u32, _data: *const u8, _len: usize, _ctx: *mut ::core::ffi::c_void, _output: output_fn_t, ) { panic!("No ocall function provided"); }
Using Result
as return type for functions that can fail is the idiomatic way to handle errors in Rust. The Result type is an enum that can be either Ok
or Err
. The Err variant can contain an error message. The ? operator can be used to propagate the error message to the caller.
expect
usage of expect method when creating the Tokio runtime. If the creation of the runtime fails, it will panic with a provided error message, causing the program to crash. This behavior is undesirable, especially in production environments or critical systems.
fn block_on<F: core::future::Future>(f: F) -> F::Output { match tokio::runtime::Handle::try_current() { Ok(handle) => handle.block_on(f), Err(_) => tokio::runtime::Runtime::new() @> .expect("Failed to create tokio runtime") .block_on(f), } }
it's recommended to handle the error case more gracefully, rather than panicking. Instead of using expect, a safer method for error handling should be employed, such as returning a default value
or propagating the error to the caller for appropriate handling.
try to mitigate all the instances in codebase where expect is used.
unwrap
The function with_global_cache employs the unwrap method when accessing the mutex lock in non-test mode. Using unwrap can result in a panic if the mutex is poisoned, leading to unexpected program termination.
fn with_global_cache<T>(f: impl FnOnce(&mut LocalCache) -> T) -> T { if TEST_MODE.load(Ordering::Relaxed) { // Unittests are running in multi-threaded env. Let's give per test case a cache instance. use std::cell::RefCell; thread_local! { pub static GLOBAL_CACHE: RefCell<LocalCache> = RefCell::new(LocalCache::new()); } GLOBAL_CACHE.with(move |cache| f(&mut cache.borrow_mut())) } else { use std::sync::Mutex; pub static GLOBAL_CACHE: Mutex<LocalCache> = Mutex::new(LocalCache::new()); @> f(&mut GLOBAL_CACHE.lock().unwrap()) } }
it is advised to handle potential errors by using custom error
handling instead of unwrap
ecall_impl.rs
The code in ecall_impl.rs contains a type mismatch vulnerability, specifically an error of type E0308 in Rust, due to inconsistent types between the expected return type and the actual return value.
indicates that there is a mismatch between the expected return type, which is an enum std::result::Result
, and the actual return value, which is a tuple (_, _, _)
it is recommended to modify the pattern matching to correctly match the expected return type.
Try wrapping the pattern in Ok
let Ok((rv, _effects, _)) = self.execute_with(&context, f);
The problem arises from the use of the format! macro. This is used to format a string with the given arguments. Returning a custom error is desirable.
let mut response = match result { Ok(response) => response, Err(err) => { // If there is somthing wrong with the network, we can not inspect the reason too // much here. Let it return a non-standard 523 here. return Ok(HttpResponse { status_code: 523, reason_phrase: "Unreachable".into(), @> body: format!("{err:?}").into_bytes(), headers: vec![], }); } };
Consider mitgating all the instances in the codebase where the format! macro is used.
It is recommended to use the custom error
handling instead of the format! macro. This will allow the caller to handle the error in a more appropriate way.
Latest version of ink!
is 5.0.0
and project is using version 4.2
Consider Upgrading ink! version to latest
#0 - c4-pre-sort
2024-03-25T04:58:13Z
141345 marked the issue as sufficient quality report
#1 - 141345
2024-03-25T09:06:52Z
34 ihtishamsudo l r nc 0 1 6
L 1 n L 2 n L 3 n L 4 n L 5 r
NC 1 n NC 2 n
#2 - c4-judge
2024-03-27T15:31:35Z
OpenCoreCH marked the issue as grade-a