智能合约漏洞——整数上溢和下溢(Integer Overflow and Underflow)

整数上溢和下溢(Integer Overflow and Underflow)

  在solidity中,整数类型有最大值和最小值。当整型变量的值超过可存储在该变量类型的最大值时,就会发生整数溢出。类似地,当整型变量的最小值低于该变量类型的最小值时,就会发生整数下溢。例如,uint8类型的最大值为255,当将256存入uint8类型时,会发生上溢,值被设置为0。当存储的值为257时,值被设置为1,以此类推。同样地,如果将-1存储在uint8类型变量中时,这个变量的值将被设置为255,这样会发生下溢。

  一些整型变量和它们的最大最小值如下所示:

  由于uint8、uint16等具有较小的最大值,因此更容易导致移除,应谨慎使用。

  为了防止上溢/下溢。solidity在0.8及以上版本通过内置安全数学函数来阻止整数上溢和下溢。但需要注意的是,无论是哪种SafeMath逻辑,如内置或者在旧合约中手动使用,上溢/下溢仍然会触发reverts,这有可能导致重要功能出现问题或者其他意想不到的影响。甚至在solidity0.8版本以后,在没有交易回退(reverting)的情况下仍然有可能发生整数的上溢和下溢。

1、类型转换(Type Casting)

  最容易导致整数上溢/下溢的方式是当一个大的uint数据类型向小的uint数据类型转换:

uint256 public a = 258;
uint8 public b = uint8(a);

  上述代码片段会产生上溢,变量b的值会被存储为2。

2、使用移位运算符(Using Shift Operators)

  移位操作不像其他算数运算那样执行上溢和下溢的检查,因此,有可能发生上溢/下溢。

  左移运算符'<<'就是将一个二进制位的操作数按照指定位数整体向左移,右边的空位补0。如uint8类型的变量a值为10,转换成二进制就使00001010,左移一位后为00010100,转换为十进制后就使20。可以简单概括为将操作数左移一位相当于乘以2,移动两位相当于乘以4,移动三位相当于乘以8。

uint8 public a = 100;
uint8 public b = 2;

uint8 public c = a << b;   //overflow as 100 * 4 > 255

3、内联汇编的使用(Use of Inline Assembly)

  内联汇编在solidity中使用是YUL语言。YUL是一种可以编译到各种不同后端的中间语言。在YUL程序语言中,整数的上溢和下溢是可能的因为编译器不会自动检查它,因为YUL是一种低级的语言,主要用于优化代码,这是通过省略许多操作码来实现的。

uint8 public c = 255;

function addition() public returns (uint8 result) {
    assembly {
        result := add(sload(c.slot), 1) // adding 1 will overflow and reset to 0
        // using inline assembly
    }

    return result;
}

  在上述代码中,我们通过内联汇编给变量加1然后返回结果。这个变量结果将会发生溢出,然后返回0,但合约不会报错或者revert。

4、未检查代码块的使用(Use of unchecked code block)

  在未检查的块内执行算术运算节省了大量的gas,因为它省略了几个检查和操作码。但是,其中一些操作码在0.8版本的默认算术运算中用于检查下溢/溢出。

uint8 public a = 255;

function uncheck() public{

  unchecked {
      a++;  // overflow and reset to 0 without reverting
  }

}

  只有在确定算法不会溢出或下溢的情况下,才建议使用未检查的代码块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值