diff options
author | chriseth <chris@ethereum.org> | 2018-09-11 18:06:31 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-11 18:06:31 +0800 |
commit | e913b753b9a4bda78f8c47df6b48429391a0fd69 (patch) | |
tree | 7abc5dc1b72d2b8612980860534c4b2407824ba6 | |
parent | 6402b8382661ae738faa07c9dbd9af6358e61f6c (diff) | |
parent | 69320472afe8c1c5031c3243ef6299cbfcd2e523 (diff) | |
download | dexon-solidity-e913b753b9a4bda78f8c47df6b48429391a0fd69.tar dexon-solidity-e913b753b9a4bda78f8c47df6b48429391a0fd69.tar.gz dexon-solidity-e913b753b9a4bda78f8c47df6b48429391a0fd69.tar.bz2 dexon-solidity-e913b753b9a4bda78f8c47df6b48429391a0fd69.tar.lz dexon-solidity-e913b753b9a4bda78f8c47df6b48429391a0fd69.tar.xz dexon-solidity-e913b753b9a4bda78f8c47df6b48429391a0fd69.tar.zst dexon-solidity-e913b753b9a4bda78f8c47df6b48429391a0fd69.zip |
Merge pull request #4927 from ethereum/struct_event_jsonpath_0425
[backport] Buglist check script supports json paths
-rw-r--r-- | circle.yml | 17 | ||||
-rw-r--r-- | docs/bugs.json | 26 | ||||
-rw-r--r-- | docs/bugs.rst | 13 | ||||
-rw-r--r-- | docs/bugs_by_version.json | 56 | ||||
-rwxr-xr-x | test/buglistTests.js | 134 |
5 files changed, 228 insertions, 18 deletions
@@ -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) + }) + }) +} |