
相關文章:
Rust智能合約養成日記(1)合約狀態數據定義與方法實現
Rust智能合約養成日記(1)合約狀態數據定義與方法實現
Rust智能合約養成日記(2)編寫Rust智能合約單元測試
Rust智能合約養成日記(2)編寫Rust智能合約單元測試
Rust智能合約養成日記(3)Rust智能合約部署,函數調用及Explorer的使用
Rust智能合約養成日記(3)Rust智能合約部署,函數調用及Explorer的使用
一級標題
Rust智能合約養成日記(1)合約狀態數據定義與方法實現
圖片
Rust智能合約養成日記(1)合約狀態數據定義與方法實現
Rust智能合約養成日記(2)編寫Rust智能合約單元測試
0xFFFFFFFF
+ 0x00000001
------------
= 0x00000000
Rust智能合約養成日記(2)編寫Rust智能合約單元測試
Rust智能合約養成日記(3)Rust智能合約部署,函數調用及Explorer的使用
0x00000000
- 0x00000001
------------
= 0xFFFFFFFF
Rust智能合約養成日記(3)Rust智能合約部署,函數調用及Explorer的使用
一級標題
Rust智能合約養成日記(1)合約狀態數據定義與方法實現
https://etherscan.io/tx/0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f
圖片
1. function batchTransfer(address[] _receivers, uint256 _value) public whenNotPausedreturns (bool) {
2. uint cnt = _receivers.length;
3. uint256 amount = uint256(cnt) * _value;
4. require(cnt > 0 && cnt <= 20);
5. require(_value > 0 && balances[msg.sender] >= amount);
6.
7. balances[msg.sender] = balances[msg.sender].sub(amount);
8. for (uint i = 0; i < cnt; i++) {
9. balances[_receivers[i]] = balances[_receivers[i]].add(_value);
10. Transfer(msg.sender, _receivers[i], _value);
11. }
12. return true;
13. }
Rust智能合約養成日記(1)合約狀態數據定義與方法實現
Rust智能合約養成日記(2)編寫Rust智能合約單元測試
Rust智能合約養成日記(2)編寫Rust智能合約單元測試
Rust智能合約養成日記(3)Rust智能合約部署,函數調用及Explorer的使用
Rust智能合約養成日記(3)Rust智能合約部署,函數調用及Explorer的使用
一級標題
[profile.release]
overflow-checks = true
panic = "abort"
1. 整數溢出漏洞概述
圖片
在大多數編程語言中,一個整數的數值通常保存在一段定長的內存當中。整數可分為兩種類型,即無符號數與有符號數。它們之間的區別在於最高位是否被用作符號位,用來表示整數的正負。例如32bit的內存空間可以存儲0到4,294,967,295範圍之間的無符號整數(uint32),或−2,147,483,648到2,147,483,647範圍之間的有符號整數(int32)。
但是,當我們在uint32的範圍內,執行計算4,294,967,295 + 1並試圖存儲大於該整數類型最大值的結果時,會發生什麼呢?
儘管該執行的結果取決於特定編程語言和編譯器,但在大多數情況下,計算的結果將表現出“溢出”的現象並返回0。同時,大多數編程語言和編譯器不會檢查該類型的錯誤,而僅僅執行一個簡單的模運算,甚至還存在其他未定義的行為。
整數溢出的存在,往往使得程序在運行時產生意料之外的結果。在區塊鏈智能合約的編寫中,尤其是去中心化金融領域,整數數值計算的使用場景十分普遍,因此需格外注意整數溢出漏洞存在的可能性。
假設,某金融機構使用無符號的32位整數來表示股票價格。然而,當使用該整數類型表示一個大於該類型所能表示的最大值數字時,計算機將在32位的內存範圍外額外放置一個1或更多的位(即溢出),最終該數字將表示為截斷了溢出位以外的值,如可能將$429,496,7296讀為0。此時,如果有人使用該數值繼續進行交易,股票價格將為0 ,這將引起各種各樣的混亂。因此,整數溢出漏洞的問題值得我們的重視。"0.9.1"如何在使用Rust語言編寫智能合約時,避免整數溢出,將是本文後續討論的重點。
[dependencies]
2. 整數溢出定義
uint = { version = "0.9.1", default-features = false }
若數值超出了變量類型所能表示的範圍,則會導致溢出。溢出主要可分為兩種情況,即整數上溢(overflow)和下溢(underflow)。
use uint::construct_uint;
2.1 整數上溢
construct_uint! {
pub struct U1024(16);
}
construct_uint! {
pub struct U512(8);
}
construct_uint! {
pub struct U256(4);
}
即類似於上文整數溢出漏洞概述中所描述的那樣,例如在Solidity中uint32所能表示的無符號整數範圍為:0 至2^32 - 1,2^32 - 1使用16進製表示為0xFFFFFFFF ,2^32 - 1再加上1即會導致上溢。
無符號整數uin32的表示範圍也有下界,即最小值0。當0減去1時將導致uint32整數的下溢:
// (2^1024)-1 = 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215
let p =U1024::from_dec_str("179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215").expect("p to be a good number in the example");
4.1.2 Rust uint crate使用方法
#[test]
fn test_uint(){
let p = U1024::from_dec_str("179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215").expect("p to be a good number in the example");
assert_eq!(p,U1024::max_value());
}
BeautyChain團隊2018年4月22日宣布,BEC token在4月22日出現了異常波動。攻擊者利用整數溢出造成的漏洞成功獲得了10^58 個BECs。
running 1 test
test tests::test_uint ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 3 filtered out; finished in 0.00s
版本。
在該合約的攻擊事件中,攻擊者執行了具有整數溢出漏洞的函數“batchTransfer”進行了交易
#[test]
fn test_overflow(){
以下是該該函數的具體實現:
let amounts: u128 = 340282366920938463463374607431768211455;
該函數用來向多個地址(receivers)轉賬, 每個地址的轉賬金額為value。
let amount_u256 = U256::from(amounts) * U256::from(amounts);
println!("{:?}",amount_u256);
上述代碼的第三行uint256 amount = uint256(cnt) * _value用來計算整個需要轉賬的金額,但是該行代碼存在整數溢出的可能性。當value =0x8000000000000000000000000000000000000000000000000000000000000000,同時receivers的長度為2. 則在第三行代碼乘法運算的時候將發生整數溢出,使得amount = 0。由於amount = 0要比用戶的balances[msg.sender]要小,因此第5行中檢查合約調用者用戶msg.sender的餘額是否大於將要轉出的amount數額會輕鬆被通過。從而攻擊者可以執行後續的轉賬操作而獲利。
let amount_u256 = U256::from(amounts) + 1;
println!("{:?}",amount_u256);
4. 整數溢出防護技術
let amount_u128 = amount_u256.as_u128();
println!("{:?}",amount_u128);
}
本小節將介紹如何使用一些常用的手段並結合Rust語言的特性來避免整數溢出。
running 1 test
115792089237316195423570985008687907852589419931798687112530834793049593217025
340282366920938463463374607431768211456
thread 'tests::test_overflow' panicked at 'Integer overflow when casting to u128', src/lib.rs:16:1
在Rust語言中:當我們編譯獲得release版本的目標文件時,若不加以配置,Rust將默認不檢查整數溢出。當整數溢出時,例如在8位無符號整數(uint8)的情況下,Rust的做法通常是,使值256變成0,257變成1,以此類推。此時Rust並不會觸發Panic,但是變量的值可能不是我們所期望的值。因此我們需要對Rust程序的編譯選項稍加配置,使得程序在Release模式下也能夠檢查整數溢出,並能夠觸發Panic,從而避免因整數溢出而導致的程序異常現象。
配置Cargo.toml,在release模式下檢查整數溢出。
利用該配置我們可以設置程序內整數溢出時的處理策略。
4.1 使用Rust Crate uint 支持更大整數(目前最新版本為0.9.1)<_>對比於Solidity所能夠支持的最大整數類型為u256,Rust目前標準庫所能提供的最大整數類型僅為u128。為了更好地在我們的Rust智能合約中支持更大的整數運算,我們可以使用Rust uint crate來幫助拓展。
4.1.1 Rust uint crate簡介
#[test]
fn test_underflow(){
let amounts= U256::from(0);
let amount_u256 = amounts.checked_sub(U256::from(1));
println!("{:?}",amount_u256);
}
使用Rust uint crate可提供大無符號整數類型,並內置支持了與Rust原始整數類型非常相似的API,同時兼顧了性能與跨平台可用性。
running 1 test
None
test tests::test_underflow ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in 0.00s
4.1.2 Rust uint crate使用方法
#[test]
fn test_underflow(){
let amounts= U256::from(0);
- let amount_u256 = amounts.checked_sub(U256::from(1));
+ let amount_u256 =amounts.checked_sub(U256::from(1)).expect("ERR_SUB_INSUFFICIENT");
println!("{:?}",amount_u256);
}
首先在Rust項目的Cargo.toml中添加對uint crate的依賴,並指定版本號為最新的
running 1 test
thread 'tests::test_underflow' panicked at 'ERR_SUB_INSUFFICIENT', src/lib.rs:126:62
版本。
# 其他依賴,例如near-sdk,near-contract-standards等
隨後我們可以在Rust程序中導入使用該crate