Both the contracts of Position and Trading may not work correctly.
Proof of Concept
The Position.sol#mint calls _safeMint will trigger a _checkOnERC721Received callback, which can be used to reenter.
Crackers can use this vulnerability to attack the protocol.
For example, the cracker could send a reentrant attack tx with a call stack like this:
cracker calls his own contract Cx.
in Cx: calls Trading.sol#initiateMarketOrder
in Trading: calls Position.sol#mint
in Position: calls Cx#_checkOnERC721Received
in Cx: calls Trading.sol#liquidatePosition (reenter here)
in Trading: calls Position.sol#burn
in Cx: calls Cx#initiateLimitOrder
in Trading: calls Position.sol#mint
in Position: calls Cx#_checkOnERC721Received
In step 6, it burned the Position NFT that created in step 3, and the state will be updated in a confusing way because the burnt NFT hasn't done state initialization in step 3 yet.
In step 8, it created another Position NFT with the same tokenId as the one in step 3, which is also an anomaly.
After step 9, when the stack returns to step 3, the burnt NFT will now go on the state initialization.
Lines of code
https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/Position.sol#L131-L161
Vulnerability details
Impact
Both the contracts of Position and Trading may not work correctly.
Proof of Concept
The Position.sol#mint calls
_safeMint
will trigger a_checkOnERC721Received
callback, which can be used to reenter.Crackers can use this vulnerability to attack the protocol.
For example, the cracker could send a reentrant attack tx with a call stack like this:
Trading.sol#initiateMarketOrder
Position.sol#mint
Cx#_checkOnERC721Received
Trading.sol#liquidatePosition
(reenter here)Position.sol#burn
Cx#initiateLimitOrder
Position.sol#mint
Cx#_checkOnERC721Received
In step 6, it burned the Position NFT that created in step 3, and the state will be updated in a confusing way because the burnt NFT hasn't done state initialization in step 3 yet. In step 8, it created another Position NFT with the same tokenId as the one in step 3, which is also an anomaly. After step 9, when the stack returns to step 3, the burnt NFT will now go on the state initialization.
Tools Used
Manual
Recommended Mitigation Steps
Simplest solution: replace
_safeMint
with_mint
.