The new security alert Security Alert – Solidity – Variables can be overwritten in storage warns that variables can overwrite other variables in storage. From the security alert:
Storage variables that are smaller than 256 bits are packed together into the same 256 bit slot if they can fit. If a value larger than what is allowed by the type is assigned to the first variable, that value will overwrite the second variable.
This means if an attacker can cause an overflow in the value of the first variable, then the second variable can be modified. Creating an overflow in the first variable is possible using arithmetics or by directly passing in a value from the call data (values in call data are aligned to 32 bytes, and padding is neither verified nor enforced).
Contracts that only use the types listed below for state variables are not affected. Arrays, mappings and structs (based on those following types) are also not affected:
- signed integers, including sizes smaller than 256 bits
- bytesNN types, including sizes smaller than 256 bits
- unsigned integers (uint) of 256 bits
Contracts with types smaller than 256 bits that are never next to each other (note that state variables of base contracts are “pulled in”) are not affected.
The Ethereum multisignature wallet contract is not affected. Note that addresses take up 160 bits, so contracts that only use addresses and 256-bit types are safe. Additionally, addresses and booleans are almost never manipulated via arithmetic operations in practice, so contracts using only addresses, booleans and 256 bit types should also be safe.
The following contracts may be affected: Contracts containing two or more contiguous state variables where the sum of their sizes is less than 256 bits and the first state variable is not a signed integer and not of bytesNN type.
Types smaller than 256 bits include: bool, enums, uint8, …, uint248, int8, …, int248, address, any contract type
There are 3 The DAO refund contracts and they all hold significant balances:
DAO to ETH Refund
Reference How do I convert my The DAO tokens into ethers using the withdrawal contract after the hard fork? The source code for the contract at 0xbf4ed7b27f1d666546e30d74d50d173d20bca754 with current balance 1,090,331.224832537 ETH (USD 11,884,610.35) follows:
contract DAO {
function balanceOf(address addr) returns (uint);
function transferFrom(address from, address to, uint balance) returns (bool);
uint public totalSupply;
}
contract WithdrawDAO {
DAO constant public mainDAO = DAO(0xbb9bc244d798123fde783fcc1c72d3bb8c189413);
address public trustee = 0xda4a4626d3e16e094de3225a751aab7128e96526;
function withdraw(){
uint balance = mainDAO.balanceOf(msg.sender);
if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
throw;
}
function trusteeWithdraw() {
trustee.send((this.balance + mainDAO.balanceOf(this)) - mainDAO.totalSupply());
}
}
The contract above uses the balances from The DAO contract at 0xbb9bc244d798123fde783fcc1c72d3bb8c189413.
DAO to ETH ExtraBalance Refund
WithdrawDAO
Reference How do I get a refund for the amount I paid in excess of 1 ether to 100 The DAO tokens? The source code for the contract at 0x755cdba6ae4f479f7164792b318b2a06c759833b with current balance 144,211.812681011 ETH (USD 1,576,235.11) follows:
// Refund contract for extraBalance
// Amounts to be paid are tokenized in another contract and allow using the same refund contract as for theDAO
// Though it may be misleading, the names 'DAO', 'mainDAO' are kept here for the ease of code review
contract DAO {
function balanceOf(address addr) returns (uint);
function transferFrom(address from, address to, uint balance) returns (bool);
uint public totalSupply;
}
contract WithdrawDAO {
DAO constant public mainDAO = DAO(0x5c40ef6f527f4fba68368774e6130ce6515123f2);
address constant public trustee = 0xda4a4626d3e16e094de3225a751aab7128e96526;
function withdraw(){
uint balance = mainDAO.balanceOf(msg.sender);
if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
throw;
}
/**
* Return funds back to the curator.
*/
function clawback() external {
if (msg.sender != trustee) throw;
if (!trustee.send(this.balance)) throw;
}
}
ExtraBalToken
The contract above uses the balances from ExtraBalToken contract at 0x5c40ef6f527f4fba68368774e6130ce6515123f2 which was loaded with the extraBalance balances for the contributing accounts. The source follows:
contract ExtraBalToken {
uint256 public totalSupply;
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
/* This generates a public event on the blockchain that will notify clients */
event Transfer(address indexed from, address indexed to, uint256 value);
/* Send coins */
function transfer(address _to, uint256 _value) {
if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough
if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
balanceOf[msg.sender] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place
}
/* Allow another contract to spend some tokens in your behalf */
function approve(address _spender, uint256 _value)
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/* A contract attempts to get the coins */
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
if (balanceOf[_from] < _value) throw; // Check if the sender has enough
if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
if (_value > allowance[_from][msg.sender]) throw; // Check allowance
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
allowance[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
return true;
}
/* This unnamed function is called whenever someone tries to send ether to it */
function () {
throw; // Prevents accidental sending of ether
}
uint constant D160 = 0x10000000000000000000000000000000000000000;
address public owner;
function ExtraBalToken() {
owner = msg.sender;
}
bool public sealed;
// The 160 LSB is the address of the balance
// The 96 MSB is the balance of that address.
function fill(uint[] data) {
if ((msg.sender != owner)||(sealed))
throw;
for (uint i=0; i<data.length; i++) {
address a = address( data[i] & (D160-1) );
uint amount = data[i] / D160;
if (balanceOf[a] == 0) { // In case it's filled two times, it only increments once
balanceOf[a] = amount;
totalSupply += amount;
}
}
}
function seal() {
if ((msg.sender != owner)||(sealed))
throw;
sealed= true;
}
}
DAO to ETC Refund
WhitehatWithdraw
Reference How do I convert my The DAO tokens into ethers on the Ethereum Classic chain? The source code for the contract at Classic chain address 0x9f5304da62a5408416ea58a17a92611019bd5ce3 with current balance 2,449,003.3962 ETH (USD 2,198,715.25) follows:
// The contract that allows DTH to withdraw funds that the white hat
// group has managed to retrieve.
//
// There are 2 ways to use the contract:
// 1. withdraw()
// 2. proxyWithdraw()
//
// For a description of each method, take a look at the docstrings.
//
// License: BSD3
contract DAOBalanceSnapShot {
function balanceOf(address _dth) constant returns(uint);
function totalSupply() constant returns(uint );
}
contract Owned {
/// Prevents methods from perfoming any value transfer
modifier noEther() {if (msg.value > 0) throw; _}
/// Allows only the owner to call a function
modifier onlyOwner { if (msg.sender != owner) throw; _ }
address owner;
function Owned() { owner = msg.sender;}
function changeOwner(address _newOwner) onlyOwner {
owner = _newOwner;
}
function getOwner() noEther constant returns (address) {
return owner;
}
}
contract WhitehatWithdraw is Owned {
uint constant WithdrawType_DIRECT = 1;
uint constant WithdrawType_PROXY = 2;
DAOBalanceSnapShot daoBalance;
mapping (address => uint) paidOut;
mapping (bytes32 => bool) usedSignatures;
uint totalFunds;
uint deployTime;
uint closingTime;
address whg_donation;
address escape;
address remainingBeneficary;
bool sealed;
event Withdraw(address indexed dth, address indexed beneficiary, uint256 amount, uint256 percentageWHG, uint256 withdrawType);
event CertifiedDepositorsChanged(address indexed _depositor, bool _allowed);
event Deposit(uint amount);
event EscapeCalled(uint amount);
event RemainingClaimed(uint amount);
function fill(uint[] data) onlyOwner {
if ((msg.sender != owner)||(sealed))
throw;
for (uint i=0; i< data.length; i+= 2) {
address dth = address(data[i]);
uint amount = uint(data[i+1]);
paidOut[dth] = amount;
totalFunds += amount;
}
}
function seal() {
if ((msg.sender != owner)||(sealed))
throw;
sealed= true;
}
function WhitehatWithdraw(address _whg_donation, address _daoBalanceSnapshotAddress, address _escapeAddress, address _remainingBeneficiary) {
whg_donation = _whg_donation;
daoBalance = DAOBalanceSnapShot(_daoBalanceSnapshotAddress);
escape = _escapeAddress;
remainingBeneficary = _remainingBeneficiary;
totalFunds = msg.value;
deployTime = now;
closingTime = 24 weeks;
}
/// Calculates the remaining funds available for a DTH to withdraw
///
/// @param _dth The address of the DAO Token Holder for whom
/// to get the funds remaining for withdrawal
/// @return The amount of funds remaining for withdrawal
function calculateWithdraw(address _dth) constant noEther returns(uint) {
uint tokens = daoBalance.balanceOf(_dth);
uint acumulatedReward = tokens * totalFunds / daoBalance.totalSupply();
if (acumulatedReward < paidOut[_dth]) {
return 0;
}
return acumulatedReward - paidOut[_dth];
}
/// The core of the withdraw functionality. It is called by all other withdraw functions
///
/// @param _dth The address of the DAO token holder for whom the
/// withdrawal is going to happen
/// @param _beneficiary The address that will receive the _percentage of
/// the funds corresponding to the _dth.
/// @param _percentageWHG The percentage of the funds that will be donated to the
/// White Hat Group. It should be a number ranging from 0
/// to 100. Anything not claimed by the DTH will be going
/// as a donation to the Whitehat Group.
/// @param _withdrawType method used to withdraw (1) Direct (2) Proxy (3) bot (4) owner
function commonWithdraw(address _dth, address _beneficiary, uint _percentageWHG, uint _withdrawType) internal {
if (_percentageWHG > 100) {
throw;
}
uint toPay = calculateWithdraw(_dth);
if (toPay == 0) {
return;
}
if (toPay > this.balance) {
toPay = this.balance;
}
uint portionWhg = toPay * _percentageWHG / 100;
uint portionDth = toPay - portionWhg;
paidOut[_dth] += toPay;
// re-entrancy is not possible due to the use of send() which limits
// the forwarded gas thanks to the gas stipend
if (portionWhg > 0) {
if ( !whg_donation.send(portionWhg) ) {
throw;
}
}
if (portionDth > 0) {
if (!_beneficiary.send(portionDth) ) {
throw;
}
}
Withdraw(_dth, _beneficiary, toPay, _percentageWHG, _withdrawType);
}
/// The simple withdraw function, where the message sender is considered as
/// the DAO token holder whose ratio needs to be retrieved.
function withdraw(address _beneficiary, uint _percentageWHG ) noEther {
commonWithdraw(msg.sender, _beneficiary, _percentageWHG, WithdrawType_DIRECT);
}
/// The proxy withdraw function. Anyone can call this for someone else as long
/// as he includes signed data retrieved by using web3.eth.sign(address, hash).
/// The DAO token holder whose ratio needs to be retrieved is determined by
/// performing ecrecover on the signed data.
///
/// This function will also allow people to use the ETH chain to give an
/// approval for withdrawal in the ETC chain without having to sync the
/// ETC chain. The only requirement is that the account that gives the
/// approval needs to be an end-user account. Multisig wallets can't do that.
function proxyWithdraw(address _beneficiary, uint _percentageWHG, uint8 _v, bytes32 _r, bytes32 _s) noEther {
if (usedSignatures[_r]) {
throw;
}
bytes32 _hash = sha3("Withdraw DAOETC to ", _beneficiary, _percentageWHG);
address _dth = ecrecover(_hash, _v, _r, _s);
usedSignatures[_r] = true;
commonWithdraw(_dth, _beneficiary, _percentageWHG, WithdrawType_PROXY);
}
/// This is the only way to send money to the contract, adding to the total
/// amount of ETH to be refunded.
function deposit() returns (bool) {
totalFunds += msg.value;
Deposit(msg.value);
return true;
}
/// Last Resort call, to allow for a reaction if something bad happens to
/// the contract or if some security issue is uncovered.
function escapeHatch() noEther onlyOwner returns (bool) {
uint total = this.balance;
if (!escape.send(total)) {
throw;
}
EscapeCalled(total);
}
/// Allows the claiming of the remaining funds after a given amount of time
/// Amount is set to 6 months for now but may still change in the future.
function claimRemaining() noEther returns (bool) {
if (now < deployTime + closingTime) {
throw;
}
uint total = this.balance;
if (!remainingBeneficary.send(total)) {
throw;
}
RemainingClaimed(total);
}
/// Allows the option to extend (but not shorten!) the closingTime of the
/// contract to more than 6 months, perhaps even to infinity if that is
/// deemed as the best choice for the DAO Token holders.
function extendClosingTime(uint _additionalSeconds) noEther onlyOwner {
closingTime += _additionalSeconds;
}
function () { //no donations
throw;
}
function getPaidOut(address _account) noEther constant returns (uint) {
return paidOut[_account];
}
function getMyBalance(address _account) noEther constant returns (uint) {
return daoBalance.balanceOf(_account);
}
function getTotalFunds() noEther constant returns (uint) {
return totalFunds;
}
function getWHGDonationAddress() noEther constant returns (address) {
return whg_donation;
}
}
The contract above uses the balances from the DAOBalanceSnapShot contract at 0x180826b05452ce96e157f0708c43381fee64a6b8. See the reference for further information on this balance snapshot.
Are any of The DAO refund contract affected by this bug?