0%

Issue about unsigned integer overflow and underflow

過年前要生產出一些東西出來,不然太久沒寫文章了
看到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