Title
Assert Violation
Relationships
CWE-670: Always-Incorrect Control Flow Implementation
Description
The Solidity assert()
function is meant to assert invariants. Properly functioning code should never reach a failing assert statement. A reachable assertion can mean one of two things:
- A bug exists in the contract that allows it to enter an invalid state;
- The
assert
statement is used incorrectly, e.g. to validate inputs.
Remediation
Consider whether the condition checked in the assert()
is actually an invariant. If not, replace the assert()
statement with a require()
statement.
If the exception is indeed caused by unexpected behaviour of the code, fix the underlying bug(s) that allow the assertion to be violated.
References
Samples
assert_constructor.sol
/*
* @source: https://github.com/ConsenSys/evm-analyzer-benchmark-suite
* @author: Suhabe Bugrara
*/
pragma solidity ^0.4.19;
contract AssertConstructor {
function AssertConstructor() public {
assert(false);
}
}
assert_minimal.sol
/*
* @source: https://github.com/ConsenSys/evm-analyzer-benchmark-suite
* @author: Suhabe Bugrara
*/
pragma solidity ^0.4.19;
contract AssertMinimal {
function run() public {
assert(false);
}
}
assert_multitx_1.sol
/*
* @source: https://github.com/ConsenSys/evm-analyzer-benchmark-suite
* @author: Suhabe Bugrara
*/
pragma solidity ^0.4.19;
contract AssertMultiTx1 {
uint256 private param;
function AssertMultiTx1(uint256 _param) public {
require(_param > 0);
param = _param;
}
function run() {
assert(param > 0);
}
}
assert_multitx_2.sol
/*
* @source: https://github.com/ConsenSys/evm-analyzer-benchmark-suite
* @author: Suhabe Bugrara
*/
pragma solidity ^0.4.19;
contract AssertMultiTx2 {
uint256 private param;
function AssertMultiTx2(uint256 _param) public {
param = 0;
}
function run() {
assert(param > 0);
}
function set(uint256 _param) {
param = _param;
}
}
constructor_create.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.25;
contract ConstructorCreate{
B b = new B();
function check(){
assert(b.foo() == 10);
}
}
contract B{
function foo() returns(uint){
return 11;
}
}
constructor_create_argument.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.22;
contract ConstructorCreateArgument{
B b = new B(11);
function check(){
assert(b.foo() == 10);
}
}
contract B{
uint x_;
constructor(uint x){
x_ = x;
}
function foo() returns(uint){
return x_;
}
}
constructor_create_modifiable.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
* Assert violation with 2 message calls:
* - B.set_x(X): X != 10
* - ContructorCreateModifiable.check()
*/
pragma solidity ^0.4.22;
contract ContructorCreateModifiable{
B b = new B(10);
function check(){
assert(b.foo() == 10);
}
}
contract B{
uint x_;
constructor(uint x){
x_ = x;
}
function foo() returns(uint){
return x_;
}
function set_x(uint x){
x_ = x;
}
}
gas_model.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.21;
contract GasModel{
uint x = 100;
function check(){
uint a = gasleft();
x = x + 1;
uint b = gasleft();
assert(b > a);
}
}
gas_model_fixed.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.21;
contract GasModelFixed{
uint x = 100;
function check(){
uint a = gasleft();
x = x + 1;
uint b = gasleft();
assert(b < a);
}
}
mapping_perfomance_2.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.22;
contract MappingPerformance2sets{
mapping(bytes32=>uint) m0;
mapping(bytes32=>uint) m1;
mapping(bytes32=>uint) m2;
mapping(bytes32=>uint) m3;
mapping(bytes32=>uint) m4;
mapping(bytes32=>uint) m5;
uint b;
constructor(){
b = 10;
}
function set(bytes32 a, uint cond){
if(cond == 0){
m0[a] = 5;
}else if(cond == 1){
m1[a] = 5;
}else if(cond == 2){
m2[a] = 5;
}else if(cond == 3){
m3[a] = 5;
}else if(cond == 4){
m4[a] = 5;
}
}
function check(bytes32 a0, uint cond0,
bytes32 a1, uint cond1, bytes32 a){
set(a0, cond0);
set(a1, cond1);
assert(m5[a] == 0);
}
}
mapping_performance_1.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.22;
contract MappingPerformance1set{
mapping(bytes32=>uint) m0;
mapping(bytes32=>uint) m1;
mapping(bytes32=>uint) m2;
mapping(bytes32=>uint) m3;
mapping(bytes32=>uint) m4;
mapping(bytes32=>uint) m5;
uint b;
constructor(){
b = 10;
}
function set(bytes32 a, uint cond){
if(cond == 0){
m0[a] = 5;
}else if(cond == 1){
m1[a] = 5;
}else if(cond == 2){
m2[a] = 5;
}else if(cond == 3){
m3[a] = 5;
}else if(cond == 4){
m4[a] = 5;
}
}
function check(bytes32 a0, uint cond0, bytes32 a){
set(a0, cond0);
assert(m5[a] == 0);
}
}
out-of-bounds-exception.sol
pragma solidity ^0.5.0;
contract OutOfBoundsException {
uint256[] private array;
function getArrayElement(uint256 idx) public returns (uint256) {
return array[idx];
}
}
return_memory.sol
/*
* @source: https://forum.zeppelin.solutions/t/using-automatic-analysis-tools-with-makerdao-contracts/1021/3
* Author: Dan Guido / Trail of Bits
* Slightly modified by Bernhard Mueller
* An assertion violation is possible in 3 transactions:
*
* etch(addr)
* lookup(slate, addr)
* checkAnInvariant()
* Whereby slate == Keccak(addr)
*
* Ideally tools should output the correct transaction trace.
*/
pragma solidity ^0.5.0;
contract ReturnMemory {
mapping(bytes32=>address) public slates;
bool everMatched = false;
function etch(address yay) public returns (bytes32 slate) {
bytes32 hash = keccak256(abi.encodePacked(yay));
slates[hash] = yay;
return hash;
}
function lookup(bytes32 slate, address nay) public {
if (nay != address(0x0)) {
everMatched = slates[slate] == nay;
}
}
function checkAnInvariant() public returns (bool) {
assert(!everMatched);
}
}
runtime_create_user_input.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.22;
contract RuntimeCreateUserInput{
function check(uint x){
B b = new B(x);
assert(b.foo() == 10);
}
}
contract B{
uint x_;
constructor(uint x){
x_ = x;
}
function foo() returns(uint){
return x_;
}
}
runtime_user_input_call.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.19;
contract RuntimeUserInputCall{
function check(address b){
assert(B(b).foo() == 10);
}
}
contract B{
function foo() returns(uint);
}
sha_of_sha_2_mappings.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.22;
contract ShaOfSha2Mappings{
mapping(bytes32=>uint) m;
mapping(bytes32=>uint) n;
constructor(){
m[keccak256(abi.encode("AAA", msg.sender))] = 100;
}
function check(address a){
assert(n[keccak256(abi.encode("BBB", a))] == 0);
}
}
sha_of_sha_collision.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
* Assert violation with 2 message calls:
* - set(66)
* - check(0x4100000000000000000000000000000000000000000000000000000000000000)
*/
pragma solidity ^0.4.22;
contract ShaOfShaCollission{
mapping(bytes32=>uint) m;
function set(uint x){
m[keccak256(abi.encodePacked("A", x))] = 1;
}
function check(uint x){
assert(m[keccak256(abi.encodePacked(x, "B"))] == 0);
}
}
sha_of_sha_concrete.sol
/*
* @source: ChainSecurity
* @author: Anton Permenev
*/
pragma solidity ^0.4.22;
contract ShaOfShaConcrete{
mapping(bytes32=>uint) m;
uint b;
constructor(){
b = 1;
}
function check(uint x){
assert(m[keccak256(abi.encodePacked(x, "B"))] == 0);
}
}
token-with-backdoor.sol
/*
* @source: TrailofBits workshop at TruffleCon 2018
* @author: Josselin Feist (adapted for SWC by Bernhard Mueller)
* Assert violation with 3 message calls:
* - airdrop()
* - backdoor()
* - test_invariants()
*/
pragma solidity ^0.4.22;
contract Token{
mapping(address => uint) public balances;
function airdrop() public{
balances[msg.sender] = 1000;
}
function consume() public{
require(balances[msg.sender]>0);
balances[msg.sender] -= 1;
}
function backdoor() public{
balances[msg.sender] += 1;
}
function test_invariants() {
assert(balances[msg.sender] <= 1000);
}
}
two_mapppings.sol
pragma solidity ^0.4.22;
contract TwoMappings{
mapping(uint=>uint) m;
mapping(uint=>uint) n;
constructor(){
m[10] = 100;
}
function check(uint a){
assert(n[a] == 0);
}
}
simpledschief.sol
/*
* @source: https://forum.zeppelin.solutions/t/using-automatic-analysis-tools-with-makerdao-contracts/1021/3
* Author: Vera Bogdanich Espina / Zeppelin Solutions
*
* A simplified version of the MakerDAO DSChief contract.
* Tools should output the correct transaction trace (see source link).
*/
contract SimpleDSChief {
mapping(bytes32=>address) public slates;
mapping(address=>bytes32) public votes;
mapping(address=>uint256) public approvals;
mapping(address=>uint256) public deposits;
function lock(uint wad) public {
deposits[msg.sender] = add(deposits[msg.sender], wad);
addWeight(wad, votes[msg.sender]);
}
function free(uint wad) public {
deposits[msg.sender] = sub(deposits[msg.sender], wad);
subWeight(wad, votes[msg.sender]);
}
function voteYays(address yay) public returns (bytes32){
bytes32 slate = etch(yay);
voteSlate(slate);
return slate;
}
function etch(address yay) public returns (bytes32 slate) {
bytes32 hash = keccak256(abi.encodePacked(yay));
slates[hash] = yay;
return hash;
}
function voteSlate(bytes32 slate) public {
uint weight = deposits[msg.sender];
subWeight(weight, votes[msg.sender]);
votes[msg.sender] = slate;
addWeight(weight, votes[msg.sender]);
}
function addWeight(uint weight, bytes32 slate) internal {
address yay = slates[slate];
approvals[yay] = add(approvals[yay], weight);
}
function subWeight(uint weight, bytes32 slate) internal {
address yay = slates[slate];
approvals[yay] = sub(approvals[yay], weight);
}
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
function checkAnInvariant() public {
bytes32 senderSlate = votes[msg.sender];
address option = slates[senderSlate];
uint256 senderDeposit = deposits[msg.sender];
assert(approvals[option] >= senderDeposit);
}
}