本文探讨了Rust和Solana智能合约中的整数溢出和下溢问题,解析了这些问题的根源以及对区块链安全的影响,并讨论了如何在Solana智能合约中有效预防这些问题。文章强调Rust在调试模式和发布模式下的不同表现,可能导致对安全性的误解。
在智能合约中,整数溢出/下溢是相当普遍的,因为区块链应用程序经常在财务数据上进行数学计算。
Rust 是一种在 Solana 和 Polkadot 等区块链中使用的流行语言。对许多开发者来说,认为 Rust 是内存安全的,因此不存在算术溢出/下溢,这可能是一个误解。本文解释了为什么 Rust 程序仍然会遭受算术错误,这些问题如何影响区块链安全,以及如何在智能合约中处理它们。
就像家里每一件家具都占用空间一样,在计算机程序中,每一条数据也需要一个存储其值的空间。这个空间是有限的。如果某个数据的值(例如账户的余额)在计算后超出了其空间的大小,就会发生溢出/下溢。
在 Rust 中,无符号整数可以有以下类型:u8、u16、u32、u64、u128 和 usize。类型表示用于存储整数的位数:u8 可以保存 0 到 255 之间的值,u16 可以保存 0 到 65535 之间的值,依此类推。例如,x = x + 1 — 如果一个 u8 整数 x 被更改为超出其范围的值,假设为 256 或 -1,那么就会发生溢出/下溢。
这有点令人惊讶 — Rust 在调试模式和发布模式下对整数溢出/下溢的行为不同。在调试模式下,Rust 会添加内置的溢出/下溢检查,并在运行时发生溢出/下溢时引发 panic。
注意:Rust 中的溢出检查可以在发布模式下启用 https://doc.rust-lang.org/cargo/reference/profiles.html#overflow-checks
换句话说,在调试时看似 panic 的 Rust 溢出在生产环境中可能不复存在。
尽管 Rust 在发布模式下忽略溢出是有合理原因的(例如性能),但这种不一致的行为可能导致对 Rust 算术操作安全性的错觉。这对区块链和智能合约尤其危险。
用 Rust 编写的 Solana 智能合约以及 Solana 的核心运行时(验证器代码)都经历了相当多的算术错误。
以下是来自 Github 的部分列表(点击链接可查看拉取请求):
图 1. jet-v1 中的整数下溢/溢出修复 fixed in jet-v1
上面的例子显示了在 jet-v1 协议中 total_loan_notes -= note_amount(第 245 行) 的整数下溢和 total_deposit += token_amount(第 246 行) 的溢出。这两个变量是 u64 类型。修复方法很简单:将 - 替换为 checked_sub,将 + 替换为 checked_add。这些将在下溢或溢出时返回 None,而不是包装运算。
图 2. Solana bpf loader 中的整数溢出修复 fixed in Solana bpf loader
上面的例子显示了在 Solana bpf loader 中发生的整数溢出。乘法 num_accounts size_of::<AccountMeta>() 和加法 + data_len 都可能导致溢出。修复方法是将 替换为 saturating_mul,将 + 替换为 saturating_add。这些将对数值在数值边界内进行饱和,而不是溢出。
图 3. Solana watchtower 中的算术错误修复 fixed in Solana watchtower
上面的例子展示了 Solana 中另一种类型的算术错误。两个整数之间的除法 / total_current_stake * 100 和 total_stake 可能会因截断小数而损失精度。修复该问题的方法是将这些变量的类型更改为浮点类型 f64。这样,结果 current_stake_percent 也具有 f64 类型的精度。
在 Rust 中编写 Solana 智能合约时,有三种常见方法来处理算术错误:
此外,拥有一个能够全面检查所有代码路径中算术错误的工具毫无疑问是明智的,就像检查拼写错误一样。
X-Ray 是一个先进的工具,内置支持检测 Solana 智能合约中的常见安全陷阱,包括算术溢出/下溢。
- 原文链接: sec3.dev/blog/understand...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!