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

Change execution order to avoid reentry through the _beforeTokenTransfer hook #3611

Merged
merged 9 commits into from Aug 19, 2022

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Aug 11, 2022

If a developer was to transfer/burn/mint the token during the _beforeTokenTransfer, that would currently not be catched by checks, resulting in invalid event being emitted, and corrupted balances.

This could in particular affect a voting instance.

This risk could become serious if the _beforeTokenTransfer hook was to perform an external call that could reenter!

We eliminate this risk by applying strict "check→effect" logic to the _mint, _transfer and _burn function, at the risk of being more gas expensive. For _mint and _transfer, the increase in gas cost only affect reverting transaction (the revert latter). For _burn, an addition hot-sload (100gas) is needed, making all burns more exepensive. Hopefully burn is not as widelly and frequently used as mint and transfer.

IMO this is one more reason to drop these hooks when possible (5.0)

PR Checklist

  • Changelog entry

@Amxx Amxx added this to the 4.8 milestone Aug 11, 2022
@Amxx Amxx requested a review from frangio August 11, 2022 16:59
CHANGELOG.md Outdated Show resolved Hide resolved
@@ -333,11 +336,11 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
address to,
uint256 tokenId
) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
Copy link
Contributor

@frangio frangio Aug 17, 2022

Choose a reason for hiding this comment

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

It is wrong to remove this from here... We need to check that from is the owner at the time of the transfer, don't we? We should do the same we did in _burn, and re-read the owner after the hook...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My impression was that it would be checked after the hook, and that would revert in the owner is not correct.

The risk is that the hook would be executed with an invalid from, and the revert would come after.

There is a world in which the from is not valid, the before hook move the token from its current owner to the from, and then the test passes and we do what we have to do... The code would have to be really f****d up, but its possible.

My concern is that doing the test twice increase transfer cost by >100 gas (on probably the most common and scrutinized operation)

Copy link
Contributor

@frangio frangio left a comment

Choose a reason for hiding this comment

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

LGTM

@frangio frangio merged commit 98c3a79 into OpenZeppelin:master Aug 19, 2022
@Amxx Amxx deleted the fix/erc721/beforeHookOrdering branch August 19, 2022 15:58
ronhuafeng added a commit to ronhuafeng/openzeppelin-contracts that referenced this pull request Sep 9, 2022
…fer hook (OpenZeppelin#3611)

Co-authored-by: Francisco <frangio.1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants