Solidity Pitfalls and Hazards Part One CS 1951












![Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >=](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-13.jpg)
![Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >=](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-14.jpg)
![Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >=](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-15.jpg)
![Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >=](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-16.jpg)
![Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >=](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-17.jpg)
![function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-18.jpg)
![function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-19.jpg)
![function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-20.jpg)
![function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-21.jpg)
![function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-22.jpg)
![function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if](https://slidetodoc.com/presentation_image_h/148bfeab4df783969cb3083776ba6f08/image-23.jpg)












































- Slides: 67
Solidity Pitfalls and Hazards Part One CS 1951 L Spring 2019 21 February 2019 Maurice Herlihy Brown University
Today’s Pitfalls: External Contract References Race Conditions 2
Solidity is based on Javascript Odd, complex behavior considered normal No precise notion of correctness Just has to work “well enough” Must be “easy” for non-experts No actual adversary
Using a web scripting language for financial applications? small errorscan be catastrophic precise definitions matter corner cases matter Language design is hard, and not for amateurs 4
Здравствуйте! I will be reviewing your Solidity code I have all day 5
On Language Choice Emin Gün Sirer Hacking, Distributed 19 July 2017 6
Next Four Lectures Review 16 Vulnerabilities Some common to all blockchains Some are Solidity idiosyncracies … "nonsense is nonsense, but the history of nonsense is scholarship" 7
Re-Entrancy Attack Call external contract … Who unexpectedly calls you back … When you were not expecting visitors! 8
Adapted from here 9
An Application DAO = Decentralized Autonomous Organization Invests in other businesses: about $50 Million capital In Ether cryptocurrency No managers or board of directors Controlled by smart contacts and investor voting 10
DAO = Decentralized Autonomous Organization “code is law” Invests in other businesses: about $50 Million capital In Ether cryptocurrency No managers or board of directors Controlled by smart contacts and investor voting 11
Much hyperventilation about effect on future of finance 12
Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Client wants to withdraw own money 13 13
Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Which client? 14 14
Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Does client have enough money? 15 15
Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Transfer the money by calling a function in another contract … 16 16
Schematic DAO Code function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; Let’s rewind … }}} 17 17
function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Transfer the money by calling another contract … function send. Money(uint amount) { balance += amount msg. sender. call. withdraw(amount). . . } 18 18
function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Credit account function send. Money(uint amount) { balance += amount msg. sender. call. withdraw(amount). . . } 19 19
function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Wait, what? Client makes “re-entrant” withdraw request! function send. Money(uint amount) { balance += amount msg. sender. call. withdraw(amount). . . } 20 20
function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} function send. Money(uint amount) { balance += amount msg. sender. call. withdraw(amount). . . } 21 21
function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Second time around, balance still looks OK … function send. Money(uint amount) { balance += amount msg. sender. call. withdraw(amount). . . } 22 22
function withdraw(uint amount) { client = msg. sender; if (balance[client] >= amount} { if (client. call. send. Money(amount)) { balance[client] -= amount; }}} Send money again … and again … function send. Money(uint amount) { balance += amount msg. sender. call. withdraw(amount). . . } 23 23
This happened “ The attack is a recursive calling vulnerability, where an attacker called the “split” function, and then calls the split function recursively …” The actual fix? 24
25 25
Hard-Fork Ethereum and roll back … Ha-ha, just kidding about that “code is law” thing … Because language design is hard 26
Rationale for Hard Fork Client did something stupid? Client’s fault, no refund. Bug in Ethereum run-time system? Ethereum’s fault, refund. No warning about re-entrancy vulterability? Ethereum’s fault, refund. 27
I don’t care what you say …
This talk is not about Bitcoin This is a concurrency error
Classical “monitor lock” pitfall Calling another contract = releasing monitor lock Don’t violate invariants before a call 30
Prevention Change state before any external calls Use a mutex Use transfer() function (too little gas to call back) 31
Silent Overflow Attack uint 8 x = 0; uint 8 y = x – 1; // y is 255! int and uint variables have fixed ranges EVM quietly rolls over on over/under flow Not exactly what you expected 32
Example contract Token { mapping(address => uint) balances; uint public total. Supply; constructor (uint _initial. Supply) public { balances[msg. sender] = total. Supply = _initial. Supply; } … } 33
Example contract Token { mapping(address => uint) balances; uint public total. Supply; simple token contract constructor (uint _initial. Supply) public { balances[msg. sender] = total. Supply = _initial. Supply; } … } 34
Example contract Token { mapping(address => uint) balances; uint public total. Supply; constructor (uint _initial. Supply) public { keep track of each client’s balances[msg. sender] = total. Supply = _initial. Supply; } … } 35
Example contract Token { mapping(address => uint) balances; uint public total. Supply; constructor (uint _initial. Supply) public { balances[msg. sender] = total. Supply = _initial. Supply; } … } constructor 36
Example pragma solidity ^0. 5. 2; contract Token { … function transfer(address _to, uint _value) public returns (bool) { require(balances[msg. sender] - _value >= 0); balances[msg. sender] -= _value; transfer caller’s tokens to another client balances[_to] += _value; return success code return true; } 37
Example pragma solidity ^0. 5. 2; contract Token { … function transfer(address _to, uint _value) public returns (bool) { require(balances[msg. sender] - _value >= 0); balances[msg. sender] -= _value; balances[_to] += _value; Make sure caller has enough tokens return true; } 38
Example pragma solidity ^0. 5. 2; contract Token { … function transfer(address _to, uint _value) public returns (bool) { require(balances[msg. sender] - _value >= 0); balances[msg. sender] -= _value; balances[_to] += _value; return true; } do the transfer 39
You Got a Problem with That? pragma solidity ^0. 5. 2; contract Token { … function transfer(address _to, uint _value) public returns (bool) { require(balances[msg. sender] - _value >= 0); balances[msg. sender] -= _value; balances[_to] += _value; return true; } 40
You Got a Problem with That? pragma solidity ^0. 5. 2; contract Token { … function transfer(address _to, uint _value) public returns (bool) { require(balances[msg. sender] - _value >= 0); balances[msg. sender] -= _value; balances[_to] += _value; return true; if _value > 0, difference always positive, } test passes even if client has zero balance! 41
Prevention library Safe. Math { … function (sum uint 256 a, uint 256 b) public view returns (uint 256) { assert(b <= a); return a – b; Never use Solidity arithmetic directly! } } Use Safe. Math library always Ugly, but safe 42
This Happened Self-described Ponzi scheme Attacker managed to subtract 1 from 0 -balance account … Leaving balance of 115 quattuorvigintillion ETH Somehow only managed to escape with 866 ETH 43
Unexpected Ether Attack You can prevent others from taking your money But you can’t stop them from sending you money Surprisingly, this can be a bad thing 44
Sending Ether desination. foo. value(1000)(arg) send 1000 wei via payable named function contract destination { function foo(uint arg) public payable { … } } 45
Sending Ether desination. transfer(1000); send 1000 wei via payable fallback function contract destination { function (uint arg) public payable { … } } 46
Controlling Ether Receipt Ether receipt triggers … Function, named or fallback Possible to refuse ether by reverting (throwing) not being payable 47
Ether You Cannot Refuse self. Destruct(destination); send balance without calling destination code contract destination { … } 48
Example Simple gambling game Sequence of milestones (amounts) Players send small ether amounts … First player to reach milestone wins! 49
function play() public payable { require(msg. value == 0. 5 ether); uint bal = this. balance + msg. value; if (bal == mile. Stone 1) { … } else if (bal == mile. Stone 2) { … } else if (bal == mile. Stone 3) { … } return; } 50
function play() public payable { require(msg. value == 0. 5 ether); uint bal = this. balance + msg. value; if (bal == mile. Stone 1) { … each play must be 0. 5 ether } else if (bal == mile. Stone 2) { … } else if (bal == mile. Stone 3) { … } return; } 51
function play() public payable { require(msg. value == 0. 5 ether); uint bal = this. balance + msg. value; if (bal == mile. Stone 1) { … } else if. Compute current balance (bal == mile. Stone 2) { … } else if (bal == mile. Stone 3) { … } return; } 52
function play() public payable { require(msg. value == 0. 5 ether); uint bal = this. balance + msg. value; if (bal == mile. Stone 1) { … payout if player } hits milestone else if (bal == mile. Stone 2) { … } else if (bal == mile. Stone 3) { … } return; } 53
function play() public payable { require(msg. value == 0. 5 ether); uint bal = this. balance + msg. value; if (bal == mile. Stone 1) { … } else if (bal == mile. Stone 2) { … If adversary calls self. Destruct() } to transfer 0. 0001 ether, else if (bal == mile. Stone 3) { … this. balance becomes slightly off, and comparions all fail! } return; } 54
Prevention Never rely on exact value of this. balance! Have payable functions track own transfers 55
Delegate. Call Attack There are 2 ways to call other contracts’s functions call executes in remote context delegatecall executes in caller’s context The distinction matters! 56
Delegate. Call Opcode Contract A Contract B delegatecall Copy of Contract B’s code copy code A delegatecalls B’s function … which runs in A’s address space 57
Composite Wallet A Wallet B Private data Wallet Library (uninitialized) Shared Code 58
Composite Wallet A Wallet B Wallet Library (uninitialized) hardwired link address constant _wallet. Library = 0 x 863 df 6 b… Private data Shared Code 59
Each Call Wallet B Wallet Library (uninitialized) Copy of Wallet Library code Private data 60 Shared Code
Attack Wallet B Private data initialize and claim ownership Wallet Library (owned by attacker) 61 Shared Code
Attack order to self-destruct! Wallet Library Wallet B Private data 62 Shared Code
Wallet A Uh, Oh Wallet Library Wallet B Private data 63 Shared Code
This Happened About $300 M (at the time) burned 64
Prevention Any number of changes to library initializing it no selfdestruct() function Declaring a contract a library enforces statelessness 65
Ideas we covered in this lecture Smart Contract Language Design Re-Entrancy Attack Silent Overflow Attack Unexpected Ether Attack Delegate. Call Attack 66
67



































































