🐞 [Bug]: Role enumeration counter-derived storage keys cause footprint invalidation under concurrency

Aperta
#752 3 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
35/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Tranquilla
Stack tecnologico
rust
Ambito
blockchain

Direzione di ricerca

Inizia in packages/access/src/access_control/storage.rs, in add_to_role_enumeration, quindi segui i suoi chiamanti a partire da grant_role. Riproduci la race condition con due chiamate concorrenti a grant_role usando account di canale e analizza le tracce della simulazione e dell'esecuzione. Il lavoro è completato quando i grant concorrenti sullo stesso ruolo non vanno più in trap, perché le chiavi di scrittura sono deterministiche rispetto agli input della funzione.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

What happened?

add_to_role_enumeration in packages/access/src/access_control/storage.rs derives write keys from a shared mutable counter:

let count = e.storage().persistent().get(&count_key).unwrap_or(0);
let new_key = AccessControlStorageKey::RoleAccounts(RoleAccountKey { role, index: count });
e.storage().persistent().set(&new_key, account);

On Soroban, transaction footprints must declare exact storage keys at simulation time. If RoleAccountsCount advances between simulation and execution (another grant_role lands first), the contract writes to a key not in the footprint and the transaction traps:

"trying to access contract data key outside of the footprint"
["RoleAccounts", {"index": 7044, "role": "WLISTED"}]

Any two concurrent grant_role calls on the same role will race on the counter. Observed on Mainnet with a role containing ~7000 members.

Reproduction: two concurrent grant_role(env, caller, user_a, role) and grant_role(env, caller, user_b, role) (sent with channel accounts to have concurrency) — both simulate with count = N and both declare RoleAccounts({role, N}) in their write footprint. First lands and increments the counter to N+1. Second executes, reads the now-updated count = N+1, and tries to write RoleAccounts({role, N+1}) which is not in its declared footprint.

Expected behavior

grant_role should be safe to call concurrently on the same role. Storage keys in the write path should be deterministic from the function inputs.

Lingua principale
Rust
Stelle
97
Fork
75
Merge medio
1g 1h
PR unite (30g)
8

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di OpenZeppelin/stellar-contracts

Tutte le issue di OpenZeppelin/stellar-contracts

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.