aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--circle.yml17
-rw-r--r--docs/bugs.json26
-rw-r--r--docs/bugs.rst13
-rw-r--r--docs/bugs_by_version.json56
-rwxr-xr-xtest/buglistTests.js134
5 files changed, 228 insertions, 18 deletions
diff --git a/circle.yml b/circle.yml
index 4ce3082b..fb504ba1 100644
--- a/circle.yml
+++ b/circle.yml
@@ -155,6 +155,23 @@ jobs:
- store_artifacts: *solc_artifact
- persist_to_workspace: *all_artifacts
+ test_buglist:
+ docker:
+ - image: circleci/node
+ environment:
+ TERM: xterm
+ steps:
+ - checkout
+ - run:
+ name: JS deps
+ command: |
+ npm install download
+ npm install JSONPath
+ npm install mktemp
+ - run:
+ name: Test buglist
+ command: ./test/buglistTests.js
+
test_x86_linux:
docker:
- image: buildpack-deps:artful
diff --git a/docs/bugs.json b/docs/bugs.json
index 560176d1..28c0fe62 100644
--- a/docs/bugs.json
+++ b/docs/bugs.json
@@ -1,12 +1,4 @@
[
- {
- "name": "EventStructWrongData",
- "summary": "Using structs in events logged wrong data.",
- "description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.",
- "introduced": "0.4.17",
- "fixed": "0.4.25",
- "severity": "very low"
- },
{
"name": "ExpExponentCleanup",
"summary": "Using the ** operator with an exponent of type shorter than 256 bits can result in unexpected values.",
@@ -16,6 +8,24 @@
"check": {"regex-source": "[^/]\\*\\* *[^/0-9 ]"}
},
{
+ "name": "EventStructWrongData",
+ "summary": "Using structs in events logged wrong data.",
+ "description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.",
+ "introduced": "0.4.17",
+ "fixed": "0.4.25",
+ "severity": "very low",
+ "check": {"ast-compact-json-path": "$..[?(@.nodeType === 'EventDefinition')]..[?(@.nodeType === 'UserDefinedTypeName' && @.typeDescriptions.typeString.startsWith('struct'))]"}
+ },
+ {
+ "name": "NestedArrayFunctionCallDecoder",
+ "summary": "Calling functions that return multi-dimensional fixed-size arrays can result in memory corruption.",
+ "description": "If Solidity code calls a function that returns a multi-dimensional fixed-size array, array elements are incorrectly interpreted as memory pointers and thus can cause memory corruption if the return values are accessed. Calling functions with multi-dimensional fixed-size arrays is unaffected as is returning fixed-size arrays from function calls. The regular expression only checks if such functions are present, not if they are called, which is required for the contract to be affected.",
+ "introduced": "0.1.4",
+ "fixed": "0.4.22",
+ "severity": "medium",
+ "check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"}
+ },
+ {
"name": "OneOfTwoConstructorsSkipped",
"summary": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored.",
"description": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored. There will be a compiler warning about the old-style constructor, so contracts only using new-style constructors are fine.",
diff --git a/docs/bugs.rst b/docs/bugs.rst
index 7629830d..f7522183 100644
--- a/docs/bugs.rst
+++ b/docs/bugs.rst
@@ -56,6 +56,19 @@ conditions
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.
+check
+ This field contains different checks that report whether the smart contract
+ contains the bug or not. The first type of check are Javascript regular
+ expressions that are to be matched against the source code ("source-regex")
+ if the bug is present. If there is no match, then the bug is very likely
+ not present. If there is a match, the bug might be present. For improved
+ accuracy, the checks should be applied to the source code after stripping
+ comments.
+ The second type of check are patterns to be checked on the compact AST of
+ the Solidity program ("ast-compact-json-path"). The specified search query
+ is a `JsonPath <https://github.com/json-path/JsonPath>`_ expression.
+ If at least one path of the Solidity AST matches the query, the bug is
+ likely present.
.. literalinclude:: bugs.json
:language: js
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index d678bf21..88a480b2 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -74,6 +74,7 @@
"0.1.4": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -92,6 +93,7 @@
"0.1.5": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -110,6 +112,7 @@
"0.1.6": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -129,6 +132,7 @@
"0.1.7": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -148,6 +152,7 @@
"0.2.0": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -167,6 +172,7 @@
"0.2.1": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -186,6 +192,7 @@
"0.2.2": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -205,6 +212,7 @@
"0.3.0": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -224,6 +232,7 @@
"0.3.1": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -242,6 +251,7 @@
"0.3.2": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -260,6 +270,7 @@
"0.3.3": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -277,6 +288,7 @@
"0.3.4": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -294,6 +306,7 @@
"0.3.5": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -311,6 +324,7 @@
"0.3.6": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -326,6 +340,7 @@
"0.4.0": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -341,6 +356,7 @@
"0.4.1": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -356,6 +372,7 @@
"0.4.10": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -367,6 +384,7 @@
"0.4.11": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -377,6 +395,7 @@
"0.4.12": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput"
@@ -386,6 +405,7 @@
"0.4.13": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput"
@@ -395,6 +415,7 @@
"0.4.14": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue"
],
@@ -403,6 +424,7 @@
"0.4.15": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-08-08"
@@ -410,35 +432,40 @@
"0.4.16": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-08-24"
},
"0.4.17": {
"bugs": [
- "EventStructWrongData",
"ExpExponentCleanup",
+ "EventStructWrongData",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-09-21"
},
"0.4.18": {
"bugs": [
+ "ExpExponentCleanup",
"EventStructWrongData",
- "ExpExponentCleanup"
+ "NestedArrayFunctionCallDecoder"
],
"released": "2017-10-18"
},
"0.4.19": {
"bugs": [
+ "ExpExponentCleanup",
"EventStructWrongData",
- "ExpExponentCleanup"
+ "NestedArrayFunctionCallDecoder"
],
"released": "2017-11-30"
},
"0.4.2": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -452,43 +479,46 @@
},
"0.4.20": {
"bugs": [
+ "ExpExponentCleanup",
"EventStructWrongData",
- "ExpExponentCleanup"
+ "NestedArrayFunctionCallDecoder"
],
"released": "2018-02-14"
},
"0.4.21": {
"bugs": [
+ "ExpExponentCleanup",
"EventStructWrongData",
- "ExpExponentCleanup"
+ "NestedArrayFunctionCallDecoder"
],
"released": "2018-03-07"
},
"0.4.22": {
"bugs": [
- "EventStructWrongData",
"ExpExponentCleanup",
+ "EventStructWrongData",
"OneOfTwoConstructorsSkipped"
],
"released": "2018-04-16"
},
"0.4.23": {
"bugs": [
- "EventStructWrongData",
- "ExpExponentCleanup"
+ "ExpExponentCleanup",
+ "EventStructWrongData"
],
"released": "2018-04-19"
},
"0.4.24": {
"bugs": [
- "EventStructWrongData",
- "ExpExponentCleanup"
+ "ExpExponentCleanup",
+ "EventStructWrongData"
],
"released": "2018-05-16"
},
"0.4.3": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -502,6 +532,7 @@
"0.4.4": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -514,6 +545,7 @@
"0.4.5": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -527,6 +559,7 @@
"0.4.6": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -539,6 +572,7 @@
"0.4.7": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -550,6 +584,7 @@
"0.4.8": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -561,6 +596,7 @@
"0.4.9": {
"bugs": [
"ExpExponentCleanup",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
diff --git a/test/buglistTests.js b/test/buglistTests.js
new file mode 100755
index 00000000..f24f0cb6
--- /dev/null
+++ b/test/buglistTests.js
@@ -0,0 +1,134 @@
+#!/usr/bin/env node
+
+"use strict";
+
+var util = require('util')
+var exec = util.promisify(require('child_process').exec)
+var mktemp = require('mktemp');
+var download = require('download')
+var JSONPath = require('JSONPath')
+var fs = require('fs')
+var bugs = JSON.parse(fs.readFileSync(__dirname + '/../docs/bugs.json', 'utf8'))
+
+var bugsByName = {}
+for (var i in bugs)
+{
+ if (bugs[i].name in bugsByName)
+ {
+ throw "Duplicate bug name: " + bugs[i].name
+ }
+ bugsByName[bugs[i].name] = bugs[i]
+}
+
+var tests = fs.readFileSync(__dirname + '/buglist_test_vectors.md', 'utf8')
+
+var testVectorParser = /\s*#\s+(\S+)\s+## buggy\n([^#]*)## fine\n([^#]*)/g
+
+runTests()
+
+async function runTests()
+{
+ var result;
+ while ((result = testVectorParser.exec(tests)) !== null)
+ {
+ var name = result[1]
+ var buggy = result[2].split('\n--\n')
+ var fine = result[3].split('\n--\n')
+ console.log("Testing " + name + " with " + buggy.length + " buggy and " + fine.length + " fine instances")
+
+ try {
+ await checkRegex(name, buggy, fine)
+ await checkJSONPath(name, buggy, fine)
+ } catch (err) {
+ console.error("Error: " + err)
+ }
+ }
+}
+
+function checkRegex(name, buggy, fine)
+{
+ return new Promise(function(resolve, reject) {
+ var regexStr = bugsByName[name].check['regex-source']
+ if (regexStr !== undefined)
+ {
+ var regex = RegExp(regexStr)
+ for (var i in buggy)
+ {
+ if (!regex.exec(buggy[i]))
+ {
+ reject("Bug " + name + ": Buggy source does not match: " + buggy[i])
+ }
+ }
+ for (var i in fine)
+ {
+ if (regex.exec(fine[i]))
+ {
+ reject("Bug " + name + ": Non-buggy source matches: " + fine[i])
+ }
+ }
+ }
+ resolve()
+ })
+}
+
+async function checkJSONPath(name, buggy, fine)
+{
+ var jsonPath = bugsByName[name].check['ast-compact-json-path']
+ if (jsonPath !== undefined)
+ {
+ var url = "http://github.com/ethereum/solidity/releases/download/v" + bugsByName[name].introduced + "/solc-static-linux"
+ try {
+ var tmpdir = await mktemp.createDir('XXXXX')
+ var binary = tmpdir + "/solc-static-linux"
+ await download(url, tmpdir)
+ exec("chmod +x " + binary)
+ for (var i in buggy)
+ {
+ var result = await checkJsonPathTest(buggy[i], tmpdir, binary, jsonPath, i)
+ if (!result)
+ throw "Bug " + name + ": Buggy source does not contain path: " + buggy[i]
+ }
+ for (var i in fine)
+ {
+ var result = await checkJsonPathTest(fine[i], tmpdir, binary, jsonPath, i + buggy.length)
+ if (result)
+ throw "Bug " + name + ": Non-buggy source contains path: " + fine[i]
+ }
+ exec("rm -r " + tmpdir)
+ } catch (err) {
+ throw err
+ }
+ }
+}
+
+function checkJsonPathTest(code, tmpdir, binary, query, idx) {
+ return new Promise(function(resolve, reject) {
+ var solFile = tmpdir + "/jsonPath" + idx + ".sol"
+ var astFile = tmpdir + "/ast" + idx + ".json"
+ writeFilePromise(solFile, code)
+ .then(() => {
+ return exec(binary + " --ast-compact-json " + solFile + " > " + astFile)
+ })
+ .then(() => {
+ var jsonRE = /(\{[\s\S]*\})/
+ var ast = JSON.parse(jsonRE.exec(fs.readFileSync(astFile, 'utf8'))[0])
+ var result = JSONPath({json: ast, path: query})
+ if (result.length > 0)
+ resolve(true)
+ else
+ resolve(false)
+ })
+ .catch((err) => {
+ reject(err)
+ })
+ })
+}
+
+function writeFilePromise(filename, data) {
+ return new Promise(function(resolve, reject) {
+ fs.writeFile(filename, data, 'utf8', function(err) {
+ if (err) reject(err)
+ else resolve(data)
+ })
+ })
+}