?名稱:
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合約:
contract Array is Test {mapping(address => UserInfo) public userInfo; // storagestruct 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 pointuser.rewardDebt = amount;}function fixedupdaterewardDebt(uint amount) public {UserInfo storage user = userInfo[msg.sender]; // storageuser.rewardDebt = amount;}
}
foundry測試合約;
// 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);
}