No Parameter Validation in Constructor
Overview
- Severity: Low
- Confidence: Medium
- Affected Versions: All
What is the No Parameter Validation in Constructor vulnerability?
In Solidity, constructor functions for smart contracts can be created with parameters, which are typically used to initialize some aspect of the contract's state. If the values passed to parameters in a constructor are not validated, it is possible that the contract might be deployed in an incorrect or undesirable state.
Further reading: Solidity Documentation: Constructors
Technical example of vulnerable code
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract Crowdfunding {
address public owner;
uint256 public goal;
uint256 public totalFundsRaised;
event ContributionReceived(address contributor, uint256 amount);
event GoalReached(uint256 totalFundsRaised);
constructor(uint256 _goal) {
owner = msg.sender;
goal = _goal;
}
function contribute() external payable {
require(msg.value > 0, "Contribution must be greater than 0");
totalFundsRaised += msg.value;
emit ContributionReceived(msg.sender, msg.value);
if (totalFundsRaised >= goal) {
emit GoalReached(totalFundsRaised);
}
}
// Functionality to withdraw funds, check goal status, etc., omitted for brevity.
}
In the example above, contract Crowdfunding
uses a constructor that accepts a value for parameter _goal
, which is used to determine when the crowdfunding exercise succeeds. However, there is no check made on the value of the parameter inside of the constructor, meaning it could be passed as a nonsensical (or harmful) value; for example, if it were inadvertently specified as zero, much of the contract functionality might fail to work.
Technical example of how to fix the vulnerability
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract CrowdfundingUpdated {
address public owner;
uint256 public goal;
uint256 public totalFundsRaised;
event ContributionReceived(address contributor, uint256 amount);
event GoalReached(uint256 totalFundsRaised);
constructor(uint256 _goal) {
require (_goal > 100, "Goal must be greater than 100");
owner = msg.sender;
goal = _goal;
}
function contribute() external payable {
require(msg.value > 0, "Contribution must be greater than 0");
totalFundsRaised += msg.value;
emit ContributionReceived(msg.sender, msg.value);
if (totalFundsRaised >= goal) {
emit GoalReached(totalFundsRaised);
}
}
// Functionality to withdraw funds, check goal status, etc., omitted for brevity.
}
In the revised example above, contract CrowdfundingUpdated
imposes a check on the _goal
parameter in the constructor using a require()
condition, specifying a minimal goal for the crowdfunding. While exact values to specify for the parameter check will be application-dependent, specifying some check rather than none can help avoid situations where a contract is deployed in an undesired state.