Summary
HorseStore.huff Incorrect implementation of the horseIdToFedTimeStamp mapping
Vulnerability Details
According to the specification, the storage of mapping values in the EVM should be at keccak256(key, slot), However, the implemented method is STORE_ELEMENT(), which stores the value to the slot keccak256(abi.encode(key)).
Add this test to Base_Test.t.sol and run forge test --mt testSlot -vvvv to validate the issue
function testSlot() public {
uint timestamp = 1705152551;
vm.warp(timestamp);
vm.prank(user);
for (uint256 index; index < 10; index++) {
horseStore.feedHorse(index);
bytes32 value = vm.load(address(horseStore), keccak256(abi.encode(index)));
assertEq(value, bytes32(timestamp));
}
Impact
May lead to unexpected storage conflicts, as the storage slot calculation for Dynamically-Sized Arrays is keccak256(abi.encode(slot)) + index
Tools Used
manual inspection
Recommendations
Make the following changes:
line 49 add:
/* Storage Slots */
#define constant TOTAL_SUPPLY = FREE_STORAGE_POINTER()
#define constant OWNER_LOCATION = FREE_STORAGE_POINTER()
#define constant BALANCE_LOCATION = FREE_STORAGE_POINTER()
#define constant SINGLE_APPROVAL_LOCATION = FREE_STORAGE_POINTER()
+ #define constant HORSE_FED_TIMESTAMP_LOCATION = FREE_STORAGE_POINTER()
Modify line 83
#define macro FEED_HORSE() = takes (0) returns (0) {
timestamp // [timestamp]
0x04 calldataload // [horseId, timestamp]
- STORE_ELEMENT(0x00) // []
+ [HORSE_FED_TIMESTAMP_LOCATION] STORE_ELEMENT_FROM_KEYS(0x00) // []
// End execution
0x11 timestamp mod
endFeed jumpi
revert
endFeed:
stop
}
Modify line 66
#define macro GET_HORSE_FED_TIMESTAMP() = takes (0) returns (0) {
0x04 calldataload // [horseId]
- GET_SLOT_FROM_KEY(0x00) // [horseFedTimestamp]
+ [HORSE_FED_TIMESTAMP_LOCATION] LOAD_ELEMENT_FROM_KEYS(0x00) // [horseFedTimestamp]
sload // []
0x00 mstore // [] Store value in memory.
0x20 0x00 return // Returns what' sin memory
}
Modify line 99
#define macro IS_HAPPY_HORSE() = takes (0) returns (0) {
0x04 calldataload // [horseId]
LOAD_ELEMENT(0x00) // [horseFedTimestamp]
timestamp // [timestamp, horseFedTimestamp]
dup2 dup2 // [timestamp, horseFedTimestamp, timestamp, horseFedTimestamp]
sub // [timestamp - horseFedTimestamp, timestamp, horseFedTimestamp]
- [HORSE_HAPPY_IF_FED_WITHIN_CONST] // [HORSE_HAPPY_IF_FED_WITHIN, timestamp - horseFedTimestamp, timestamp, horseFedTimestamp]
+ [HORSE_FED_TIMESTAMP_LOCATION] LOAD_ELEMENT_FROM_KEYS(0x00) // [HORSE_HAPPY_IF_FED_WITHIN, timestamp - horseFedTimestamp, timestamp, horseFedTimestamp]
lt // [HORSE_HAPPY_IF_FED_WITHIN < timestamp - horseFedTimestamp, timestamp, horseFedTimestamp]
start_return_true jumpi // [timestamp, horseFedTimestamp]
eq // [timestamp == horseFedTimestamp]
start_return
jump