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

WIP: Add support for uninterrupted delegatecall chains #10

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

forshtat
Copy link

ATTENTION: ERC-RELATED PULL REQUESTS NOW OCCUR IN ETHEREUM/ERCS

--

When opening a pull request to submit a new EIP, please use the suggested template: https://github.com/ethereum/EIPs/blob/master/eip-template.md

We have a GitHub bot that automatically merges some PRs. It will merge yours immediately if certain criteria are met:

  • The PR edits only existing draft PRs.
  • The build passes.
  • Your GitHub username or email address is listed in the 'author' header of all affected PRs, inside .
  • If matching on email address, the email address is the one publicly listed on your GitHub profile.

Copy link

github-actions bot commented Jan 9, 2025

The commit fafba65 (as a parent of 631f555) contains errors.
Please inspect the Run Summary for details.

@github-actions github-actions bot removed the w-ci label Jan 9, 2025
### Context variable for the `entry_point_role` value

During the execution of the `Sender`, `Paymaster` or a `Deployer` code as defined by the `AA_TX_TYPE` transaction,
the global `entry_point_role` variable is set to the corresponding role.
Copy link

Choose a reason for hiding this comment

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

How is this variable accessed from EVM? A new opcode?

Copy link
Author

Choose a reason for hiding this comment

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

This variable is not actually visible inside the EVM. It is used by the EVM implementation to decide which code section to enter when using DELEGATECALL.

Copy link

Choose a reason for hiding this comment

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

We could encode that variable into ORIGIN so instead of 0x7701000000000000000000000000000000007701 it would return 0x7701000000000000000000000000000000000000 | entry_point_role but I'm not sure if we should. May be useful if a function called by the role's code section needs to know the context. OTOH the code itself could pass it as an arg if it's needed.

If the specified contract does not contain such a section, or is not an EOF contract,
execution starts at code section 0, and pc is set to 0.

The transaction is considered invalid if it never reached a code section corresponding to the current `entry_point_role`
Copy link

Choose a reason for hiding this comment

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

I know the goal is to support legacy proxies but it seems risky:

  1. Suppose a contract that implements user-controlled callbacks (such as an ERC-1155 token that calls onERC1155BatchReceived) is called this way. An attacker could deploy a contract that implements the methodsig of onERC1155BatchReceived to call its own role_sender_validation and role_sender_execution sections which accept the transaction. Will this result in executing the attacker's call from the token contract?
  2. DoS resistance. Many proxies could point to the same implementation, which could then invalidate many mempool transactions. We have no way to associate the proxy with the implementation and ensure 1:1 relation. How do we mitigate this?

Copy link
Author

@forshtat forshtat Jan 10, 2025

Choose a reason for hiding this comment

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

Re 1: The the entry_point_role is only preserved (and can activate) through an uninterrupted chain of DELEGATECALLs. Calling the malicious contract with a regular CALL does not carry the entry_point_role, and the callbacks cannot authorize a transaction.

Re 2: As the implementation in this scenario is an EOF contract, which means it has no SELFDESTRUCT or any other way to modify its own code, and must be called with DELEGATECALL meaning its storage cannot be accessed, I don't see a way for the "owner" of the implementation contract to invalidate a transaction. Am I missing something?

Copy link

Choose a reason for hiding this comment

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

Re 2: Its storage can't be accessed, but environment opcodes can still be used, no?

Copy link

Choose a reason for hiding this comment

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

Got it. That makes sense. I previously misunderstood the uninterrupted chain of DELEGATECALLs part. Looks like it does solve the proxy case, neat.

Re 2: As the implementation in this scenario is an EOF contract, which means it has no SELFDESTRUCT or any other way to modify its own code,

Since it's a chain, a non-EOF link in the chain could break the chain and never reach an EOF contract. But your assumption still holds since there's no SELFDESTRUCT for an existing non-EOF contract either and it can't access its own storage. Therefore invalidation can only be done in the account itself, making it an O(n) attack. Seems fine.

Re 2: Its storage can't be accessed, but environment opcodes can still be used, no?

Since it can't use storage to divert the flow to a different implementation, the opcodes during offchain simulation and block inclusion are the same. Therefore the ERC-7562 rules can ensure that no banned opcodes are used.

### Accepting the transaction by `Sender` or `Paymaster`

In order for the `Sender` or `Paymaster` contracts to accept the transaction,
they must perform a `CALL` to the `AA_ORIGIN` address with the appropriate acceptance data as call data.
Copy link

Choose a reason for hiding this comment

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

Does the call have to be performed by the actual Sender / Paymaster, or just in its context? If it's the former then the above proxy-support method, then proxies won't work. If the latter, then the security concerns I wrote above apply.

Copy link
Author

Choose a reason for hiding this comment

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

I think supporting existing proxy contracts is essential, so the call must be made in the context of the Sender / Paymaster. As I replied, I don't think there is a security concern unless there is a DELEGATECALL to an untrusted contract, in which case the attack does not require messing with roles.

### Context variable for the `entry_point_role` value

During the execution of the `Sender`, `Paymaster` or a `Deployer` code as defined by the `AA_TX_TYPE` transaction,
the global `entry_point_role` variable is set to the corresponding role.
Copy link

Choose a reason for hiding this comment

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

We could encode that variable into ORIGIN so instead of 0x7701000000000000000000000000000000007701 it would return 0x7701000000000000000000000000000000000000 | entry_point_role but I'm not sure if we should. May be useful if a function called by the role's code section needs to know the context. OTOH the code itself could pass it as an arg if it's needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants