Fix edge case in integer module for 0.8.0 (#1805)

* Fix edge case in integer module for 0.8.0

* Remove print
pull/1811/head
Nikhil Parasaram 1 year ago committed by GitHub
parent 8aa56600ef
commit a28d600ced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      mythril/ethereum/util.py
  2. 30
      mythril/mythril/mythril_disassembler.py
  3. 2
      tests/integration_tests/version_test.py
  4. 12
      tests/testdata/input_contracts/integer_edge_case.sol

@ -13,7 +13,7 @@ import typing
from pathlib import Path from pathlib import Path
from requests.exceptions import ConnectionError from requests.exceptions import ConnectionError
from subprocess import PIPE, Popen from subprocess import PIPE, Popen
from typing import Tuple
from json.decoder import JSONDecodeError from json.decoder import JSONDecodeError
import semantic_version as semver import semantic_version as semver
@ -217,19 +217,13 @@ def extract_version(file: typing.Optional[str]):
return str(version) return str(version)
def extract_binary(file: str) -> str: def extract_binary(file: str) -> Tuple[str, str]:
file_data = None file_data = None
with open(file) as f: with open(file) as f:
file_data = f.read() file_data = f.read()
version = extract_version(file_data) version = extract_version(file_data)
if (
version
and NpmSpec("^0.8.0").match(Version(version))
and "unchecked" not in file_data
):
args.use_integer_module = False
if version is None: if version is None:
return os.environ.get("SOLC") or "solc" return os.environ.get("SOLC") or "solc", version
return solc_exists(version) return solc_exists(version), version

@ -57,6 +57,7 @@ class MythrilDisassembler:
solc_args=None, solc_args=None,
) -> None: ) -> None:
args.solc_args = solc_args args.solc_args = solc_args
self.solc_version = solc_version
self.solc_binary = self._init_solc_binary(solc_version) self.solc_binary = self._init_solc_binary(solc_version)
self.solc_settings_json = solc_settings_json self.solc_settings_json = solc_settings_json
self.eth = eth self.eth = eth
@ -68,8 +69,9 @@ class MythrilDisassembler:
def _init_solc_binary(version: str) -> Optional[str]: def _init_solc_binary(version: str) -> Optional[str]:
""" """
Only proper versions are supported. No nightlies, commits etc (such as available in remix). Only proper versions are supported. No nightlies, commits etc (such as available in remix).
This functions extracts
:param version: Version of the solc binary required :param version: Version of the solc binary required
:return: The solc binary of the corresponding version :return: AThe solc binary of the corresponding version
""" """
if not version: if not version:
@ -84,8 +86,6 @@ class MythrilDisassembler:
if version.startswith("v"): if version.startswith("v"):
version = version[1:] version = version[1:]
if version and NpmSpec("^0.8.0").match(Version(version)):
args.use_integer_module = False
if version == main_version_number: if version == main_version_number:
log.info("Given version matches installed version") log.info("Given version matches installed version")
solc_binary = os.environ.get("SOLC") or "solc" solc_binary = os.environ.get("SOLC") or "solc"
@ -231,6 +231,24 @@ class MythrilDisassembler:
self.sigs.add_sigs(original_filename, targets_json) self.sigs.add_sigs(original_filename, targets_json)
return address, contracts return address, contracts
def check_run_integer_module(self, source_file):
# Strip leading 'v' from version if it's there
normalized_version = self.solc_version.lstrip("v")
# Check if solc_version is not provided or doesn't match the required version
# As post 0.8.0 solc versions automatically add assertions to sanity check arithmetic
if not self.solc_version or not NpmSpec("^0.8.0").match(
Version(normalized_version)
):
return True
with open(source_file, "r") as f:
for line in f:
if "unchecked" in line:
return True
return False
def load_from_solidity( def load_from_solidity(
self, solidity_files: List[str] self, solidity_files: List[str]
) -> Tuple[str, List[SolidityContract]]: ) -> Tuple[str, List[SolidityContract]]:
@ -248,7 +266,11 @@ class MythrilDisassembler:
contract_name = None contract_name = None
file = os.path.expanduser(file) file = os.path.expanduser(file)
solc_binary = self.solc_binary or util.extract_binary(file) solc_binary = self.solc_binary
if solc_binary is None:
solc_binary, self.solc_version = util.extract_binary(file)
if self.check_run_integer_module(file) is False:
args.use_integer_module = False
try: try:
# import signatures from solidity source # import signatures from solidity source
self.sigs.import_solidity_file( self.sigs.import_solidity_file(

@ -15,6 +15,8 @@ test_data = (
("version_2.sol", None, True), ("version_2.sol", None, True),
("version_3.sol", None, True), ("version_3.sol", None, True),
("version_patch.sol", None, False), ("version_patch.sol", None, False),
("integer_edge_case.sol", None, True),
("integer_edge_case.sol", "v0.8.19", True),
) )

@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract B {
function f(uint256 arg) public view returns(uint256) {
uint256 res;
unchecked{
res = 10_000_000_000 * arg; // undetected overflow
}
//assert(res > arg); // the assertion violation is correctly detected if added
return res;
}
}
Loading…
Cancel
Save