Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學

Linux 內核宏 __user 與 Sparse 靜態檢查機制解析

編輯:Linux 系統常見問題解答

宏定義源碼


// 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 不成對)檢查所有路徑的鎖邏輯

最佳實踐

  1. 所有面向用戶空間緩衝區的內核函數參數**必須**標記 __user。
  2. 所有映射 I/O 內存的指針變量**必須**標記 __iomem。
  3. 常規內核內部指針可選標記 __kernel (默認即為 0)。
  4. CI 流程集成 make C=1 攔截地址空間誤用 Bug。
Copyright © Windows教程網 All Rights Reserved