Skip to content

Instantly share code, notes, and snippets.

@buddies2705
Forked from Arachnid/OpcodeChecker.sol
Created May 8, 2018 20:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save buddies2705/e75add8ff3eefcfada2d6fdc4854f918 to your computer and use it in GitHub Desktop.
Save buddies2705/e75add8ff3eefcfada2d6fdc4854f918 to your computer and use it in GitHub Desktop.
/**
* @dev OpcodeChecker processes contract code to generate a bitmap of used opcodes.
*
* The generated bitmap can be used to enforce whitelisting and blacklisting on contract code.
* Bit n of the bitmap is set iff opcode n is used. For instance, the presence of the STOP opcode
* will result in bit 0 of the bitmap being set.
*
* A best-effort attempt is made to skip over unreachable data, but there may be false positives.
* To the extent the checker is written correctly, there are no false negatives.
*
* To check a contract against a blacklist, construct a mask by ORing the values of the opcodes you
* want to prohibit - for instance, if you want to prohibit SELFDESTRUCT (0xFF) CALLCODE (0xF2) and
* DELEGATECALL (0xF4), your mask should be
> `(1 << OP_SELFDESTRUCT) | (1 << OP_CALLCODE) | (1 << OP_DELEGATECALL)`.
*/
contract OpcodeChecker {
uint8 constant OP_STOP = 0x00;
uint8 constant OP_PUSH1 = 0x60;
uint8 constant OP_PUSH32 = 0x7F;
uint8 constant OP_JUMP = 0x56;
uint8 constant OP_JUMPDEST = 0x5B;
uint8 constant OP_RETURN = 0xF3;
uint8 constant OP_SELFDESTRUCT = 0xFF;
bytes32 constant UNREACHABLE_MASK =
(bytes32(1) << OP_STOP) |
(bytes32(1) << OP_JUMP) |
(bytes32(1) << OP_RETURN) |
(bytes32(1) << OP_SELFDESTRUCT);
mapping(bytes32=>bytes32) public opcodesUsed;
mapping(address=>bytes32) public bytecodeHash;
function getCode(address target) internal view returns(bytes memory) {
uint codelen;
assembly { codelen := extcodesize(target) }
bytes memory ret = new bytes(codelen);
assembly {
extcodecopy(target, add(ret, 32), 0, codelen)
}
return ret;
}
function process(address target) public returns (bytes32 bitmap) {
if(bytecodeHash[target] != 0) return opcodesUsed[bytecodeHash[target]];
bytes memory code = getCode(target);
bytes32 codeHash = keccak256(code);
bytecodeHash[target] = codeHash;
bitmap = analyse(code);
opcodesUsed[codeHash] = bitmap;
}
function analyse(bytes code) internal pure returns (bytes32 bitmap) {
bool reachable = true;
for(uint pc = 0; pc < code.length; pc++) {
uint8 opcode = uint8(code[pc]);
if(reachable) {
bytes32 bit = bytes32(1) << opcode;
bitmap |= bit;
if(opcode >= 0x60 && opcode <= 0x7F) {
pc += opcode - 0x5F;
continue;
} else if(reachable && ((bit & UNREACHABLE_MASK) != 0)) {
reachable = false;
}
} else if(opcode == OP_JUMPDEST) {
reachable = true;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment