1 / 67

Solidity Pitfalls and Hazards: Part One

This article discusses the external contract references and race conditions in Solidity, highlighting the odd and complex behavior as well as the lack of precise notion of correctness. It emphasizes the importance of precise definitions and corner cases in language design.

sdurant
Download Presentation

Solidity Pitfalls and Hazards: Part One

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Solidity Pitfalls and Hazards Part One CS1951 L Spring 2019 21 February 2019 Maurice Herlihy Brown University

  2. Today’s Pitfalls: External Contract References Race Conditions

  3. 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

  4. 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

  5. I will be reviewing your Solidity code Здравствуйте! I have all day

  6. On Language Choice EminGünSirer Hacking, Distributed 19 July 2017

  7. 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"

  8. Re-Entrancy Attack Call external contract … Who unexpectedly calls you back … When you were not expecting visitors!

  9. Adapted from here

  10. 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

  11. 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

  12. Much hyperventilation about effect on future of finance

  13. Schematic DAO Code function withdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Client wants to withdraw own money 13

  14. Schematic DAO Code function withdraw(uintamount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Which client? 14

  15. Schematic DAO Code function withdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Does client have enough money? 15

  16. Schematic DAO Code function withdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Transfer the money by calling a function in another contract … 16

  17. Schematic DAO Code function withdraw(uintamount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Let’s rewind … 17

  18. function withdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Transfer the money by calling another contract … function sendMoney(uint amount) { balance += amount msg.sender.call.withdraw(amount) ... } 18

  19. functionwithdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Credit account functionsendMoney(uint amount) { balance += amount msg.sender.call.withdraw(amount) ... } 19

  20. functionwithdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Wait, what? Client makes “re-entrant” withdraw request! function sendMoney(uintamount) { balance += amount msg.sender.call.withdraw(amount) ... } 20

  21. functionwithdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} function sendMoney(uintamount) { balance += amount msg.sender.call.withdraw(amount) ... } 21

  22. functionwithdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Second time around, balance still looks OK … function sendMoney(uintamount) { balance += amount msg.sender.call.withdraw(amount) ... } 22

  23. functionwithdraw(uint amount) { client = msg.sender; if (balance[client] >= amount} { if (client.call.sendMoney(amount)) { balance[client] -= amount; }}} Send money again … and again and again … function sendMoney(uintamount) { balance += amount msg.sender.call.withdraw(amount) ... } 23

  24. 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?

  25. 25

  26. Hard-Fork Ethereum and roll back … Ha-ha, just kidding about that “code is law” thing … Because language design is hard 26

  27. 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-entrancyvulterability? Ethereum’s fault, refund.

  28. I don’t care what you say …

  29. This talk is not about Bitcoin This is a concurrency error

  30. Classical “monitor lock” pitfall Calling another contract = releasing monitor lock Don’t violate invariants before a call

  31. Prevention Change state before any external calls Use a mutex Use transfer() function (too little gas to call back)

  32. Silent Overflow Attack uint8 x = 0; uint8 y = x – 1; // y is 255! intand uintvariableshave fixed ranges EVM quietly rolls over on over/under flow Not exactly what you expected

  33. Example contractToken { mapping(address => uint) balances; uint public totalSupply; constructor (uint _initialSupply) public { balances[msg.sender] = totalSupply = _initialSupply; } … }

  34. Example contractToken { mapping(address => uint) balances; uint public totalSupply; constructor (uint _initialSupply) public { balances[msg.sender] = totalSupply = _initialSupply; } … } simple token contract

  35. Example contract Token { mapping(address => uint) balances; uint public totalSupply; constructor (uint _initialSupply) public { balances[msg.sender] = totalSupply = _initialSupply; } … } keep track of each client’s balance

  36. Example contract Token { mapping(address => uint) balances; uint public totalSupply; constructor (uint _initialSupply) public { balances[msg.sender] = totalSupply = _initialSupply; } … } constructor

  37. Example pragma solidity ^0.5.2; contract Token { … function transfer(address _to, uint _value) publicreturns (bool) { require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; } transfer caller’s tokens to another client return success code

  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; } Make sure caller has enough tokens

  39. 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

  40. You Got a Problem with That? pragma solidity ^0.5.2; contract Token { … function transfer(address _to, uint _value) publicreturns (bool) { require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; }

  41. 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!

  42. Prevention librarySafeMath { … function (sum uint256 a, uint256 b) public view returns (uint256) { assert(b <= a); return a – b; } } Never use Solidity arithmetic directly! Use SafeMath library always Ugly, but safe

  43. 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

  44. 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

  45. Sending Ether desination.foo.value(1000)(arg) send 1000 wei via payablenamed function contract destination { function foo(uintarg) publicpayable { … } }

  46. Sending Ether desination.transfer(1000); send 1000 wei via payablefallback function contract destination { function (uintarg) publicpayable { … } }

  47. Controlling Ether Receipt Ether receipt triggers … Function, named or fallback Possible to refuse ether by reverting (throwing) not being payable

  48. Ether You Cannot Refuse selfDestruct(destination); send balance without calling destination code contract destination { … }

  49. Example Simple gambling game Sequence of milestones (amounts) Players send small ether amounts … First player to reach each milestone wins!

  50. function play() public payable { require(msg.value== 0.5 ether); uintbal = this.balance+ msg.value; if (bal == mileStone1) { … } else if (bal == mileStone2) { … } else if (bal == mileStone3) { … } return; }

More Related