Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

整数溢出

概述

整数溢出(Integer Overflow)是一种常见的编程错误,发生在数值运算结果超出了数据类型所能表示的范围时。这种错误在智能合约中尤其危险,因为它可能导致意外的资金损失或安全漏洞。 在 0.8.0 版本中自带了溢出检查的功能,而早期的智能合约依赖 safemath 库

基本原理

有符号整数溢出

对于 n 位有符号整数:

  • 取值范围:[-2^(n-1), 2^(n-1)-1]
  • 例如 8 位有符号整数:[-128, 127]

在 Solidity 0.8.0 及以上版本,下面的运算会直接回滚;只有旧编译器或显式 unchecked 才会得到取模结果。为了展示旧版本行为,示例使用 unchecked:

int8 a = 127;
int8 b = 1;
int8 c;
unchecked { c = a + b; } // 结果按 2^8 取模,变为 -128

无符号整数溢出

对于 n 位无符号整数:

  • 取值范围:[0, 2^n-1]
  • 例如 8 位无符号整数:[0, 255]

同样,Solidity 0.8.0 及以上默认回滚;以下代码显式关闭检查:

uint8 a = 255;
uint8 b = 1;
uint8 c;
unchecked { c = a + b; } // 结果按 2^8 取模,变为 0

常见溢出场景

  1. 加法溢出
uint8 a = 255;
uint8 b = 1;
    uint8 c;
    unchecked { c = a + b; } // 旧版本或 unchecked 下溢出为 0
  1. 乘法溢出
uint8 a = 16;
uint8 b = 16;
    uint8 c;
    unchecked { c = a * b; } // 旧版本或 unchecked 下溢出为 0
  1. 减法下溢
uint8 a = 0;
uint8 b = 1;
    uint8 c;
    unchecked { c = a - b; } // 旧版本或 unchecked 下溢出为 255

智能合约中的风险

  1. 资金损失
function transfer(uint256 amount) public {
    balances[msg.sender] += amount; // 可能溢出
    // 转账逻辑
}
  1. 权限绕过
function checkAccess(uint256 userId) public view returns (bool) {
    return userId < totalUsers; // 可能被溢出绕过
}

防护措施

  1. 旧版本使用 SafeMath 库
using SafeMath for uint256;

function safeAdd(uint256 a, uint256 b) public pure returns (uint256) {
    return a.add(b); // 使用 SafeMath 防止溢出
}
  1. Solidity 0.8.0+ 内置检查
// 0.8.0 及以上版本默认启用溢出检查
function add(uint256 a, uint256 b) public pure returns (uint256) {
    return a + b; // 自动检查溢出
}
  1. 前置条件检查
function transfer(uint256 amount) public {
    require(balances[msg.sender] + amount >= balances[msg.sender], "Overflow");
    balances[msg.sender] += amount;
}

最佳实践

  1. 使用合适的数据类型

    • 根据实际需求选择合适位数的整数类型
    • 避免使用过小的数据类型
  2. 进行边界检查

    • 在运算前检查操作数
    • 验证运算结果
  3. 使用安全库

    • Solidity 0.8.0+ 默认检查;旧版本使用 SafeMath
    • 避免自己实现数学运算
  4. 代码审计

    • 定期进行安全审计
    • 使用自动化工具检测

历史漏洞案例

  1. BEC (BeautyChain) BatchOverflow(2018-04)

    • batchTransfer 中 cnt * _value 乘法溢出,绕过余额检查凭空铸造巨量代币
    • 交易所随即暂停 BEC 充提,代币价格崩盘;同一时期 SMT 也出现相同模式
    • 该事件直接推动了批量转账类函数的溢出审计,也是 0.8.0 内置检查被加速采纳的背景之一
  2. PoWHC (PonziToken)(2018-02)

    • transfer 路径的余额减法下溢(wrap 成天文数字假余额),随后经兑换/提现路径套现
    • 合约内约 866 ETH 被清空

溢出解释

图片来源为 csapp+个人笔记

进阶(unchecked、舍入方向、ERC-4626 通胀):整数溢出与精度