Skip to content

Latest commit

 

History

History
88 lines (63 loc) · 2.92 KB

File metadata and controls

88 lines (63 loc) · 2.92 KB

S005 — Storage Key Collision

  • Category: storage_keys
  • Severity: Medium
  • Rule name: storage_collision

What it detects

S005 walks storage accesses and tracks the literal keys used per storage tier (instance, persistent, temporary). When the same key value is reused for different data types within the same tier, it reports a potential collision. It distinguishes keys by their (storage_type, key_value) pair and flags reuse across distinct value types or distinct data domains.

Why it matters

A storage key uniquely identifies a ledger entry within a tier. If two unrelated pieces of data are written under the same key, one write silently overwrites the other, and a later read deserializes bytes that were never meant for it. The result is cross-feature data corruption that is hard to reproduce and easy to miss in review.

Vulnerable example

#![no_std]
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env};

#[contract]
pub struct Vault;

#[contractimpl]
impl Vault {
    pub fn set_owner(env: Env, owner: Address) {
        // S005: "data" reused for an Address ...
        env.storage().instance().set(&symbol_short!("data"), &owner);
    }

    pub fn set_total(env: Env, total: i128) {
        // ... and here reused for an i128 — same key, different type.
        env.storage().instance().set(&symbol_short!("data"), &total);
    }
}

Safe example

#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

#[contracttype]
pub enum DataKey {
    Owner,
    Total,
}

#[contract]
pub struct Vault;

#[contractimpl]
impl Vault {
    pub fn set_owner(env: Env, owner: Address) {
        // Distinct, typed key variant per data domain.
        env.storage().instance().set(&DataKey::Owner, &owner);
    }

    pub fn set_total(env: Env, total: i128) {
        env.storage().instance().set(&DataKey::Total, &total);
    }
}

CVSS-style risk rating

  • Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:L
  • Base score: 5.9
  • Rating: Medium

Triggering a meaningful collision generally requires a specific call ordering (higher complexity), but the consequence — silent overwrite of unrelated state — is a high integrity impact.

How to fix

  1. Use a single typed key enum (#[contracttype] enum DataKey { ... }) so each data domain has its own variant.
  2. Give each domain a unique prefix; avoid reusing the same symbol_short! / Symbol for different value types.
  3. Never key two different types off the same literal within one storage tier.

Related rules

Related rules: S004, S001

References