Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sequencer)!: fix TOCTOU issues by merging check and execution #1332

Merged
merged 13 commits into from
Aug 20, 2024

Conversation

SuperFluffy
Copy link
Member

@SuperFluffy SuperFluffy commented Aug 1, 2024

Summary

Introduces ActionHandler::check_and_execute, replacing ActionHandler::check_stateful and ActionHandler::execute.

Background

Zelic found that separating execution into check_stateful and execute lead to time-of-check-vs-time-of-use risks: while check_stateful for two actions A and B might each pass, the execution of action A might change the state such that a subsequent execution of action B would now fail - or worse, lead to invalid state.

This patch follows Penumbra (see issues linked at the bottom) in merging them into one atomic operation (atomic in the sense that <Action>::check_and_execute are run sequentially).

There is also a ActionHandler::check_historic trait method, which however is currently not used. It is left for future work to go through the individual checks and move them to check_historic, where applicable (for example, checking address prefixes as long as changing these is not possible after chain init).

Changes

  • change ActionHandler trait to merge check_stateful and execute into check_and_execute.
  • inject a transaction's signer into the ephemeral object store, setting before and after a transaction is executed. Necessary because this follows the cnidarium_component::ActionHandler convention, but also allows simplifying
  • remove the notion of bech32m addresses from many state writes and reads: the prefix only matters at the boundary, not inside the system

Testing

All tests were updated and pass.

NOTE: a useful test would be to craft a problematic transaction that would be rejected with the newest change. However, crafting and executing such a transaction so that it is rejected by the current sequencer but leads to incorrect is left to a follow-up.

Breaking Changelist

While no snapshots guarding against breaking app state were triggered, this is still a breaking change: if supplied with a problematic payload, a sequencer node without this patch will reach a different (and invalid) state compared a node with the present patch.

Related Issues

Original Penumbra issues and fix:
penumbra-zone/penumbra#3960
penumbra-zone/penumbra#3960

Closes #1318

@SuperFluffy SuperFluffy added the ENG-674 Zelic's July 15 audit label Aug 1, 2024
@github-actions github-actions bot added the sequencer pertaining to the astria-sequencer crate label Aug 1, 2024
@SuperFluffy SuperFluffy marked this pull request as ready for review August 1, 2024 20:56
@SuperFluffy SuperFluffy requested a review from a team as a code owner August 1, 2024 20:56
Copy link
Contributor

@Fraser999 Fraser999 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any blocking issues with the changes in terms of moving the checks and executions around. I have a few nitpick comments, but I'd be really keen to see the new transaction::State[Read|Write]Ext removed.

crates/astria-sequencer/src/app/action_handler.rs Outdated Show resolved Hide resolved
crates/astria-sequencer/src/app/action_handler.rs Outdated Show resolved Hide resolved
crates/astria-sequencer/src/app/action_handler.rs Outdated Show resolved Hide resolved
// }
// ```

async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> anyhow::Result<()>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly think we should follow the previous style in ActionHandler::execute where the from address is passed directly into the method.

It would mean that we couldn't implement ActionHandler on SignedTransaction, but that's far outweighed by the benefit of not using the StateExt's ephemeral store for passing around the address.

My biggest concern is that we're calling get_current_source().expect() in many places now, and while I think they're all currently safe to unwrap, I guess that might not always be the case. By just passing the address, we avoid the risk of panicking altogether.

There's also a performance hit: every time we read from the ephemeral cache, at least one lock is acquired, and more if the cache entry is nested down in a lower layer of StateDelta.

Finally, there's also the cost in terms of code complexity - it seems simpler to read and follow the code where the from address is just passed in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline: leaving this in to not change the entire fix. We will rethink and rework the construction of this trait, keeping in mind cache and mempool access.

crates/astria-sequencer/src/app/mod.rs Outdated Show resolved Hide resolved
crates/astria-sequencer/src/app/mod.rs Outdated Show resolved Hide resolved
Co-authored-by: Fraser Hutchison <190532+Fraser999@users.noreply.github.com>
Copy link
Contributor

@Fraser999 Fraser999 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion, approving pending resolution of the comments in follow-up discussions or PR.

@SuperFluffy SuperFluffy added this pull request to the merge queue Aug 20, 2024
Merged via the queue into main with commit 9f959f4 Aug 20, 2024
42 checks passed
@SuperFluffy SuperFluffy deleted the gh-1318 branch August 20, 2024 11:42
steezeburger added a commit that referenced this pull request Aug 22, 2024
* main:
  refactor(core, proto)!: define app genesis state in proto (#1346)
  fix(sequencer): bump penumbra dep to fix ibc state access bug (#1389)
  feat(conductor)!: support disabled celestia auth (#1372)
  fix(sequencer)!: fix block fees (#1343)
  perf(sequencer): add benchmark for prepare_proposal (ENG-660) (#1337)
  fix(proto): fix import name mismatch (#1380)
  fix(ci): enable bridge withdrawer building with tag (#1374)
  feat(sequencer): rewrite memool to have per-account transaction storage and maintenance  (#1323)
  refactor(core, sequencer)!: require that bridge unlock address always be set (#1339)
  fix(sequencer)!: take funds from bridge in ics20 withdrawals (#1344)
  fix(sequencer)!: fix TOCTOU issues by merging check and execution (#1332)
  fix: abci error code (#1280)
  refactor(core): shorten `try_from_block_info_and_data()` (#1371)
  fix(relayer): change `reqwest` for `isahc` in relayer blackbox tests (ENG-699) (#1366)
  fix(conductor): update for celestia-node v0.15.0 (#1367)
  Chore: Upgrade celestia-node to v0.14.1 (#1360)
  chore(charts): fix charts production templates (#1359)
  chore(core, proto): migrate byte slices from Vec to Bytes (#1319)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ENG-674 Zelic's July 15 audit sequencer pertaining to the astria-sequencer crate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sequencer suffers from TOCTOU issues
2 participants