ink icon indicating copy to clipboard operation
ink copied to clipboard

Add compile-time check that prevents ink! messages with a `&self` receiver from mutating state via `ink_env`

Open davidsemakula opened this issue 10 months ago • 2 comments

Overview

Following Rust semantics, ink! messages with a &self receiver (i.e. an immutable self reference receiver) shouldn't be able to mutate state/storage because the storage type is the Self type. However, this is currently possible by calling functions from ink_env directly (e.g. ink_env::set_contract_storage, ink_env::transfer e.t.c).

See https://github.com/use-ink/ink/issues/1969 for additional details.

Motivation

  • Consistency with Rust semantics makes ink! contracts easier to formally verify with Rust verification tools
  • Enforcement of claimed guarantees and consistency with Rust semantics makes ink! contracts easier to audit
  • Producing verifiable builds of ink! contracts with messages which mutate state, presented as read-only operations in verifiable ink! ABI metadata, and thus in potentially user facing tools (like wallets and explorers) likely has security implications at the application level

Design

  • Require ink! build tools (i.e. cargo-contract) to set a rustc cfg setting/flag (i.e. --cfg 'ink_check="msg_mut"') that essentially advertises there ability check/enforce (at compile-time) that ink! messages with &self receivers don't call any ink_env functions that can mutate state (i.e. they'll emit an error otherwise during the check or build process)
  • Update the ink proc-macro to emit an compile-time error if (and only if) it detects a message with a &self receiver and the aforementioned cfg setting is NOT enabled/set (i.e. #[cfg(not(ink_check = "msg_mut"))] - see also)
  • Create a MIR check that finds ink! message functions with immutable self reference receivers and emits a compiler error if it finds any calls to any ink_env functions that mutate state/storage/environment (i.e. ink_env::set_contract_storage, ink_env::transfer, ink_env::emit_event e.t.c)
  • Run the MIR check in cargo-contract during "check" and "build" commands for ink! contracts using ink! version >= 6

Additional Considerations

The current implementation of ink! linting infrastructure adds about a ~2x build-time penalty because it re-compiles the ink! contract with a fixed version of the Rust nightly compiler. Potential solutions for reducing this build-time penalty will be explored as part of the fix(es) for this issue.

davidsemakula avatar Feb 18 '25 16:02 davidsemakula

@davidsemakula While I agree that this is a very important issue, I've taken the liberty of marking it "Optional" instead of "Required" for v6. I think it's an involved issue and we shouldn't block the release on it. Lmk if you disagree!

cmichi avatar Oct 29 '25 15:10 cmichi

@cmichi We already gave up on landing this in v6 due to resource constraints, I just forgot to update its tags on the board. So marking it optional is fine.

davidsemakula avatar Oct 29 '25 16:10 davidsemakula