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
IIP-2: Limit contracts with a wildcard selector to one other message #1676
Comments
Closing issue as completed by #1708 |
Why is the proxy contract only allowed one other function? In our proxy contract we have several useful functions (below) that allow us to withdraw from the proxy, set the code hash of the proxy, terminate the proxy, etc.. Are we to handle these in one giant function now? |
I get it now. We have one public dispatching function that calls private functions that implement withdraw, set_code_hash, terminate, etc. Makes sense! |
Precisely: define an enum with variants that can then dispatch to the other private fns. As I said it is a tradeoff, but I think a minor inconvenience to close the vulnerability, and most contracts will not be proxy contracts with a wildcard. |
Thanks for confirming. I've made the changes in our proxy contract and got nearly all of the tests passing using the new method. There is one bug in that terminate now fails during testing.
I know terminate is meant to panic but it seems the introduction of the function dispatch enum and the closure has confused |
Abstract
Wildcard selectors used in proxy patterns are vulnerable to malicious or accidental selector clashing, causing the
Proxy
contract to intercept a message intended for aLogic
contract. This proposal aims to eliminate this vulnerability by restricting anink!
contract with a wildcard selector to only having one other message with a well-known reserved selector.Motivation
Wildcard selectors can be used to annotate a so called "fallback" function which handles any message with a selector which does not match any of the other selectors in an
ink!
smart contract. These can be used to implement common proxy patterns for contract upgradeability. Users send messages to aProxy
contract which forwards/delegates messages to aLogic
contract via the fallback message handler e.g.However this introduces a vulnerability whereby the
Proxy
contract may define a message with a selector identical to one from theLogic
contract. In this case theProxy
would handle the message rather than it being passed on to theLogic
contract. This is known as Proxy Selector Clashing, described in detail in this OpenZeppelin post.Because the
Logic
contract can be upgraded dynamically, there is no way to guarantee at compile time whether a selector clash exists.Specification
The proposal is to restrict contracts with a wildcard selector to only have one other message with a reserved/well known selector. This guarantees that there are no selector clashes, either by chance or malicious intent, and that the
Proxy
will only handle messages intended for it.If a contract uses a wildcard selector
#[ink(message, payable, selector = _)]
it MAY define one other message. This message MUST use the reserved selector::ink::IIP2_HANDLER_SELECTOR
. This selector MUST only be used in conjunction with a wildcard selector.Considerations
The goal is to introduce this restriction in order to eliminate the selector clashing vulnerability, but at the same time retain enough flexibility for contract authors to use the wildcard selector for different proxy patterns and any other usage.
The tradeoff is that contracts using wildcard selectors won't look exactly like idiomatic
ink!
contracts, because the other non-handler message will have to implement it's own inner dispatch logic most likely using an enum (see example).Rationale
By constraining an
ink!
contract using a wildcard selector to only having a single well known selector, we can provide compile-time guarantees for that aProxy
andLogic
contract written inink!
will not have clashing selectors.The text was updated successfully, but these errors were encountered: