diff options
-rw-r--r-- | docs/bugs.json | 92 | ||||
-rw-r--r-- | docs/bugs.rst | 61 | ||||
-rw-r--r-- | docs/bugs_by_version.json | 297 | ||||
-rw-r--r-- | docs/index.rst | 1 | ||||
-rw-r--r-- | docs/security-considerations.rst | 6 | ||||
-rwxr-xr-x | scripts/update_bugs_by_version.py | 43 | ||||
-rwxr-xr-x | test/cmdlineTests.sh | 5 |
7 files changed, 503 insertions, 2 deletions
diff --git a/docs/bugs.json b/docs/bugs.json new file mode 100644 index 00000000..2a8d167a --- /dev/null +++ b/docs/bugs.json @@ -0,0 +1,92 @@ +[ + { + "name": "IdentityPrecompileReturnIgnored", + "summary": "Failure of the identity precompile was ignored.", + "description": "Calls to the identity contract, which is used for copying memory, ignored its return value. On the public chain, calls to the identity precompile can be made in a way that they never fail, but this might be different on private chains.", + "severity": "low", + "fixed": "0.4.7" + }, + { + "name": "OptimizerStateKnowledgeNotResetForJumpdest", + "summary": "The optimizer did not properly reset its internal state at jump destinations, which could lead to data corruption.", + "description": "The optimizer performs symbolic execution at certain stages. At jump destinations, multiple code paths join and thus it has to compute a common state from the incoming edges. Computing this common state was simplified to just use the empty state, but this implementation was not done properly. This bug can cause data corruption.", + "severity": "medium", + "introduced": "0.4.5", + "fixed": "0.4.6", + "conditions": { + "optimizer": true + } + }, + { + "name": "HighOrderByteCleanStorage", + "summary": "For short types, the high order bytes were not cleaned properly and could overwrite existing data.", + "description": "Types shorter than 32 bytes are packed together into the same 32 byte storage slot, but storage writes always write 32 bytes. For some types, the higher order bytes were not cleaned properly, which made it sometimes possible to overwrite a variable in storage when writing to another one.", + "link": "https://blog.ethereum.org/2016/11/01/security-alert-solidity-variables-can-overwritten-storage/", + "severity": "high", + "introduced": "0.1.6", + "fixed": "0.4.4" + }, + { + "name": "OptimizerStaleKnowledgeAboutSHA3", + "summary": "The optimizer did not properly reset its knowledge about SHA3 operations resulting in some hashes (also used for storage variable positions) not being calculated correctly.", + "description": "The optimizer performs symbolic execution in order to save re-evaluating expressions whose value is already known. This knowledge was not properly reset across control flow paths and thus the optimizer sometimes thought that the result of a SHA3 operation is already present on the stack. This could result in data corruption by accessing the wrong storage slot.", + "severity": "medium", + "fixed": "0.4.3", + "conditions": { + "optimizer": true + } + }, + { + "name": "LibrariesNotCallableFromPayableFunctions", + "summary": "Library functions threw an exception when called from a call that received Ether.", + "description": "Library functions are protected against sending them Ether through a call. Since the DELEGATECALL opcode forwards the information about how much Ether was sent with a call, the library function incorrectly assumed that Ether was sent to the library and threw an exception.", + "severity": "low", + "introduced": "0.4.0", + "fixed": "0.4.2" + }, + { + "name": "SendFailsForZeroEther", + "summary": "The send function did not provide enough gas to the recipient if no Ether was sent with it.", + "description": "The recipient of an Ether transfer automatically receives a certain amount of gas from the EVM to handle the transfer. In the case of a zero-transfer, this gas is not provided which causes the recipient to throw an exception.", + "severity": "low", + "fixed": "0.4.0" + }, + { + "name": "DynamicAllocationInfiniteLoop", + "summary": "Dynamic allocation of an empty memory array caused an infinite loop and thus an exception.", + "description": "Memory arrays can be created provided a length. If this length is zero, code was generated that did not terminate and thus consumed all gas.", + "severity": "low", + "fixed": "0.3.6" + }, + { + "name": "OptimizerClearStateOnCodePathJoin", + "summary": "The optimizer did not properly reset its internal state at jump destinations, which could lead to data corruption.", + "description": "The optimizer performs symbolic execution at certain stages. At jump destinations, multiple code paths join and thus it has to compute a common state from the incoming edges. Computing this common state was not done correctly. This bug can cause data corruption, but it is probably quite hard to use for targeted attacks.", + "severity": "low", + "fixed": "0.3.6", + "conditions": { + "optimizer": true + } + }, + { + "name": "CleanBytesHigherOrderBits", + "summary": "The higher order bits of short bytesNN types were not cleaned before comparison.", + "description": "Two variables of type bytesNN were considered different if their higher order bits, which are not part of the actual value, were different. An attacker might use this to reach seemingly unreachable code paths by providing incorrectly formatted input data.", + "severity": "medium/high", + "fixed": "0.3.3" + }, + { + "name": "ArrayAccessCleanHigherOrderBits", + "summary": "Access to array elements for arrays of types with less than 32 bytes did not correctly clean the higher order bits, causing corruption in other array elements.", + "description": "Multiple elements of an array of values that are shorter than 17 bytes are packed into the same storage slot. Writing to a single element of such an array did not properly clean the higher order bytes and thus could lead to data corruption.", + "severity": "medium/high", + "fixed": "0.3.1" + }, + { + "name": "AncientCompiler", + "summary": "This compiler version is ancient and might contain several undocumented or undiscovered bugs.", + "description": "The list of bugs is only kept for compiler versions starting from 0.3.0, so older versions might contain undocumented bugs.", + "severity": "high", + "fixed": "0.3.0" + } +]
\ No newline at end of file diff --git a/docs/bugs.rst b/docs/bugs.rst new file mode 100644 index 00000000..55771a35 --- /dev/null +++ b/docs/bugs.rst @@ -0,0 +1,61 @@ +.. index:: Bugs + +.. _known_bugs: + +################## +List of Known Bugs +################## + +Below, you can find a JSON-formatted list of some of the known security-relevant bugs in the +Solidity compiler. The file itself is hosted in the `Github repository +<https://github.com/ethereum/solidity/blob/develop/docs/bugs.json>`_. +The list stretches back as far as version 0.3.0, bugs known to be present only +in versions preceding that are not listed. + +There is another file called `bugs_by_version.json +<https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json>`_, +which can be used to check which bugs affect a specific version of the compiler. + +Contract source verification tools and also other tools interacting with +contracts should consult this list according to the following criteria: + + - It is mildly suspicious if a contract was compiled with a nightly + compiler version instead of a released version. This list does not keep + track of unreleased or nightly versions. + - It is also mildly suspicious if a contract was compiled with a version that was + not the most recent at the time the contract was created. For contracts + created from other contracts, you have to follow the creation chain + back to a transaction and use the date of that transaction as creation date. + - It is highly suspicious if a contract was compiled with a compiler that + contains a known bug and the contract was created at a time where a newer + compiler version containing a fix was already released. + +The JSON file of known bugs below is an array of objects, one for each bug, +with the following keys: + +name + Unique name given to the bug +summary + Short description of the bug +description + Detailed description of the bug +link + URL of a website with more detailed information, optional +introduced + The first published compiler version that contained the bug, optional +fixed + The first published compiler version that did not contain the bug anymore +publish + The date at which the bug became known publicly, optional +severity + Severity of the bug: low, medium, high. Takes into account + discoverability in contract tests, likelihood of occurrence and + potential damage by exploits. +conditions + Conditions that have to be met to trigger the bug. Currently, this + is an object that can contain a boolean value ``optimizer``, which + means that the optimizer has to be switched on to enable the bug. + If no conditions are given, assume that the bug is present. + +.. literalinclude:: bugs.json + :language: js diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json new file mode 100644 index 00000000..64015d4a --- /dev/null +++ b/docs/bugs_by_version.json @@ -0,0 +1,297 @@ +{ + "0.1.0": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-07-10" + }, + "0.1.1": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-08-04" + }, + "0.1.2": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-08-20" + }, + "0.1.3": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-09-25" + }, + "0.1.4": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-09-30" + }, + "0.1.5": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-10-07" + }, + "0.1.6": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-10-16" + }, + "0.1.7": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-11-17" + }, + "0.2.0": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2015-12-02" + }, + "0.2.1": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2016-01-30" + }, + "0.2.2": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", + "AncientCompiler" + ], + "released": "2016-02-17" + }, + "0.3.0": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits" + ], + "released": "2016-03-11" + }, + "0.3.1": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits" + ], + "released": "2016-03-31" + }, + "0.3.2": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits" + ], + "released": "2016-04-18" + }, + "0.3.3": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin" + ], + "released": "2016-05-27" + }, + "0.3.4": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin" + ], + "released": "2016-05-31" + }, + "0.3.5": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin" + ], + "released": "2016-06-10" + }, + "0.3.6": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther" + ], + "released": "2016-08-10" + }, + "0.4.0": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "LibrariesNotCallableFromPayableFunctions" + ], + "released": "2016-09-08" + }, + "0.4.1": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "LibrariesNotCallableFromPayableFunctions" + ], + "released": "2016-09-09" + }, + "0.4.10": { + "bugs": [], + "released": "2017-03-15" + }, + "0.4.2": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3" + ], + "released": "2016-09-17" + }, + "0.4.3": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage" + ], + "released": "2016-10-25" + }, + "0.4.4": { + "bugs": [ + "IdentityPrecompileReturnIgnored" + ], + "released": "2016-10-31" + }, + "0.4.5": { + "bugs": [ + "IdentityPrecompileReturnIgnored", + "OptimizerStateKnowledgeNotResetForJumpdest" + ], + "released": "2016-11-21" + }, + "0.4.6": { + "bugs": [ + "IdentityPrecompileReturnIgnored" + ], + "released": "2016-11-22" + }, + "0.4.7": { + "bugs": [], + "released": "2016-12-15" + }, + "0.4.8": { + "bugs": [], + "released": "2017-01-13" + }, + "0.4.9": { + "bugs": [], + "released": "2017-01-31" + } +}
\ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 9c74aa51..457b280f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -136,5 +136,6 @@ Contents using-the-compiler.rst style-guide.rst common-patterns.rst + bugs.rst contributing.rst frequently-asked-questions.rst diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 7c3f87ee..1e92afa7 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -22,7 +22,11 @@ you should be more careful. This section will list some pitfalls and general security recommendations but can, of course, never be complete. Also, keep in mind that even if your smart contract code is bug-free, the compiler or the platform itself might -have a bug. +have a bug. A list of some publicly known security-relevant bugs of the compiler +can be found in the +:ref:`list of known bugs<known_bugs>`, which is also machine-readable. Note +that there is a bug bounty program that covers the code generator of the +Solidity compiler. As always, with open source documentation, please help us extend this section (especially, some examples would not hurt)! diff --git a/scripts/update_bugs_by_version.py b/scripts/update_bugs_by_version.py new file mode 100755 index 00000000..c4bc0c9b --- /dev/null +++ b/scripts/update_bugs_by_version.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# +# This script is used to generate the list of bugs per compiler version +# from the list of bugs. +# It updates the list in place and signals failure if there were changes. +# This makes it possible to use this script as part of CI to check +# that the list is up to date. + +import os +import json +import re +import sys + +def comp(version_string): + return [int(c) for c in version_string.split('.')] + +path = os.path.dirname(os.path.realpath(__file__)) +with open(path + '/../docs/bugs.json') as bugsFile: + bugs = json.load(bugsFile) + +versions = {} +with open(path + '/../Changelog.md') as changelog: + for line in changelog: + m = re.search(r'^### (\S+) \((\d+-\d+-\d+)\)$', line) + if m: + versions[m.group(1)] = {} + versions[m.group(1)]['released'] = m.group(2) + +for v in versions: + versions[v]['bugs'] = [] + for bug in bugs: + if 'introduced' in bug and comp(bug['introduced']) > comp(v): + continue + if comp(bug['fixed']) <= comp(v): + continue + versions[v]['bugs'] += [bug['name']] + +with open(path + '/../docs/bugs_by_version.json', 'r+') as bugs_by_version: + old_contents = bugs_by_version.read() + new_contents = json.dumps(versions, sort_keys=True, indent=4) + bugs_by_version.seek(0) + bugs_by_version.write(new_contents) + sys.exit(old_contents != new_contents)
\ No newline at end of file diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 99cac346..caf09a91 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -31,7 +31,10 @@ set -e REPO_ROOT="$(dirname "$0")"/.. SOLC="$REPO_ROOT/build/solc/solc" -# Compile all files in std and examples. +echo "Checking that the bug list is up to date..." +"$REPO_ROOT"/scripts/update_bugs_by_version.py + +echo "Compiling all files in std and examples..." for f in "$REPO_ROOT"/std/*.sol do |