Static Analysis: improvements suggested by chriseth

pull/1/head
soad003 8 years ago committed by chriseth
parent 85ae6115e5
commit 7ef1afc331
  1. 46
      src/app/staticanalysis/modules/abstractAstView.js
  2. 28
      src/app/staticanalysis/modules/checksEffectsInteraction.js
  3. 29
      src/app/staticanalysis/modules/constantFunctions.js
  4. 1
      src/app/staticanalysis/modules/list.js
  5. 3
      src/app/staticanalysis/modules/staticAnalysisCommon.js

@ -2,11 +2,20 @@ var common = require('./staticAnalysisCommon')
var AstWalker = require('ethereum-remix').util.AstWalker
function abstractAstView () {
this.contracts = null
this.contracts = []
this.currentContractIndex = null
this.currentFunctionIndex = null
this.currentModifierIndex = null
this.isFunctionNotModifier = false
/*
file1: contract c{}
file2: import "file1" as x; contract c{}
therefore we have two contracts with the same name c. At the moment this is not handled because alias name "x" is not
available in the current AST implementation thus can not be resolved.
Additionally the fullQuallified function names e.g. [contractName].[functionName](param1Type, param2Type, ... ) must be prefixed to
fully support this and when inheritance is resolved it must include alias resolving e.g x.c = file1.c
*/
this.multipleContractsWithSameName = false
}
/**
@ -33,8 +42,7 @@ function abstractAstView () {
* @contractsOut {list} return list for high level AST view
* @return {ASTNode -> void} returns a function that can be used as visit function for static analysis modules, to build up a higher level AST view for further analysis.
*/
abstractAstView.prototype.builder = function (relevantNodeFilter, contractsOut) {
this.contracts = contractsOut
abstractAstView.prototype.build_visit = function (relevantNodeFilter) {
var that = this
return function (node) {
if (common.isContractDefinition(node)) {
@ -49,9 +57,6 @@ abstractAstView.prototype.builder = function (relevantNodeFilter, contractsOut)
var currentContract = getCurrentContract(that)
var inheritsFromName = common.getInheritsFromName(node)
currentContract.inheritsFrom.push(inheritsFromName)
// add variables from inherited contracts
var inheritsFrom = that.contracts.find((contract) => common.getContractName(contract.node) === inheritsFromName)
currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables)
} else if (common.isFunctionDefinition(node)) {
setCurrentFunction(that, {
node: node,
@ -76,7 +81,36 @@ abstractAstView.prototype.builder = function (relevantNodeFilter, contractsOut)
}
}
abstractAstView.prototype.build_report = function (wrap) {
var that = this
return function (compilationResult) {
resolveStateVariablesInHierarchy(that.contracts)
return wrap(that.contracts, that.multipleContractsWithSameName)
}
}
function resolveStateVariablesInHierarchy (contracts) {
contracts.map((c) => {
resolveStateVariablesInHierarchyForContract(c, contracts)
})
}
function resolveStateVariablesInHierarchyForContract (currentContract, contracts) {
currentContract.inheritsFrom.map((inheritsFromName) => {
// add variables from inherited contracts
var inheritsFrom = contracts.find((contract) => common.getContractName(contract.node) === inheritsFromName)
if (inheritsFrom) {
currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables)
} else {
console.log('abstractAstView.js: could not find contract defintion inherited from ' + inheritsFromName)
}
})
}
function setCurrentContract (that, contract) {
var name = common.getContractName(contract.node)
if (that.contracts.map((c) => common.getContractName(c.node)).filter((n) => n === name).length > 0) {
console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment')
that.multipleContractsWithSameName = true
}
that.currentContractIndex = (that.contracts.push(contract) - 1)
}

@ -6,22 +6,25 @@ var fcallGraph = require('./functionCallGraph')
var AbstractAst = require('./abstractAstView')
function checksEffectsInteraction () {
this.contracts = []
var that = this
checksEffectsInteraction.prototype.visit = new AbstractAst().builder(
(node) => common.isInteraction(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node),
that.contracts
this.abstractAst = new AbstractAst()
this.visit = this.abstractAst.build_visit(
(node) => common.isInteraction(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node)
)
this.report = this.abstractAst.build_report(report)
}
checksEffectsInteraction.prototype.report = function (compilationResults) {
checksEffectsInteraction.prototype.visit = function () { throw new Error('checksEffectsInteraction.js no visit function set upon construction') }
checksEffectsInteraction.prototype.report = function () { throw new Error('checksEffectsInteraction.js no report function set upon construction') }
function report (contracts, multipleContractsWithSameName) {
var warnings = []
var hasModifiers = this.contracts.some((item) => item.modifiers.length > 0)
var hasModifiers = contracts.some((item) => item.modifiers.length > 0)
var callGraph = fcallGraph.buildGlobalFuncCallGraph(this.contracts)
var callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts)
this.contracts.forEach((contract) => {
contracts.forEach((contract) => {
contract.functions.forEach((func) => {
func.changesState = checkIfChangesState(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters),
getContext(callGraph, contract, func))
@ -30,9 +33,10 @@ checksEffectsInteraction.prototype.report = function (compilationResults) {
contract.functions.forEach((func) => {
if (isPotentialVulnerableFunction(func, getContext(callGraph, contract, func))) {
var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
var comments = (hasModifiers) ? '<br/><i>Note:</i>Modifiers are currently not considered by the this static analysis.' : ''
var comments = (hasModifiers) ? '<br/><i>Note:</i> Modifiers are currently not considered by this static analysis.' : ''
comments += (multipleContractsWithSameName) ? '<br/><i>Note:</i> Import aliases are currently not supported by this static analysis.' : ''
warnings.push({
warning: `Potential Violation of Checks-Effects-Interaction pattern in <i>${funcName}</i>: Could potentially lead to re-entrancy vulnerability.${comments}`,
warning: `Potential Violation of Checks-Effects-Interaction pattern in <i>${funcName}</i>: Could potentially lead to re-entrancy vulnerability. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy'
})

@ -6,22 +6,26 @@ var fcallGraph = require('./functionCallGraph')
var AbstractAst = require('./abstractAstView')
function constantFunctions () {
this.contracts = []
var that = this
this.abstractAst = new AbstractAst()
constantFunctions.prototype.visit = new AbstractAst().builder(
(node) => common.isLowLevelCall(node) || common.isTransfer(node) || common.isExternalDirectCall(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node) || common.isInlineAssembly(node) || common.isNewExpression(node),
that.contracts
this.visit = this.abstractAst.build_visit(
(node) => common.isLowLevelCall(node) || common.isTransfer(node) || common.isExternalDirectCall(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node) || common.isInlineAssembly(node) || common.isNewExpression(node)
)
this.report = this.abstractAst.build_report(report)
}
constantFunctions.prototype.report = function (compilationResults) {
constantFunctions.prototype.visit = function () { throw new Error('constantFunctions.js no visit function set upon construction') }
constantFunctions.prototype.report = function () { throw new Error('constantFunctions.js no report function set upon construction') }
function report (contracts, multipleContractsWithSameName) {
var warnings = []
var hasModifiers = this.contracts.some((item) => item.modifiers.length > 0)
var hasModifiers = contracts.some((item) => item.modifiers.length > 0)
var callGraph = fcallGraph.buildGlobalFuncCallGraph(this.contracts)
var callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts)
this.contracts.forEach((contract) => {
contracts.forEach((contract) => {
contract.functions.forEach((func) => {
func.potentiallyshouldBeConst = checkIfShouldBeConstant(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters),
getContext(callGraph, contract, func))
@ -30,16 +34,17 @@ constantFunctions.prototype.report = function (compilationResults) {
contract.functions.filter((func) => common.hasFunctionBody(func.node)).forEach((func) => {
if (common.isConstantFunction(func.node) !== func.potentiallyshouldBeConst) {
var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
var comments = (hasModifiers) ? '<br/><i>Note:</i>Modifiers are currently not considered by the this static analysis.' : ''
var comments = (hasModifiers) ? '<br/><i>Note:</i> Modifiers are currently not considered by this static analysis.' : ''
comments += (multipleContractsWithSameName) ? '<br/><i>Note:</i> Import aliases are currently not supported by this static analysis.' : ''
if (func.potentiallyshouldBeConst) {
warnings.push({
warning: `<i>${funcName}</i>: Potentially should be constant but is not.${comments}`,
warning: `<i>${funcName}</i>: Potentially should be constant but is not. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
})
} else {
warnings.push({
warning: `<i>${funcName}</i>: Is constant but potentially should not be.${comments}`,
warning: `<i>${funcName}</i>: Is constant but potentially should not be. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
})

@ -4,6 +4,7 @@ module.exports = [
require('./thisLocal'),
require('./checksEffectsInteraction'),
require('./constantFunctions')
// require('./similarVariableNames.js'),
// require('./inlineAssembly'),
// require('./blockTimestamp'),
// require('./lowLevelCalls'),

@ -55,7 +55,8 @@ var builtinFunctions = {
'mulmod(uint256,uint256,uint256)': true,
'selfdestruct(address)': true,
'revert()': true,
'assert()': true
'assert(bool)': true,
'require(bool)': true
}
var lowLevelCallTypes = {

Loading…
Cancel
Save