SWC-110/触发assert断言
Solidity的assert()函数用于不变量的断言,正常运行的代码永远不会到达失败的assert语句。 可达到的断言可能意味着以下两种情况之一:
- 合约中存在一个错误,使其进入无效状态。
- 该assert语句使用不正确,例如,用于验证输入。
CWE漏洞分类
整改方案
考虑在assert()中检查的条件是否真的不变。如果不是,则应该使用require()语句替换该assert()。
如果异常确实是由于代码的意外行为引起的,请修复可能触发断言的潜在错误。
参考文献
合约示例
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_constructor.yaml
description: Assert violation in constructor
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x78a26dc3f4a5757a59e3a9d9872f127cb3941448491b0e903c126462041f2779': [24]
line_numbers:
assert_constructor.sol: [10]
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_minimal.yaml
description: Simple assert violation
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0xa40b253d3c13b16521a0123d94cb32124885577e67659d17db972cf36414861b': [96]
line_numbers:
assert_minimal.sol: [10]
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_1.yaml
description: Assert is triggered through constructor argument
issues:
- id: SWC-110
count: 0
locations: []
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;
}
}
assert_multitx_2.yaml
description: Assert can be triggered with 2 transactions
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x25e5dd25cd6cc2409070e066096e3c426133dc56dae1d6d4ae3d565e305cd28b': [161]
line_numbers:
assert_multitx_2.sol: [16]
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.yaml
description: Assert violation after call to concretely created contract
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0xc27f0d2e53876fb4e1100569b750ff701f48b85487e89782f8b8e4dd72369e67': [295]
line_numbers:
constructor_create.sol: [12]
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_argument.yaml
description: Assert violation after call to concretely created contract with argument
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0xa9bafdd71a4744d05c44b4cf2f75e05bb66b0252ae3ec370587e49b15fde453c': [295]
line_numbers:
constructor_create_argument.sol: [11]
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;
}
}
constructor_create_modifiable.yaml
description: Assert violation after call to concretely created contract that can be modified
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x1c77f218521194c2d487296217ed6bb645d315faf344a09769103a7eee6a8250': [277]
line_numbers:
constructor_create_modifiable.sol: [15]
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.yaml
description: Gas modeling check
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x1d0245bf42c590285bb4751565a6fabbdb6825e95c009d8e5392b63d9851d9ab': [118]
line_numbers:
gas_model.sol: [13]
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);
}
}
gas_model_fixed.yaml
description: Gas modeling check fixed
issues:
- id: SWC-110
count: 0
locations: []
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_perfomance_2.yaml
description: Mapping performance test.
issues:
- id: SWC-110
count: 0
locations: []
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);
}
}
mapping_performance_1.yaml
description: Mapping performance test.
issues:
- id: SWC-110
count: 0
locations: []
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];
}
}
out-of-bounds-exception.yaml
description: Exceptions triggered by out-of-bounds array (not an explicit assert violation).
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0xc6af8128d4c993b2765e9fef1047e9d90551e08379297bcc2f33ffc17cb005e2': [122]
line_numbers:
out-of-bounds-exception.sol: [8]
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);
}
}
return_memory.yaml
description: Simplified version of Zeppelin's DSChief invariant
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x4aa4705ae5bd0ad1640337ff069d078211aa2641d234e79e7f3da8ada3de1dce': [594]
line_numbers:
return_memory.sol: [36]
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_create_user_input.yaml
description: Assert violation after call to contract created with user input during runtime
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x1410f656e6c549fa79b9b13b559ef15eb4149c80d4f22a314642993add450e19': [336]
line_numbers:
runtime_create_user_input.sol: [11]
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);
}
runtime_user_input_call.yaml
description: Assert violation after call to user input contract
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x8559dc02c1ce9782181ae177826759435d170c82dac501e8f2d626df5eef577e': [269]
line_numbers:
runtime_user_input_call.sol: [10]
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_2_mappings.yaml
description: Check of non-collision between two maps that use SHA as a keys
issues:
- id: SWC-110
count: 0
locations: []
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_collision.yaml
description: Check for collision in maps that use SHA hashes as keys
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x7cb9e07513139d72ea4ef963082a4b9f9474e7add211816a1b537bdfdd6750f8': [377]
line_numbers:
sha_of_sha_collision.sol: [18]
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);
}
}
sha_of_sha_concrete.yaml
description: Check the mapping that uses the SHA value as a key does not collide with concretes
issues:
- id: SWC-110
count: 0
locations: []
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);
}
}
token-with-backdoor.yaml
description: Assertion can be violated with 3 transactions
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x78c4c9ab906637a8d3453dd8c6c6a8357e2459ec6c52f0e48fea0437fbca6910': [698]
line_numbers:
token-with-backdoor.sol: [28]
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);
}
}
two_mapppings.yaml
description: Check that two mappings are not colliding with each other
issues:
- id: SWC-110
count: 0
locations: []
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);
}
}
simpledschief.yaml
description: Simplified version of the MakerDAO DSChief contract with invariant
issues:
- id: SWC-110
count: 1
locations:
- bytecode_offsets:
'0x1e8071a550b197a299c21ee7089e30a6363f126917e6d308cd5242c612ec63f9': [1156]
line_numbers:
simpledschief.sol: [70]