宏定義源碼
// include/linux/compiler.h
#ifdef __CHECKER__
# define __user __attribute__((noderef, address_space(1)))
# define __kernel __attribute__((address_space(0)))
# define __iomem __attribute__((noderef, address_space(2)))
#else
# define __user
# define __kernel
# define __iomem
#endif
關鍵點解析
- __CHECKER__:僅在啟用 Sparse 靜態檢查時定義 (編譯命令
make C=1 或 make C=2)。
- __attribute__((noderef)):告訴 Sparse 該指針不能直接解引用 (如
*ptr),必須通過特定訪問函數 (copy_from_user, put_user 等)。
- address_space(n):定義指針所屬地址空間編號,Sparse 檢查跨空間訪問合法性:
0:核心空間 (__kernel)
1:用戶空間 (__user)
2:I/O 內存空間 (__iomem)
- 非檢查編譯時:宏展開為空,**零開銷**,不影響生成代碼。
Sparse 靜態檢查工具
- 專為 Linux 內核設計的 C 語義檢查器,檢查:指針地址空間混用、鎖平衡、內存屏障、位操作、類型轉換等。
- 安裝:
git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git && make && make install 或發行版包 sparse。
啟用檢查
# 檢查當前目錄文件
make C=1 # 僅重新編譯的文件
make C=2 # 強制檢查所有文件
# 檢查特定文件
make C=1 drivers/char/mem.c
實際驅動代碼中的應用
// 字符設備 read 回調
ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) {
// buf 標記為 __user,Sparse 會檢查:
// 1. 代碼中不直接 *buf 或 buf[i] (noderef)
// 2. 必須使用 copy_to_user(buf, kernel_ptr, count)
// 3. 不能將 buf 傳遞給期望 __kernel 指針的函數
char *kbuf = kmalloc(count, GFP_KERNEL);
if (!kbuf) return -ENOMEM;
// ... 填充 kbuf ...
if (copy_to_user(buf, kbuf, count)) {
kfree(kbuf);
return -EFAULT;
}
kfree(kbuf);
return count;
}
常見 Sparse 警告示例
| 警告 | 原因 | 修正 |
| dereference of noderef expression | 直接解引用 __user/__iomem 指針 | 使用 copy_to/from_user, readb/writeb, ioread32/iowrite32 等 |
| incorrect address space in argument | 將 __user 指針傳給期望 __kernel 的函數 | 確保接口匹配,或在安全上下文中轉換 |
| symbol 'xxx' was not declared. Should it be static? | 函數/變量未聲明就使用 | 添加 static 或頭文件聲明 |
| context imbalance in 'func' - unexpected unlock | 鎖操作不匹配 (lock/unlock 不成對) | 檢查所有路徑的鎖邏輯 |
最佳實踐
- 所有面向用戶空間緩衝區的內核函數參數**必須**標記
__user。
- 所有映射 I/O 內存的指針變量**必須**標記
__iomem。
- 常規內核內部指針可選標記
__kernel (默認即為 0)。
- CI 流程集成
make C=1 攔截地址空間誤用 Bug。