1、Sparse介绍
Linus在2004年开发了kernel代码静态检查工具,可以检查出kernel中潜在的风险代码,Sparse通过 gcc 的扩展属性__attribute__
以及自己定义的 __context__
来对代码进行静态检查,重点检查kernel代码中的变量类型和各类锁。
具体可以参考kernel文档
Documentation/dev-tools/sparse.rst
那么如何指定代码可以被Sparse检查的到呢?以__iomem
为例,可以对需要检查的变量类型放到特定文件中,以__CHECKER__
进行限定即可。
#ifdef __CHECKER__
# define __user __attribute__((noderef, address_space(1)))
# define __kernel __attribute__((address_space(0)))
# define __safe __attribute__((safe))
# define __force __attribute__((force))
# define __nocast __attribute__((nocast))
# define __iomem __attribute__((noderef, address_space(2)))
# define __must_hold(x) __attribute__((context(x,1,1)))
# define __acquires(x) __attribute__((context(x,0,1)))
# define __releases(x) __attribute__((context(x,1,0)))
# define __acquire(x) __context__(x,1)
# define __release(x) __context__(x,-1)
# define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0)
# define __percpu __attribute__((noderef, address_space(3)))
# define __rcu __attribute__((noderef, address_space(4)))
# define __private __attribute__((noderef))
extern void __chk_user_ptr(const volatile void __user *);
extern void __chk_io_ptr(const volatile void __iomem *);
# define ACCESS_PRIVATE(p, member) (*((typeof((p)->member) __force *) &(p)->member))
#else /* __CHECKER__ */
# ifdef STRUCTLEAK_PLUGIN
# define __user __attribute__((user))
# else
# define __user
# endif
# define __kernel
# define __safe
# define __force
# define __nocast
# define __iomem
# define __chk_user_ptr(x) (void)0
# define __chk_io_ptr(x) (void)0
# define __builtin_warning(x, y...) (1)
# define __must_hold(x)
# define __acquires(x)
# define __releases(x)
# define __acquire(x) (void)0
# define __release(x) (void)0
# define __cond_lock(x,c) (c)
# define __percpu
# define __rcu
# define __private
# define ACCESS_PRIVATE(p, member) ((p)->member)
#endif /* __CHECKER__ */
2、安装Sparse工具
通常来说,开发环境中没有Sparse工具,需要手动安装,否则报如下错误:
ubuntu16@ubuntu16:linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4 C=2
CHECK scripts/mod/empty.c
/bin/sh: 1: sparse: not found
scripts/Makefile.build:261: recipe for target 'scripts/mod/empty.o' failed
ubuntu16@ubuntu16:~$ git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git
Cloning into 'sparse'...
remote: Enumerating objects: 17, done.
remote: Counting objects: 100% (17/17), done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 20026 (delta 6), reused 0 (delta 0), pack-reused 20009
Receiving objects: 100% (20026/20026), 4.29 MiB | 924.00 KiB/s, done.
Resolving deltas: 100% (14035/14035), done.
Checking connectivity... done.
ubuntu16@ubuntu16:~$ cd sparse/
ubuntu16@ubuntu16:sparse$ make
Makefile:152: Your system does not have libxml, disabling c2xml
Makefile:170: Your system does not have sqlite3, disabling semind
Makefile:192: Your system does not have gtk3/gtk2, disabling test-inspect
Makefile:226: Your system does not have llvm, disabling sparse-llvm
ubuntu16@ubuntu16:sparse$ make install
Makefile:152: Your system does not have libxml, disabling c2xml
Makefile:170: Your system does not have sqlite3, disabling semind
Makefile:192: Your system does not have gtk3/gtk2, disabling test-inspect
Makefile:226: Your system does not have llvm, disabling sparse-llvm
INSTALL /home/ubuntu16/bin/sparse
INSTALL /home/ubuntu16/bin/cgcc
INSTALL /home/ubuntu16/share/man/man1/sparse.1
INSTALL /home/ubuntu16/share/man/man1/cgcc.1
ubuntu16@ubuntu16:sparse$
3、执行Sparse静态检查
从kernel顶层Makefile中可以看出,当编译kernel时通过指定C=1
或C=2
,可以调用到Sparse进行代码静态检查。
192 # Call a source code checker (by default, "sparse") as part of the
193 # C compilation.
194 #
195 # Use 'make C=1' to enable checking of only re-compiled files.
196 # Use 'make C=2' to enable checking of *all* source files, regardless
197 # of whether they are re-compiled or not.
198 #
199 # See the file "Documentation/dev-tools/sparse.rst" for more details,
200 # including where to get the "sparse" utility.
执行结果:
ubuntu16@ubuntu16:linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4 C=2
CHECK scripts/mod/empty.c
CALL scripts/atomic/check-atomics.sh
CALL scripts/checksyscalls.sh
CHECK arch/arm/vfp/vfpmodule.c
CHECK init/main.c
arch/arm/vfp/vfpmodule.c:38:17: warning: symbol 'vfp_vector' was not declared. Should it be static?
arch/arm/vfp/vfpmodule.c:56:17: warning: symbol 'vfp_current_hw_state' was not declared. Should it be static?
arch/arm/vfp/vfpmodule.c:323:6: warning: symbol 'VFP_bounce' was not declared. Should it be static?
arch/arm/vfp/vfpmodule.c:714:6: warning: symbol 'kernel_neon_begin' was not declared. Should it be static?
arch/arm/vfp/vfpmodule.c:745:6: warning: symbol 'kernel_neon_end' was not declared. Should it be static?
...