6

I’d like to know what is preventing me to write an assembly code who knows the contract address and call some arbitrary functions, even if declared “internal” via a simple JUMP.

In theory I can: 1) discover what is the location to jump to analyzing the bytecode and using some accessible address as baseaddress; 2) write my own assembly code which loads the needed parameters into the stack; 3) make a JUMP to the location discovered at (1).

What is the mechanism that should prevent me to do that?

(I understand that the various “call” type opcodes jump to the initial location of the called code, but apparently the JUMP and JUMPI instructions have no such limitation. Reading yellow paper sections 9.4.2 and 9.4.3 AND the Geth implementation apparently do not contradict the possibility to jump to an arbitrary absolute address, but for an unclear definition of “permitted jump range” which apparently simply requires that a JUMPDEST opcode be present in the location where the EVM jumps. And this is easy to find in the attacked contract opcode. Some further analysis is required and previous answer to similar questions is to much rough and straight and do not give evidences)

Rick Park
  • 3,194
  • 2
  • 8
  • 25

1 Answers1

6

You can not jump in on arbitrary location of the called contract. You can only call it via CALL/CALLCODE/DELEGATECALL. When you use the CALL opcode for instance, you can only provide the input for the call, but the program counter is starting at 0 at the destination. The first thing that is executed at the destination (if the bytecode has been generated by the solidity compiler) is matching the first 4 bytes of the input data (function signature) with a location of a public function. Since internal functions are not part of this "switch", you'll not be able to call them.

See Solidity Assembly or Ethereum Yellow Paper.

call(g, a, v, in, insize, out, outsize)     

call contract at address a with input mem[in..(in+insize)) providing g gas 
and v wei and output area mem[out..(out+outsize))
returning 0 on error (eg. out of gas) and 1 on success

Additional Info: See "How to separate functions in evm bytecode?" for an example of the function selection bytecode.

For a deep dive, you can look at geth's implementation. Entry points are:

ivicaa
  • 7,519
  • 1
  • 20
  • 50
  • 1
    Thank you for the answer! I appreciate it. At the moment I do not understand any formal evidence of that. JUMP and JUMPI can be used in any stand alone assembly and they have as argument absolute addresses. The function D defined in yellow paper (9.4.2) which should compute the valid jump addresses), if I understand, asks for the presence of the JUMPDEST instruction in the place where you jump, but this do not prevent me to search for those codes and to jump there. Can you add any more detailed explaination about this? – Rick Park Jan 28 '19 at 21:21
  • 1
    JUMP works only within your own contract. For your own bytecode, you can construct whatever you want. You could manually construct some bytecode without the solidity compiler. But if you wish to call an existing contract JUMP opcode will not help you, because you can only CALL/CALLCODE/DELEGATECALL the target contract and the execution at the target will start at program counter (PC) = 0 – ivicaa Jan 28 '19 at 21:24
  • Thank you. Could you please give me a reference for this? Of course I think you are right (or ethereum did fail immediately), but I’d like to understand a formal demonstration/declaration or whatever. – Rick Park Jan 28 '19 at 23:52
  • (I mean: about the “jump works only within your own contract”) – Rick Park Jan 29 '19 at 00:26
  • See Yellow Paper (link in the answer) section 9, or more specific 9.4.3. This is the ultimate reference. – ivicaa Jan 29 '19 at 06:15
  • I know 9.4.3, but it is defined as I said before, I.e. it requires the presence of a JUMPDEST opcode in the destination, but nothing relevant more if not in the case of PUSH. The concept of “valid jump destination” is poorly defined. At least, not in a clear way. If that is the ultimate reference, definitively I can jump everywhere in the EVM. Isn’t it? – Rick Park Jan 29 '19 at 08:22
  • Every contract runs in it's own isolated execution context. The location that a JUMP opcode can take is limited to the address space of the bytecode of the own contract. There is no global address space where you can jump to an arbitrary location of any smart contract existing in the system. Contract to contract interaction is done via CALL opcodes. – ivicaa Jan 29 '19 at 08:31
  • You can think of it like isolated OS processes which communicate with each other via some IPC (inter process communication) mechanism. I have to admit that it's difficult for me to find the exact location in the yellow paper formally explaining what I said before, but if you look at geth code for instance, you can see how it's implemented. https://github.com/ethereum/go-ethereum/blob/master/core/vm/evm.go (Call and run functions) – ivicaa Jan 29 '19 at 08:31
  • My doubts come from https://github.com/ethereum/go-ethereum/blob/master/core/vm/instructions.go, opjmp definition... – Rick Park Jan 29 '19 at 08:58
  • Ok.. that's a good example. There you can see the line interpreter.intPool.put(pos). Now the trick is that every CALL runs the bytecode of the contract in it's own interpreter instance isolated from other contracts. – ivicaa Jan 29 '19 at 09:03