Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

### Fixes

- Storage slot types are now validated against the supported set at account creation, and the delta commitment rejects an unrecognized slot type instead of treating it as a map ([#3598](https://github.com/0xMiden/protocol/issues/3598)).
- Documented that `authority::assert_authorized` is a no-op under `Authority::AuthControlled` ([#3500](https://github.com/0xMiden/protocol/pull/3500)).
- Fixed `PrivateOutputNote` construction and deserialization accepting attachment data that is not committed by the note header ([#3556](https://github.com/0xMiden/protocol/issues/3556)).
- Fixed the authentication procedure not ending up at index 0 of an account's code when its MAST root was already exported by another component ([#3566](https://github.com/0xMiden/protocol/pull/3566)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const ERR_ACCOUNT_PROC_NOT_AUTH_PROC =
const ERR_ACCOUNT_STORAGE_SLOTS_MUST_BE_SORTED_AND_UNIQUE =
"slot IDs must be unique and sorted in ascending order"

const ERR_ACCOUNT_STORAGE_SLOT_TYPE_IS_INVALID =
"account storage slot has an unsupported type"

const ERR_ACCOUNT_PROCEDURES_MUST_BE_SORTED_AND_UNIQUE =
"account procedures following the authentication procedure must be unique and sorted in ascending order"

Expand Down Expand Up @@ -937,15 +940,20 @@ pub proc validate_seed
# => []
end

#! Validates that slot IDs are sorted in ascending order and that slot IDs are unique.
#! Validates that slot IDs are sorted in ascending order, that slot IDs are unique, and that every
#! slot's type belongs to the set of supported types (value or map).
#!
#! Inputs: []
#! Outputs: []
#!
#! Pancis if:
Comment thread
mmagician marked this conversation as resolved.
Outdated
#! - each slot's ID is not strictly less than the next slot's ID.
#! - this ensures sorting and uniqueness among slot IDs.
#! - any slot has a type that is not a supported storage slot type.
pub proc validate_storage
exec.validate_storage_slot_types
Comment thread
mmagician marked this conversation as resolved.
Outdated
# => []

exec.memory::get_num_storage_slots
# => [num_slots]

Expand Down Expand Up @@ -997,6 +1005,46 @@ pub proc validate_storage
# => []
end

#! Validates that every storage slot has a supported type (value or map).
#!
#! Unlike the sort check, this loop runs for every slot, including single-slot accounts.
#!
#! Inputs: []
#! Outputs: []
#!
#! Panics if:
#! - any slot has a type that is not a supported storage slot type.
proc validate_storage_slot_types
exec.memory::get_num_storage_slots
# => [num_slots]

dup neq.0
# => [should_loop, num_slots]

while.true
# first iteration: remaining = num_slots, slot index = remaining - 1
# => [remaining]

sub.1
# => [slot_idx]

dup exec.get_native_storage_slot_type
# => [slot_type, slot_idx]

# assert the slot type is value or map
dup eq.STORAGE_SLOT_TYPE_VALUE swap eq.STORAGE_SLOT_TYPE_MAP or
assert.err=ERR_ACCOUNT_STORAGE_SLOT_TYPE_IS_INVALID
# => [slot_idx]

dup neq.0
# => [should_continue, slot_idx]
end
# => [slot_idx]

drop
# => []
end

#! Returns 1 if the previous slot ID is smaller than the current slot ID, 0 otherwise.
#!
#! In the slot ID comparison, the prefix takes precedence over the suffix.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use miden::tx_kernel_core::memory
use miden::core::crypto::hashes::poseidon2
use miden::core::word
use {ASSET_SIZE, COMPOSITION_NONE} from miden::tx_kernel_core::asset
use {STORAGE_SLOT_TYPE_VALUE} from miden::tx_kernel_core::constants
use {STORAGE_SLOT_TYPE_MAP, STORAGE_SLOT_TYPE_VALUE} from miden::tx_kernel_core::constants
use {ACCOUNT_UPDATE_ASSET_PTR} from miden::tx_kernel_core::memory

# ERRORS
Expand All @@ -20,6 +20,9 @@ const ERR_ACCOUNT_PATCH_NONCE_MUST_BE_INCREMENTED_IF_VAULT_OR_STORAGE_CHANGED =
const ERR_ACCOUNT_DELTA_TOO_MANY_REMOVED_ASSETS =
"number of removed assets in the transaction exceeds the maximum limit of 1024"

const ERR_ACCOUNT_STORAGE_SLOT_TYPE_IS_INVALID =
"account storage slot has an unsupported type"

# EVENTS
# =================================================================================================

Expand Down Expand Up @@ -275,12 +278,19 @@ proc commit_slot_patch
# => [storage_slot_type, slot_idx, RATE0, RATE1, CAPACITY]

# check if slot is of type value
push.STORAGE_SLOT_TYPE_VALUE eq
# => [is_value_slot_type, slot_idx, RATE0, RATE1, CAPACITY]
dup push.STORAGE_SLOT_TYPE_VALUE eq
# => [is_value_slot_type, storage_slot_type, slot_idx, RATE0, RATE1, CAPACITY]

if.true
drop
# => [slot_idx, RATE0, RATE1, CAPACITY]

exec.commit_value_slot_patch
else
# reject any type that is neither value nor map instead of defaulting to map
push.STORAGE_SLOT_TYPE_MAP eq assert.err=ERR_ACCOUNT_STORAGE_SLOT_TYPE_IS_INVALID
# => [slot_idx, RATE0, RATE1, CAPACITY]

exec.commit_map_slot_patch
end
# => [RATE0, RATE1, CAPACITY]
Expand Down
31 changes: 31 additions & 0 deletions crates/miden-testing/src/kernel_tests/tx/test_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use miden_protocol::errors::tx_kernel::{
ERR_ACCOUNT_NONCE_AT_MAX,
ERR_ACCOUNT_NONCE_CAN_ONLY_BE_INCREMENTED_ONCE,
ERR_ACCOUNT_PROCEDURES_MUST_BE_SORTED_AND_UNIQUE,
ERR_ACCOUNT_STORAGE_SLOT_TYPE_IS_INVALID,
ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME,
};
use miden_protocol::field::PrimeField64;
Expand Down Expand Up @@ -594,6 +595,36 @@ async fn test_get_native_storage_slot_type() -> anyhow::Result<()> {
Ok(())
}

/// Tests that `validate_storage` rejects a storage slot whose type is outside the supported set
/// (value or map) instead of silently committing it as a map (audit finding L-11).
#[tokio::test]
async fn validate_storage_rejects_unsupported_slot_type() -> anyhow::Result<()> {
let mock_tx = TestTransactionBuilder::with_existing_mock_account().build().unwrap();

// Overwrite the type element (offset 1) of the first storage slot with an unsupported type,
// then run the new-account storage validation which must reject it.
let code = "
use miden::tx_kernel_core::account
use miden::tx_kernel_core::memory
use miden::tx_kernel_core::prologue

begin
exec.prologue::prepare_transaction

push.2
exec.memory::get_native_account_active_storage_slots_ptr add.1
mem_store

exec.account::validate_storage
end
";

let exec_output = mock_tx.execute_code(code).await;
assert_execution_error!(exec_output, ERR_ACCOUNT_STORAGE_SLOT_TYPE_IS_INVALID);

Ok(())
}

/// Tests that accessing an unknown slot fails with the expected error message.
///
/// This tests both accounts with empty storage and non-empty storage.
Expand Down
Loading