Canister trapped: heap out of bounds, When trying to upload wasm and store it in stable memory

I am trying to upload wasm into this canister and store it in stable memory

The canister state has a field which uses stable memory to store wasm file

 #[serde(skip, default = "_default_wasms")]
 pub wasms: StableBTreeMap<WasmType, Blob<4000000>, Memory>,

fn _default_wasms() -> StableBTreeMap<WasmType, Blob<4000000>, Memory> { 
    StableBTreeMap::init(get_subnet_orchestrator_wasm_memory())
}

We have an upload_wasms update function that uploads Wasm to according to the wasmtype

 #[ic_cdk::update]
 #[candid_method(update)]
pub async fn upload_wasms(wasm_type: WasmType, wasm: Vec<u8>) -> Result<String, String> {
    CANISTER_DATA.with_borrow_mut(|canister_data| {
        canister_data.wasms.insert(wasm_type, Blob::from_bytes(Cow::Owned(wasm)));
    });
    Ok("Success".into())
}

When testing this functionality using the above update function on local machine using dfx I get

Canister <canister-id >trapped: heap out of bounds, error code None

WasmType:

#[derive(Clone, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, CandidType)]
pub enum WasmType {
    SubnetOrchestratorWasm,
    IndividualUserWasm,
    PostCacheWasm
}

impl BoundedStorable for WasmType {
    const MAX_SIZE: u32 = 4;

    const IS_FIXED_SIZE: bool = true;
}

impl Storable for WasmType {
    fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
        let mut bytes = vec![];
        ciborium::ser::into_writer(self, &mut bytes).unwrap();
        Cow::Owned(bytes)
    }

    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
        let wasm_type: WasmType = de::from_reader(bytes.as_ref()).unwrap();
        wasm_type
    }
}
1 Like

@dsarlis do you have any suggestion around this? Kind of stuck due to this

1 Like

I am trying the same method with compressed wasm size which is under 1MB.

I think some more information will be needed to help here. What exactly is CANISTER_DATA? Where have you defined it? If you can provide a minimal complete example that reproduces the problem (as opposed to giving some extracts of code) we can probably be more efficient in helping.

1 Like

Actually how many of these Wasms are you attempting to upload?

Also, it’d be interesting to see what get_subnet_orchestrator_wasm_memory() is doing.

@dsarlis Sorry for the confusion. Here are all the details

thread_local! {
    pub static CANISTER_DATA: RefCell<CanisterData> = RefCell::default();
}

#[derive(Clone, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, CandidType)]
pub enum WasmType {
    SubnetOrchestratorWasm,
    IndividualUserWasm,
    PostCacheWasm
}

impl BoundedStorable for WasmType {
    const MAX_SIZE: u32 = 4;

    const IS_FIXED_SIZE: bool = true;
}

impl Storable for WasmType {
    fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
        let mut bytes = vec![];
        ciborium::ser::into_writer(self, &mut bytes).unwrap();
        Cow::Owned(bytes)
    }

    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
        let wasm_type: WasmType = de::from_reader(bytes.as_ref()).unwrap();
        wasm_type
    }
}


#[derive(Serialize, Deserialize)]
pub struct CanisterData {
    pub all_subnet_orchestrator_canisters_list: HashSet<Principal>,
    pub all_post_cache_orchestrator_list: HashSet<Principal>,
    pub subet_orchestrator_with_capacity_left: HashSet<Principal>,
    pub version_detail: VersionDetails,
    #[serde(skip, default = "_default_wasms")]
    pub wasms: StableBTreeMap<WasmType, Blob<2000000>, Memory>,
    #[serde(skip, default = "_default_canister_upgrade_log")]
    pub subnet_canister_upgrade_log: StableLog<CanisterUpgradeStatus, Memory, Memory>,
    pub last_subnet_canister_upgrade_status: CanisterUpgradeStatus
}

fn _default_wasms() -> StableBTreeMap<WasmType, Blob<2000000>, Memory> { 
    StableBTreeMap::init(get_subnet_orchestrator_wasm_memory())
}

fn _default_canister_upgrade_log() -> StableLog<CanisterUpgradeStatus, Memory, Memory> {
    StableLog::init(get_canister_upgrade_log_index_memory(), get_canister_upgrade_log_memory()).unwrap()
}


 
use ic_stable_structures::{DefaultMemoryImpl, memory_manager::{self, MemoryId, MemoryManager, VirtualMemory}};
use std::cell::RefCell;

// A memory for upgrades, where data from the heap can be serialized/deserialized.
const UPGRADES: MemoryId = MemoryId::new(0);

// A memory for BtreeMap to store wasms
const SUBNET_ORCHESTRATOR_WASM_MEMORY: MemoryId = MemoryId::new(1);

//A memory for canister upgrade log index
const CANISTER_UPGRADE_LOG_INDEX: MemoryId = MemoryId::new(2);


//A memory for canister upgrade log 
const CANISTER_UPGRADE_LOG: MemoryId = MemoryId::new(3);

// A memory for the StableBTreeMap we're using. A new memory should be created for
// every additional stable structure.


pub type Memory = VirtualMemory<DefaultMemoryImpl>;

thread_local! {
    // The memory manager is used for simulating multiple memories. Given a `MemoryId` it can
    // return a memory that can be used by stable structures.
    static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> =
        RefCell::new(MemoryManager::init_with_bucket_size(DefaultMemoryImpl::default(), 1));
}

pub fn get_upgrades_memory() -> Memory {
    MEMORY_MANAGER.with(|m| m.borrow_mut().get(UPGRADES))
}

pub fn get_subnet_orchestrator_wasm_memory() -> Memory {
    MEMORY_MANAGER.with_borrow_mut(|memory_manager| memory_manager.get(SUBNET_ORCHESTRATOR_WASM_MEMORY))
}


pub fn get_canister_upgrade_log_index_memory() -> Memory {
    MEMORY_MANAGER.with_borrow_mut(|memory_manager| memory_manager.get(CANISTER_UPGRADE_LOG_INDEX))
}

pub fn get_canister_upgrade_log_memory() -> Memory {
    MEMORY_MANAGER.with_borrow_mut(|memory_manager| memory_manager.get(CANISTER_UPGRADE_LOG))
}

pub fn init_memory_manager() {
    MEMORY_MANAGER.with(|m| {
        *m.borrow_mut() = MemoryManager::init_with_bucket_size(DefaultMemoryImpl::default(), 1);
    })
}

I am trying to store three wasms corresponding to WasmType uploaded one at a time using upload_wasms method

@dsarlis I narrowed down the error and it seems like it’s only occurring if I have a Blob inside a StableBtreeMap

pub wasms: StableBTreeMap<WasmType, Blob<2000000>, Memory>,

When I used other type (a custom struct) it worked just fine.

@gravity_vi Thanks for the extra details. If you say it’s only happening when you use a blob that’s interesting information – could indicate a bug perhaps. I’ll loop in @ielashi here as he’s more proficient with stable structures than I am.

1 Like

@gravity_vi is it possible to share an end-to-end example where we can reproduce the error? Putting it together in a small repo would be very helpful to better triage what the problem could be.

@ielashi yep I will do that and share it here.

1 Like