diff options
-rw-r--r-- | .circleci/config.yml | 12 | ||||
-rw-r--r-- | docs/bugs.json | 9 | ||||
-rw-r--r-- | docs/bugs.rst | 8 | ||||
-rw-r--r-- | docs/bugs_by_version.json | 48 | ||||
-rwxr-xr-x | test/buglistTests.js | 45 | ||||
-rw-r--r-- | test/buglist_test_vectors.md | 69 |
6 files changed, 187 insertions, 4 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index e3596d2b..4514626b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -179,6 +179,17 @@ jobs: name: Check spelling command: ~/.local/bin/codespell -S "*.enc,.git" -I ./scripts/codespell_whitelist.txt + test_buglist: + docker: + - image: circleci/node + environment: + TERM: xterm + steps: + - checkout + - run: + name: Test buglist + command: ./test/buglistTests.js + test_x86_linux: docker: - image: buildpack-deps:artful @@ -252,6 +263,7 @@ workflows: build_all: jobs: - test_check_spelling: *build_on_tags + - test_buglist: *build_on_tags - build_emscripten: *build_on_tags - test_emscripten_solcjs: <<: *build_on_tags diff --git a/docs/bugs.json b/docs/bugs.json index b464be18..3f20077f 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,13 @@ [ + { + "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.", diff --git a/docs/bugs.rst b/docs/bugs.rst index 7629830d..6f315a65 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -56,6 +56,14 @@ 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 JavaScript regular expressions that are to be matched + against the source code ("source-regex") to find out if the + smart contract contains the bug or not. 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 regular + expression should be applied to the source code after stripping + comments. .. literalinclude:: bugs.json :language: js diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 2fe1d226..b400121f 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -69,6 +69,7 @@ }, "0.1.4": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -86,6 +87,7 @@ }, "0.1.5": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -103,6 +105,7 @@ }, "0.1.6": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -121,6 +124,7 @@ }, "0.1.7": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -139,6 +143,7 @@ }, "0.2.0": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -157,6 +162,7 @@ }, "0.2.1": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -175,6 +181,7 @@ }, "0.2.2": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -193,6 +200,7 @@ }, "0.3.0": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -211,6 +219,7 @@ }, "0.3.1": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -228,6 +237,7 @@ }, "0.3.2": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -245,6 +255,7 @@ }, "0.3.3": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -261,6 +272,7 @@ }, "0.3.4": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -277,6 +289,7 @@ }, "0.3.5": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -293,6 +306,7 @@ }, "0.3.6": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -307,6 +321,7 @@ }, "0.4.0": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -321,6 +336,7 @@ }, "0.4.1": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -335,6 +351,7 @@ }, "0.4.10": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -345,6 +362,7 @@ }, "0.4.11": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -354,6 +372,7 @@ }, "0.4.12": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput" @@ -362,6 +381,7 @@ }, "0.4.13": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput" @@ -370,6 +390,7 @@ }, "0.4.14": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue" ], @@ -377,32 +398,40 @@ }, "0.4.15": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], "released": "2017-08-08" }, "0.4.16": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], "released": "2017-08-24" }, "0.4.17": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], "released": "2017-09-21" }, "0.4.18": { - "bugs": [], + "bugs": [ + "NestedArrayFunctionCallDecoder" + ], "released": "2017-10-18" }, "0.4.19": { - "bugs": [], + "bugs": [ + "NestedArrayFunctionCallDecoder" + ], "released": "2017-11-30" }, "0.4.2": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -415,11 +444,15 @@ "released": "2016-09-17" }, "0.4.20": { - "bugs": [], + "bugs": [ + "NestedArrayFunctionCallDecoder" + ], "released": "2018-02-14" }, "0.4.21": { - "bugs": [], + "bugs": [ + "NestedArrayFunctionCallDecoder" + ], "released": "2018-03-07" }, "0.4.22": { @@ -438,6 +471,7 @@ }, "0.4.3": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -450,6 +484,7 @@ }, "0.4.4": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -461,6 +496,7 @@ }, "0.4.5": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -473,6 +509,7 @@ }, "0.4.6": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -484,6 +521,7 @@ }, "0.4.7": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -494,6 +532,7 @@ }, "0.4.8": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", @@ -504,6 +543,7 @@ }, "0.4.9": { "bugs": [ + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", diff --git a/test/buglistTests.js b/test/buglistTests.js new file mode 100755 index 00000000..6b7df2f2 --- /dev/null +++ b/test/buglistTests.js @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +"use strict"; + +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 + +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") + + var regex = RegExp(bugsByName[name].check['regex-source']) + for (var i in buggy) + { + if (!regex.exec(buggy[i])) + { + throw "Bug " + name + ": Buggy source does not match: " + buggy[i] + } + } + for (var i in fine) + { + if (regex.exec(fine[i])) + { + throw "Bug " + name + ": Non-buggy source matches: " + fine[i] + } + } +} diff --git a/test/buglist_test_vectors.md b/test/buglist_test_vectors.md new file mode 100644 index 00000000..ce95403b --- /dev/null +++ b/test/buglist_test_vectors.md @@ -0,0 +1,69 @@ +# NestedArrayFunctionCallDecoder + +## buggy + +function f() pure returns (uint[2][2]) { } + +-- + +function f() returns (uint[2][2] a) { } + +-- + +function f() returns (uint x, uint[200][2] a) { } + +-- + +function f() returns (uint[200][2] a, uint x) { } + +-- + +function f() returns (uint[200][2] a, uint x); + +-- + +function f() returns ( + uint + [ + 200 + ] + [2] + a, uint x); + +-- + +function f() returns ( + uint + [ + ContractName.ConstantName + ] + [2] + a, uint x); + +## fine + +function f() returns (uint[2]) { } + +-- + +function f() public pure returns (uint[2][] a) { } + +-- + +function f() public pure returns (uint[ 2 ] [ ] a) { } + +-- + +function f() public pure returns (uint x, uint[] a) { } + +-- + +function f(uint[2][2]) { } + +-- + +function f() m(uint[2][2]) { } + +-- + +function f() returns (uint, uint) { uint[2][2] memory x; } |