SWC智能合约漏洞库

在线工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器

SWC-110/触发assert断言

Solidity的assert()函数用于不变量的断言,正常运行的代码永远不会到达失败的assert语句。 可达到的断言可能意味着以下两种情况之一:

  • 合约中存在一个错误,使其进入无效状态。
  • 该assert语句使用不正确,例如,用于验证输入。

CWE漏洞分类

CWE-670:不正确的控制流程实现

整改方案

考虑在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]