名称:
memory滥用
https://github.com/XuHugo/solidityproject/tree/master/vulnerable-defi
描述:
在合约函数中滥用storage和memory。
memory是一个关键字,用于临时存储执行合约所需的数据。它保存函数的参数数据,并在执行后清除。
storage可以看作是默认的数据存储。它持久地保存数据,消耗更多的gas。
函数updaterewardDebt的功能是,更新UserInfo结构体的rewardDebt值。为了节约gas,我们将变量用关键字memory声明了,这样会导致的问题是,在函数执行结束之后,rewardDebt的值并不会保存下来。因为一旦函数完成执行,内存就会被清除,所做的更改也会丢失。
参考:
Cover protocol hack analysis: Infinite Cover tokens minted via an exploit - Mudit Gupta's Blog
解决方法:
https://mudit.blog/cover-protocol-hack-analysis-tokens-minted-exploit/
proxy合约:
javascript
contract Array is Test {
mapping(address => UserInfo) public userInfo; // storage
struct UserInfo {
uint256 amount; // How many tokens got staked by user.
uint256 rewardDebt; // Reward debt. See Explanation below.
}
function updaterewardDebt(uint amount) public {
UserInfo memory user = userInfo[msg.sender]; // memory, vulnerable point
user.rewardDebt = amount;
}
function fixedupdaterewardDebt(uint amount) public {
UserInfo storage user = userInfo[msg.sender]; // storage
user.rewardDebt = amount;
}
}
foundry测试合约;
javascript
// A function to demonstrate the difference between memory and storage data locations in Solidity.
function testDataLocation() public {
// Simulate dealing 1 ether to Alice and Bob.
address alice = vm.addr(1);
address bob = vm.addr(2);
vm.deal(address(alice), 1 ether);
vm.deal(address(bob), 1 ether);
// Create a new instance of the Array contract.
ArrayContract = new Array();
// Update the rewardDebt storage variable in the Array contract to 100.
ArrayContract.updaterewardDebt(100);
// Retrieve the userInfo struct for the contract's address and print the rewardDebt variable.
// Note that the rewardDebt should still be the initial value, as updaterewardDebt operates on a memory variable, not the storage one.
(uint amount, uint rewardDebt) = ArrayContract.userInfo(address(this));
console.log("Non-updated rewardDebt", rewardDebt);
// Print a message.
console.log("Update rewardDebt with storage");
// Now use the fixedupdaterewardDebt function, which correctly updates the storage variable.
ArrayContract.fixedupdaterewardDebt(100);
// Retrieve the userInfo struct again, and print the rewardDebt variable.
// This time the rewardDebt should be updated to 100.
(uint newamount, uint newrewardDebt) = ArrayContract.userInfo(
address(this)
);
console.log("Updated rewardDebt", newrewardDebt);
}