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