過年前要生產出一些東西出來,不然太久沒寫文章了
看到Unsigned integer overflow和underflow造成的問題,覺得Rust的解法實在很好,在編譯時就能檢查出來
1 2 3 4 5 6 7 8 9
| #![deny(clippy::integer_arithmetic)]
use std::env; const PAGE_SIZE: u64 = 4096; fn main() { let args: Vec<String> = env::args().skip(1).collect(); let size: u64 = args[0].parse().unwrap(); println!("({} - 2 - {}) => {}", PAGE_SIZE, size, PAGE_SIZE - 2 - size); }
|
然後安裝clippy
當作cargo的subcommand
1 2 3 4 5 6 7
| $ cargo clippy error: integer arithmetic detected --> src/main.rs:8:54 | 8 | println!("({} - 2 - {}) => {}", PAGE_SIZE, size, PAGE_SIZE - 2 - size); | ^^^^^^^^^^^^^^^^^^^^ |
|
如果要正確的處理,程式碼大概是這樣
1 2 3 4 5 6 7 8 9 10 11 12 13
| fn foo(len: u64, size: u64) { match (PAGE_SIZE - 2).checked_sub(size) { Some(capacity) if len > capacity => { println!("no capacity left"); } Some(capacity) => { println!("sufficient capacity {}", capacity); } None => { println!("underflow! bad user input!"); } } }
|
等價的C語言表示方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #define IS_UINT_SUB_UNDERFLOW(x, y) ((x) - (y) > (x)) #define IS_UINT_ADD_OVERFLOW(x, y) ((x) + (y) < (x)) #define IS_UINT_MUL_OVERFLOW(x, y, size_max) ((x) && (y) > (size_max) / (x)) #define PAGE_SIZE 4096u
void foo(unsigned int len, unsigned int size) { if (IS_UINT_SUB_UNDERFLOW(PAGE_SIZE - 2, size)) { printf("underflow! bad user input!\n"); } else { unsigned int capacity = PAGE_SIZE - 2 - size; if (len > capacity) { printf("no capacity left\n"); } else { printf("sufficient capacity %u\n", capacity); } } }
|
不過這需要CPU實作Modular arithmetic而不是Saturation arithmetic
不然還是有像Integers)這樣的第三方library,不過易用性就不如Rust了
Reference
– Rust: detect unsigned integer underflow