Commit eb84b083 authored by Michael Fröwis's avatar Michael Fröwis Committed by chriseth

Static Analysis: fix blockBlockhash, warn on modifiers

parent 44327da2
...@@ -7,19 +7,19 @@ var AbstractAst = require('./abstractAstView') ...@@ -7,19 +7,19 @@ var AbstractAst = require('./abstractAstView')
function checksEffectsInteraction () { function checksEffectsInteraction () {
this.contracts = [] this.contracts = []
}
checksEffectsInteraction.prototype.visit = new AbstractAst().builder( checksEffectsInteraction.prototype.visit = new AbstractAst().builder(
(node) => common.isInteraction(node) || common.isEffect(node) || common.isLocalCall(node), (node) => common.isInteraction(node) || common.isEffect(node) || (common.isLocalCall(node) && !common.isBuiltinFunctionCall(node)),
this.contracts this.contracts
) )
}
checksEffectsInteraction.prototype.report = function (compilationResults) { checksEffectsInteraction.prototype.report = function (compilationResults) {
var warnings = [] var warnings = []
var cg = callGraph.buildGlobalFuncCallGraph(this.contracts) var cg = callGraph.buildGlobalFuncCallGraph(this.contracts)
if (this.contracts.some((item) => item.modifiers.length > 0)) this.warning.push({ warning: `Modifiers are currently not supported by this analysis.` }) if (this.contracts.some((item) => item.modifiers.length > 0)) warnings.push({ warning: `Modifiers are currently not supported by the '${name}' static analysis.` })
this.contracts.forEach((contract) => { this.contracts.forEach((contract) => {
contract.functions.forEach((func) => { contract.functions.forEach((func) => {
......
...@@ -7,19 +7,19 @@ var AbstractAst = require('./abstractAstView') ...@@ -7,19 +7,19 @@ var AbstractAst = require('./abstractAstView')
function constantFunctions () { function constantFunctions () {
this.contracts = [] this.contracts = []
}
constantFunctions.prototype.visit = new AbstractAst().builder( constantFunctions.prototype.visit = new AbstractAst().builder(
(node) => common.isLowLevelCall(node) || common.isExternalDirectCall(node) || common.isEffect(node) || common.isLocalCall(node) || common.isInlineAssembly(node), (node) => common.isLowLevelCall(node) || common.isExternalDirectCall(node) || common.isEffect(node) || (common.isLocalCall(node) && !common.isBuiltinFunctionCall(node)) || common.isInlineAssembly(node),
this.contracts this.contracts
) )
}
constantFunctions.prototype.report = function (compilationResults) { constantFunctions.prototype.report = function (compilationResults) {
var warnings = [] var warnings = []
var cg = callGraph.buildGlobalFuncCallGraph(this.contracts) var cg = callGraph.buildGlobalFuncCallGraph(this.contracts)
if (this.contracts.some((item) => item.modifiers.length > 0)) this.warning.push({ warning: `Modifiers are currently not supported by this analysis.` }) if (this.contracts.some((item) => item.modifiers.length > 0)) warnings.push({ warning: `Modifiers are currently not supported by the '${name}' static analysis.` })
this.contracts.forEach((contract) => { this.contracts.forEach((contract) => {
if (!common.isFullyImplementedContract(contract.node)) return if (!common.isFullyImplementedContract(contract.node)) return
......
...@@ -41,6 +41,17 @@ var basicFunctionTypes = { ...@@ -41,6 +41,17 @@ var basicFunctionTypes = {
DELEGATECALL: buildFunctionSignature([], [basicTypes.BOOL], false) DELEGATECALL: buildFunctionSignature([], [basicTypes.BOOL], false)
} }
var builtinFunctions = {
'keccak256()': true,
'sha3()': true,
'sha256()': true,
'ripemd160()': true,
'ecrecover(bytes32,uint8,bytes32,bytes32)': true,
'addmod(uint256,uint256,uint256)': true,
'mulmod(uint256,uint256,uint256)': true,
'selfdestruct(address)': true
}
var lowLevelCallTypes = { var lowLevelCallTypes = {
CALL: { ident: 'call', type: basicFunctionTypes.CALL }, CALL: { ident: 'call', type: basicFunctionTypes.CALL },
CALLCODE: { ident: 'callcode', type: basicFunctionTypes.CALL }, CALLCODE: { ident: 'callcode', type: basicFunctionTypes.CALL },
...@@ -182,6 +193,10 @@ function isInlineAssembly (node) { ...@@ -182,6 +193,10 @@ function isInlineAssembly (node) {
// #################### Complex Node Identification // #################### Complex Node Identification
function isBuiltinFunctionCall (node) {
return isLocalCall(node) && builtinFunctions[getLocalCallName(node) + '(' + getFunctionCallTypeParameterType(node) + ')'] === true
}
function isStorageVariableDeclaration (node) { function isStorageVariableDeclaration (node) {
return isVariableDeclaration(node) && expressionType(node, basicRegex.REFTYPE) return isVariableDeclaration(node) && expressionType(node, basicRegex.REFTYPE)
} }
...@@ -297,7 +312,7 @@ function isMemberAccess (node, retType, accessor, accessorType, memberName) { ...@@ -297,7 +312,7 @@ function isMemberAccess (node, retType, accessor, accessorType, memberName) {
} }
function isSpecialVariableAccess (node, varType) { function isSpecialVariableAccess (node, varType) {
return isMemberAccess(node, varType.type, varType.obj, varType.obj, varType.member) return isMemberAccess(node, exactMatch(utils.escapeRegExp(varType.type)), varType.obj, varType.obj, varType.member)
} }
// #################### Node Identification Primitives // #################### Node Identification Primitives
...@@ -340,7 +355,6 @@ function exactMatch (regexStr) { ...@@ -340,7 +355,6 @@ function exactMatch (regexStr) {
* @param {Array} returnTypes * @param {Array} returnTypes
* list of return type names * list of return type names
* @return {Boolean} isPayable * @return {Boolean} isPayable
* CAUTION: only needed in low level call signature or message-calls (other contracts, this.)
*/ */
function buildFunctionSignature (paramTypes, returnTypes, isPayable) { function buildFunctionSignature (paramTypes, returnTypes, isPayable) {
return 'function (' + utils.concatWithSeperator(paramTypes, ',') + ')' + ((isPayable) ? ' payable' : '') + ((returnTypes.length) ? ' returns (' + utils.concatWithSeperator(returnTypes, ',') + ')' : '') return 'function (' + utils.concatWithSeperator(paramTypes, ',') + ')' + ((isPayable) ? ' payable' : '') + ((returnTypes.length) ? ' returns (' + utils.concatWithSeperator(returnTypes, ',') + ')' : '')
...@@ -400,6 +414,7 @@ module.exports = { ...@@ -400,6 +414,7 @@ module.exports = {
isCallToNonConstLocalFunction: isCallToNonConstLocalFunction, isCallToNonConstLocalFunction: isCallToNonConstLocalFunction,
isPlusPlusUnaryOperation: isPlusPlusUnaryOperation, isPlusPlusUnaryOperation: isPlusPlusUnaryOperation,
isMinusMinusUnaryOperation: isMinusMinusUnaryOperation, isMinusMinusUnaryOperation: isMinusMinusUnaryOperation,
isBuiltinFunctionCall: isBuiltinFunctionCall,
// #################### Trivial Node Identification // #################### Trivial Node Identification
isFunctionDefinition: isFunctionDefinition, isFunctionDefinition: isFunctionDefinition,
......
...@@ -1066,6 +1066,59 @@ test('staticAnalysisCommon.isInlineAssembly', function (t) { ...@@ -1066,6 +1066,59 @@ test('staticAnalysisCommon.isInlineAssembly', function (t) {
// #################### Complex Node Identification // #################### Complex Node Identification
test('staticAnalysisCommon.isBuiltinFunctionCall', function (t) {
t.plan(2)
var selfdestruct = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (address)',
'value': 'selfdestruct'
},
'name': 'Identifier'
},
{
'attributes': {
'type': 'address',
'value': 'a'
},
'name': 'Identifier'
}
],
'name': 'FunctionCall'
}
var localCall = {
'attributes': {
'type': 'tuple()',
'type_conversion': false
},
'children': [
{
'attributes': {
'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli'
},
'name': 'Identifier'
},
{
'attributes': {
'type': 'struct Ballot.Voter storage pointer',
'value': 'x'
},
'name': 'Identifier'
}
],
'name': 'FunctionCall'
}
t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin')
t.notOk(common.isBuiltinFunctionCall(localCall), 'local call is not builtin')
})
test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) { test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) {
t.plan(3) t.plan(3)
var node1 = { var node1 = {
...@@ -1522,23 +1575,17 @@ test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) { ...@@ -1522,23 +1575,17 @@ test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) {
'type': 'function (struct Ballot.Voter storage pointer)', 'type': 'function (struct Ballot.Voter storage pointer)',
'value': 'bli' 'value': 'bli'
}, },
'id': 37, 'name': 'Identifier'
'name': 'Identifier',
'src': '540:3:0'
}, },
{ {
'attributes': { 'attributes': {
'type': 'struct Ballot.Voter storage pointer', 'type': 'struct Ballot.Voter storage pointer',
'value': 'x' 'value': 'x'
}, },
'id': 38, 'name': 'Identifier'
'name': 'Identifier',
'src': '544:1:0'
} }
], ],
'id': 39, 'name': 'FunctionCall'
'name': 'FunctionCall',
'src': '540:6:0'
} }
t.ok(common.isCallToNonConstLocalFunction(node1), 'should be call to non const Local func') t.ok(common.isCallToNonConstLocalFunction(node1), 'should be call to non const Local func')
...@@ -1595,10 +1642,26 @@ test('staticAnalysisCommon.isBlockTimestampAccess', function (t) { ...@@ -1595,10 +1642,26 @@ test('staticAnalysisCommon.isBlockTimestampAccess', function (t) {
test('staticAnalysisCommon.isBlockBlockhashAccess', function (t) { test('staticAnalysisCommon.isBlockBlockhashAccess', function (t) {
t.plan(4) t.plan(4)
var node = { name: 'MemberAccess', children: [{attributes: { value: 'block', type: 'block' }}], attributes: { value: 'blockhash', type: 'bytes32' } } var node = {
'attributes': {
'member_name': 'blockhash',
'type': 'function (uint256) returns (bytes32)'
},
'children': [
{
'attributes': {
'type': 'block',
'value': 'block'
},
'name': 'Identifier'
}
],
'name': 'MemberAccess'
}
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work') t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work') t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
t.ok(common.isBlockBlockHashAccess(node), 'blockhash should work') t.ok(common.isBlockBlockHashAccess(node), 'blockhash should work') // todo:
t.notOk(common.isNowAccess(node), 'is now used should not work') t.notOk(common.isNowAccess(node), 'is now used should not work')
}) })
......
var test = require('tape') var test = require('tape')
var StatRunner = require('../../babelify-src/app/staticanalysis/staticAnalysisRunner') var StatRunner = require('../../src/app/staticanalysis/staticAnalysisRunner')
// const util = require('util') // const util = require('util')
var solc = require('solc') var solc = require('solc')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment