Summary
The functionality of the fallback
function in the Huff version differs from that of the fallback
function in the Solidity version.
Vulnerability Details
There is no fallback function in the Solidity version, while in the Huff version, the totalSupply
function is a fallback function.
Impact
It is important to note that third-party contracts may depend on the success of the execution of the fallback
function or the values returned by it, which could lead to unexpected results if the implementation of the fallback
function differs between the Solidity and Huff versions. Therefore, it is necessary to ensure that the fallback
function is implemented consistently across all versions to prevent any potential issues.
Proof of Concept (PoC)
Add the next test in HorseStoreSolidity.t.sol
.
function test_Fallback() public {
(bool success, ) = address(horseStore).call("");
assertFalse(success);
}
Run a test with forge test --mt test_Fallback
.
Add the next test in HorseStoreHuff.t.sol
.
function test_Fallback() public {
for (uint256 i = 1; i <= 10; ++i) {
vm.prank(user);
horseStore.mintHorse();
uint256 totalSupply = horseStore.totalSupply();
assertEq(totalSupply, i);
(bool success, bytes memory data) = address(horseStore).call("");
assertTrue(success);
uint256 fallbackData = abi.decode(data, (uint256));
assertEq(fallbackData, totalSupply);
}
}
Run a test with forge test --mt test_Fallback
.
Tools Used
Recommendations
Recommended changes to HorseStore.huff::MAIN()
function to be reverted if no valid function selector is found:
#define macro MAIN() = takes (0) returns (0) {
// Identify which function is being called.
0x00 calldataload 0xE0 shr
dup1 __FUNC_SIG(totalSupply) eq totalSupply jumpi
dup1 __FUNC_SIG(feedHorse) eq feedHorse jumpi
dup1 __FUNC_SIG(isHappyHorse) eq isHappyHorse jumpi
dup1 __FUNC_SIG(horseIdToFedTimeStamp) eq horseIdToFedTimeStamp jumpi
dup1 __FUNC_SIG(mintHorse) eq mintHorse jumpi
dup1 __FUNC_SIG(HORSE_HAPPY_IF_FED_WITHIN) eq horseHappyIfFedWithin jumpi
dup1 __FUNC_SIG(approve) eq approve jumpi
dup1 __FUNC_SIG(setApprovalForAll) eq setApprovalForAll jumpi
dup1 __FUNC_SIG(transferFrom) eq transferFrom jumpi
dup1 __FUNC_SIG(name) eq name jumpi
dup1 __FUNC_SIG(symbol) eq symbol jumpi
dup1 __FUNC_SIG(tokenURI) eq tokenURI jumpi
dup1 __FUNC_SIG(supportsInterface)eq supportsInterface jumpi
dup1 __FUNC_SIG(getApproved) eq getApproved jumpi
dup1 __FUNC_SIG(isApprovedForAll) eq isApprovedForAll jumpi
dup1 __FUNC_SIG(balanceOf) eq balanceOf jumpi
dup1 __FUNC_SIG(ownerOf)eq ownerOf jumpi
+ // Revert if no match is found.
+ 0x00 0x00 revert
totalSupply:
GET_TOTAL_SUPPLY()
feedHorse:
FEED_HORSE()
isHappyHorse:
IS_HAPPY_HORSE()
mintHorse:
MINT_HORSE()
horseIdToFedTimeStamp:
GET_HORSE_FED_TIMESTAMP()
horseHappyIfFedWithin:
HORSE_HAPPY_IF_FED_WITHIN()
approve:
APPROVE()
setApprovalForAll:
SET_APPROVAL_FOR_ALL()
transferFrom:
TRANSFER_FROM()
name:
NAME()
symbol:
SYMBOL()
tokenURI:
TOKEN_URI()
supportsInterface:
SUPPORTS_INTERFACE()
getApproved:
GET_APPROVED()
isApprovedForAll:
IS_APPROVED_FOR_ALL()
balanceOf:
BALANCE_OF()
ownerOf:
OWNER_OF()
MINT_HORSE()
}
Add the next test in HorseStoreHuff.t.sol
.
function test_FallbackNotExist() public {
(bool success, ) = address(horseStore).call("");
assertFalse(success);
}
Run a test with forge test --mt test_FallbackNotExist
.