-
Notifications
You must be signed in to change notification settings - Fork 78
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
Conversation
There was a problem hiding this 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.
// } | ||
// ``` | ||
|
||
async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> anyhow::Result<()>; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Co-authored-by: Fraser Hutchison <[email protected]>
There was a problem hiding this 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.
* 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)
Summary
Introduces
ActionHandler::check_and_execute
, replacingActionHandler::check_stateful
andActionHandler::execute
.Background
Zelic found that separating execution into
check_stateful
andexecute
lead to time-of-check-vs-time-of-use risks: whilecheck_stateful
for two actionsA
andB
might each pass, the execution of actionA
might change the state such that a subsequent execution of actionB
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 tocheck_historic
, where applicable (for example, checking address prefixes as long as changing these is not possible after chain init).Changes
ActionHandler
trait to mergecheck_stateful
andexecute
intocheck_and_execute
.cnidarium_component::ActionHandler
convention, but also allows simplifyingTesting
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