linux kernel - sparse 靜態分析工具

收錄在 AIoT Ameba 2020 - Just for Fun

--
分享一個 Linux 上的 靜態分析工具 – Sparse

這是 linus Torvalds 2003 年開始的專案,

像商用軟體 Coverity SWAT, 也是做靜態分析, Sparse 不用錢^^

sparse 是一個靜態代碼分析工具, 可以檢查所寫的代碼是否有些問題.
他是透過 gcc 的擴展屬性 __attribute__, 以及自己定義的 __context__,
來對代碼進行靜態檢查.

安裝方式:


1. 得到最新的 source code 來編譯安裝
 
    $ git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git   

    $ make

    $ sudo cp sparse /usr/bin/sparse

2. make C=2

我們寫一個簡單的 spin lock 來看看.

https://github.com/neojou/new_ldd/blob/master/sparse-lock/sparse-lock.c


做了 spin_lock 但不做 spin_unlock



    warning: context imbalance in 'work_handler1' - different lock contexts for basic block

做了 spin_unlock 但不做 spin_lock


    warning: context imbalance in 'work_handler1' : unexpected unlock


--
acquire 作法:

# define __acquire(x) __context__(x,1)


--
接下來我們把 rtw88-usb 也加上 sparse check 並修改.
中間遇到 __le32 / __le16 的 bitwise 檢查. patch 如下:
  https://github.com/neojou/rtw88-usb/commit/3f10af10ae5909bea41c931378e1cd89627078a4


在這個
https://lore.kernel.org/patchwork/patch/863311/
中提議的 le32_get_bits() 和 le32p_replace_bits()

是在 include/linux/bitfield.h, 這邊利用 compiler 的寫法和 C define 用法值得參考

#define ____MAKE_OP(type,base,to,from)     \
static __always_inline __##type type##_encode_bits(base v, base field) \
{         \
 if (__builtin_constant_p(v) && (v & ~field_mask(field))) \
  __field_overflow();     \
 return to((v & field_mask(field)) * field_multiplier(field)); \
}         \
static __always_inline __##type type##_replace_bits(__##type old, \
     base val, base field)  \
{         \
 return (old & ~to(field)) | type##_encode_bits(val, field); \
}         \
static __always_inline void type##p_replace_bits(__##type *p,  \
     base val, base field)  \
{         \
 *p = (*p & ~to(field)) | type##_encode_bits(val, field); \
}         \
static __always_inline base type##_get_bits(__##type v, base field) \
{         \
 return (from(v) & field)/field_multiplier(field);  \
}
#define __MAKE_OP(size)       \
 ____MAKE_OP(le##size,u##size,cpu_to_le##size,le##size##_to_cpu) \
 ____MAKE_OP(be##size,u##size,cpu_to_be##size,be##size##_to_cpu) \
 ____MAKE_OP(u##size,u##size,,)
____MAKE_OP(u8,u8,,)
__MAKE_OP(16)
__MAKE_OP(32)
__MAKE_OP(64)
其他還有如下, 後續有空時繼續研究:


宏名稱
宏定義
檢查點
__bitwise__attribute__((bitwise))確保變量是相同的位方式(比如 bit-endian, little-endiandeng)
__user__attribute__((noderef, address_space(1)))指針地址必須在用戶地址空間
__kernel__attribute__((noderef, address_space(0)))指針地址必須在內核地址空間
__iomem__attribute__((noderef, address_space(2)))指針地址必須在設備地址空間
__safe__attribute__((safe))變量可以為空
__force__attribute__((force))變量可以進行強制轉換
__nocast__attribute__((nocast))參數類型與實際參數類型必須一致
__acquires(x)__attribute__((context(x, 0, 1)))參數x 在執行前引用計數必須是0,執行後,引用計數必須為1
__releases(x)__attribute__((context(x, 1, 0)))與 __acquires(x) 相反
__acquire(x)__context__(x, 1)參數x 的引用計數 + 1
__release(x)__context__(x, -1)與 __acquire(x) 相反
__cond_lock(x,c)((c) ? ({ __acquire(x); 1; }) : 0)參數c 不為0時,引用計數 + 1, 並返回1


留言

熱門文章