Skip to content

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

Merged
frangio merged 9 commits intoOpenZeppelin:masterfrom
Amxx:fix/erc721/beforeHookOrdering
Aug 19, 2022
Merged

Change execution order to avoid reentry through the _beforeTokenTransfer hook#3611
frangio merged 9 commits intoOpenZeppelin:masterfrom
Amxx:fix/erc721/beforeHookOrdering

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
Amxx and others added 4 commits August 17, 2022 12:55
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
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.

2 participants