內核工具 – Sparse 簡介

sparse介紹

        Sparse 誕生於 2004 年, 是由linux之父開發的, 目的就是提供一個靜態檢查代碼的工具, 從而減小linux內核的隱患.html

其實在Sparse以前, 已經有了一個不錯的代碼靜態檢查工具("SWAT"), 只不過這個工具不是免費軟件, 使用上有一些限制.node

因此 linus 仍是本身開發了一個靜態檢查工具.linux

具體能夠參考這篇文章(2004年的文章了): Finding kernel problems automatically工具

Sparse相關的資料很是少, 關於它的使用方法我也是網上查找+本身實驗得出來的.測試

內核代碼中還有一個簡略的關於 Sparse的說明文件: Documentation/sparse.txtui

Sparse經過 gcc 的擴展屬性 __attribute__ 以及本身定義的 __context__ 來對代碼進行靜態檢查.spa

這些屬性以下(儘可能整理的,可能還有些不全的地方):.net

#define __bitwise    __attribute__((bitwise))     
確保變量是相同的位方式(好比 bit-endian, little-endiandeng)    
#define __user    __attribute__((noderef, address_space(1)))     
指針地址必須在用戶地址空間    
#define __kernel    __attribute__((noderef, address_space(0)))     
指針地址必須在內核地址空間    
#define __iomem    __attribute__((noderef, address_space(2)))     
指針地址必須在設備地址空間    
#define __safe    __attribute__((safe))     
變量能夠爲空    
#define __force    __attribute__((force))     
變量能夠進行強制轉換    
#define __nocast    __attribute__((nocast))     
參數類型與實際參數類型必須一致    
#define __acquires(x)    __attribute__((context(x, 0, 1)))     
參數x 在執行前引用計數必須是0,執行後,引用計數必須爲1    
#define __releases(x)    __attribute__((context(x, 1, 0)))     
與 __acquires(x) 相反    
#define __acquire(x)    __context__(x, 1)     
參數x 的引用計數 + 1    
#define __release(x)    __context__(x, -1)     
與 __acquire(x) 相反    
#define __cond_lock(x,c)    ((c) ? ({ __acquire(x); 1; }) : 0)     
參數c 不爲0時,引用計數 + 1, 並返回1    
其中 __acquires(x) 和 __releases(x), __acquire(x) 和 __release(x) 必須配對使用, 不然 Sparse 會給出警告
 
注: 在Fedora系統中經過 rpm 安裝的 sparse 存在一個小bug.
即便用時會報出 error: unable to open ’stddef.h’ 的錯誤, 最好從本身源碼編譯安裝 sparse.
參考: http://wangcong.org/blog/archives/504

Sparse 使用方法

__bitwise 的使用

      主要做用就是確保內核使用的整數是在一樣的位方式下.指針

在內核代碼根目錄下 grep -r '__bitwise', 會發現內核代碼中不少地方都使用了這個宏.code

對於使用了這個宏的變量, Sparse 會檢查這個變量是否一直在同一種位方式(big-endian, little-endian或其餘)下被使用,

若是此變量在多個位方式下被使用了, Sparse 會給出警告.

內核代碼中的例子:

/*
 內核版本:v2.6.32.61  file:include/sound/core.h 51行 
*/
typedef int __bitwise snd_device_type_t;

 __user 的使用

若是使用了 __user 宏的指針不在用戶地址空間初始化, 或者指向內核地址空間, 設備地址空間等等, Sparse會給出警告.

內核代碼中的例子:

/*
 內核版本:v2.6.32.61  file:arch/score/kernel/signal.c 45行 
*/

static
 
int setup_sigcontext(structpt_regs *regs, struct sigcontext __user *sc)

 __kernel 的使用

若是使用了 __kernel 宏的指針不在內核地址空間初始化, 或者指向用戶地址空間, 設備地址空間等等, Sparse會給出警告.

內核代碼中的例子:

/*  內核版本:v2.6.32.61  file:arch/s390/lib/uaccess_pt.c 180行   
*/ memcpy(to, ( void __kernel __force *)  from , n);

__iomem 的使用

若是使用了 __iomem 宏的指針不在設備地址空間初始化, 或者指向用戶地址空間, 內核地址空間等等, Sparse會給出警告.

內核代碼中的例子:

*/
/* 內核版本:v2.6.32.61  file:arch/microblaze/include/asm/io.h 22行 */

static inline unsigned char __raw_readb(const volatile void __iomem *addr)

__safe 的使用

使用了 __safe修飾的變量在使用前沒有判斷它是否爲空(null), Sparse會給出警告.

我參考的內核版本(v2.6.32.61) 中的全部內核代碼都沒有使用 __safe, 估計多是因爲隨着gcc版本的更新,

gcc已經會對這種狀況給出警告, 因此沒有必要用Sparse去檢查了.

__force 的使用

使用了__force修飾的變量能夠進行強制類型轉換, 沒有使用 __force修飾的變量進行強制類型轉換時, Sparse會給出警告.

內核代碼中的例子:

/* 內核版本:v2.6.32.61  file:arch/s390/lib/uaccess_pt.c 180行 */ 
memcpy(to, (void __kernel __force *) from, n);

__nocast 的使用

使用了__nocast修飾的參數的類型必須和實際傳入的參數類型一致才行,不然Sparse會給出警告.

內核代碼中的例子:

/* 內核版本:v2.6.32.61  file:fs/xfs/support/ktrace.c 55行 */
ktrace_alloc(int nentries, unsigned int __nocast sleep)

 __acquires __releases __acquire __release的使用

這4個宏都是和鎖有關的, __acquires 和 __releases 必須成對使用, __acquire 和 __release 必須成對使用, 不然Sparse會給出警告.

__cond_lock 的使用

這個宏有點特別, 由於沒有 __cond_unlock 之類的宏和它對應.

之因此有這個宏的緣由能夠參見: http://yarchive.net/comp/linux/sparse.html 最後一段.

這個宏的來源清楚了, 可是爲何這個宏裏面還要調用一次 __acquire(x)? 我也不是很清楚, 在網上找了很久也沒找到, 誰能指教的話很是感謝!!!

Sparse 在編譯內核中的使用

用 Sparse 對內核進行靜態分析很是簡單.

# 檢查全部內核代碼
make C=1 檢查全部從新編譯的代碼
make C=2 檢查全部代碼, 無論是否是被從新編譯

補充

Sparse除了可以用在內核代碼的靜態分析上, 其實也能夠用在通常的C語言程序中.

好比下面的小例子:

/******************************************************************************
 * @file    : sparse_test.c
 * @author  : wangyubin
 * @date    : Fri Feb 28 16:33:34 2014
 * 
 * @brief   : 測試 sparse 的各個檢查點
 * history  : init
 ******************************************************************************/
#include <stdio.h>
#define __acquire(x) __context__(x,1)
#define __release(x) __context__(x,-1)
int main(int argc, char *argv[])
{
    int lock = 1;
    __acquire(lock);
    /* TODO something */
    __release(lock);            /* 註釋掉這一句 sparse 就會報錯 */
    return 0;
}
相關文章
相關標籤/搜索