SC Integer Overflow and Underflow

整數溢位(Overflow)與下溢(Underflow)是智能合約中的一種安全漏洞,源於以太坊虛擬機(EVM)中整數資料型別的固定大小限制。EVM 使用固定長度的整數型別,例如:

  • uint8(無符號 8 位整數):只能表示 0 到 255 的數字。
  • int8(有符號 8 位整數):可表示 -128 到 127 的數字。

當算術運算結果超出型別的範圍時,會發生溢位或下溢:

  • 溢位(Overflow):試圖將值設為超出型別最大範圍。例如,將 uint8 的值 255 加 1,結果不是 256,而是「回繞」到 0。
  • 下溢(Underflow):試圖將值減到小於型別最小範圍。例如,將 uint8 的值 0 減 1,結果不是 -1,而是「回繞」到 255。
  • 有符號整數:對於 int8,如果從 -128 減 1,結果會回繞到 127。

這種行為類似於汽車里程表(達到 999999 後重置為 000000)或週期性數學函數(如正弦函數)。

重要注意事項

  • 在 Solidity 0.8.0 及以上版本,編譯器內建了溢位和下溢檢查,當發生溢位或下溢時,交易會自動回滾。
  • Solidity 0.8.0 引入了 unchecked 關鍵字,允許開發者在特定情況下禁用這些檢查以節省 gas(例如,當確定不會發生溢位時)。這會讓算術運行為回歸到舊版本的行為。

範例(存在漏洞的合約)

以下是一個易受整數溢位和下溢攻擊的合約範例:

// SPDX-License-Identifier: MIT
pragma solidity ^0.4.17;

contract Solidity_OverflowUnderflow {
    uint8 public balance;

    constructor() public {
        balance = 255; // Maximum value of uint8
    }

    // Increments the balance by a given value
    function increment(uint8 value) public {
        balance += value; // Vulnerable to overflow
    }

    // Decrements the balance by a given value
    function decrement(uint8 value) public {
        balance -= value; // Vulnerable to underflow
    }
}

漏洞分析

  • 溢位漏洞:假設 balance = 255,調用 increment(1) 會導致 255 + 1 = 0(溢位),因為 uint8 最大值為 255,超過後回繞到 0。
  • 下溢漏洞:假設 balance = 0,調用 decrement(1) 會導致 0 - 1 = 255(下溢)。
  • 攻擊情境
  • 攻擊者可調用 increment(1)balance 從 255 變為 0,造成餘額錯誤重置。
  • 攻擊者可調用 decrement(1) 從 0 變為 255,製造大量虛假餘額,進而提領不屬於自己的資金。

影響

整數溢位和下溢可能導致以下嚴重後果:

  1. 資金損失:攻擊者可通過溢位或下溢操縱餘額,提取超出其合法擁有的資金。
  2. 邏輯破壞:錯誤的數值可能改變合約的預期行為,例如允許未授權的操作或生成過量代幣。
  3. 協議破壞:這些漏洞可能導致協議功能失常,影響用戶信任和生態系統穩定。

修復方法

為防止整數溢位和下溢,開發者應採取以下措施:

使用 Solidity 0.8.0 或更高版本

  • 從 Solidity 0.8.0 開始,編譯器自動檢查溢位和下溢,交易會在發生異常時回滾。
  • 這是最簡單的修復方法,無需額外程式碼。

使用安全數學庫

  • 對於低於 0.8.0 的版本,可使用 OpenZeppelin 的 SafeMath 庫,該庫提供安全的算術運算函數(如 add()sub()),在溢位或下溢時自動回滾。

手動檢查

  • 在關鍵運算前手動檢查輸入值,確保結果不會超出範圍。

修復後的範例合約

以下是修復後的合約,使用 Solidity 0.8.0 的內建溢位檢查,並加入額外防護:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Solidity_OverflowUnderflow {
    uint8 public balance;

    constructor() {
        balance = 255; // Maximum value of uint8
    }

    // Increments the balance by a given value
    function increment(uint8 value) public {
        balance += value; // Solidity 0.8.x automatically checks for overflow
    }

    // Decrements the balance by a given value
    function decrement(uint8 value) public {
        require(balance >= value, "Underflow detected");  
        balance -= value;
    }
}

修復內容

Solidity 0.8.0 溢位檢查

  • increment 中,balance += value 若導致溢位(例如,255 + 1),編譯器會自動回滾交易。

手動下溢檢查

  • decrement 中,新增 require(balance >= value),確保不會發生下溢。

總結

整數溢位和下溢是智能合約中的危險漏洞,可能導致餘額操縱、未授權操作和資金損失。這些問題源於 EVM 整數型別的固定範圍限制,導致數值回繞行為。Solidity 0.8.0 及以上版本的內建溢位檢查是最簡單的解決方案,結合 OpenZeppelin 的 SafeMath 庫或手動檢查可進一步增強安全性。修復後的合約展示了如何利用現代 Solidity 功能和額外檢查來防止這些漏洞,確保合約的可靠性和安全性。開發者在設計合約時應始終使用最新版本並進行全面測試。