
??想了解更多關(guān)于開源的內(nèi)容,請訪問:??
??51CTO 開源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
接著本系列之前的文章,補充下CodeChecker的部分用法。使用命令CodeChecker analyzers可以查看當前支持的靜態(tài)分析檢查器analyzers。如果安裝了Cppcheck就會展示出來,clangsa和Clang-tidy是LLVM Clang編譯工具鏈提供的靜態(tài)分析檢查器。
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ CodeChecker analyzers
cppcheck
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ export PATH=~/openharmony/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PATH
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ CodeChecker analyzers
clangsa
cppcheck
clang-tidy
在使用CodeChecker靜態(tài)分析時,可以使用選項[--analyzers ANALYZER [ANALYZER ...]指定只使能部分分析器,這些ANALYZER可以是clangsa、cppcheck、clang-tidy。如下:
CodeChecker analyze --analyzers clangsa compile_commands.json -o ./reports
系列文章中的前幾篇中,已經(jīng)到了Cppcheck和Clang-tidy這2個靜態(tài)檢查分析器,本文快速介紹下clangsa靜態(tài)檢查分析器。
1、clangsa介紹
前面系列文章介紹了Clang Static Analyzer,知道Clang 靜態(tài)分析器CSA是一個源代碼分析工具,可查找 C、C++ 和 Objective-C 程序的bugs。Clang Static Analyzer可以理解提供了兩部分內(nèi)容,一部分是CSA工具,比如scan-build、CodeChecker,還有一部分是clangsa,雖然是Clang Static Analyzer的縮寫,但是指的是Clang Static Analyzer的靜態(tài)檢查分析器,定義了一些的代碼缺陷檢查規(guī)則。詳細可以參考鏈接https://clang.llvm.org/docs/ClangStaticAnalyzer.html,除了支持的檢查器,還有用戶文檔和開發(fā)文檔,用于支持定制自己的檢查器。
本文主要學(xué)習(xí)下clangsa支持的檢查器Available Checkers。
clangsa、cppcheck、clang-tidy屬于分析器analyzer,每個分析器定義了自己的一套檢查器“checkers”。檢查器還會通過分組進行管理。clangsa分析器的檢查器分為三組,分別為:
- Default Checkers 默認檢查器,可以發(fā)現(xiàn)安全和API使用缺陷,死代碼和其他邏輯錯誤。。請參閱下面的默認檢查器列表。
- Experimental Checkers 實驗檢查器也稱為alpha checkers,處于開發(fā)過程中,默認關(guān)閉。可能會崩潰,也可能產(chǎn)生較多的誤報。
- Debug Checkers 調(diào)測檢查器被分析器開發(fā)者用作調(diào)測目的。
2、Default Checkers 默認檢查器
默認檢查器分為如下幾組。只關(guān)注C、C++相關(guān)的檢查器,osx、Fuchsia、nullability、WebKit可以自行按需了解。
- core 核心語言特性,通用的檢查器
- cplusplus C++語言檢查器
- deadcode 死代碼檢查器
- nullability Objective-C空指針傳遞和解引用檢查器
- optin 可移植、性能或編程風格相關(guān)的檢查器
- security 安全相關(guān)檢查器
- unix POSIX/Unix檢查器
- osx macOS檢查器
- Fuchsia Fuchsia操作系統(tǒng)相關(guān)的檢查器。Fuchsia是Google開發(fā)的一款開源操作系統(tǒng)。
- WebKit WebKit相關(guān)檢查器。WebKit是開源Web瀏覽器引擎。
(1)core檢查器
core檢查器關(guān)注語言的核心特性,包含通用目的的檢查器,例如除零錯誤,空指針解引用,使用未初始化的值等。這些檢查器需要一直開啟,其他檢查器依賴這些核心檢查器。
- core.CallAndMessage (C, C++, ObjC)
檢查函數(shù)調(diào)用的邏輯錯誤,Objective-C消息表達錯誤,例如,未初始化參數(shù),空函數(shù)指針等。適用于C, C++, ObjC等編程語言。
//C
void test() {
void (*foo)(void);
foo = 0;
foo(); // warn: function pointer is null 函數(shù)指針為空
}
// C++
class C {
public:
void f();
};
void test() {
C *pc;
pc->f(); // warn: object pointer is uninitialized 對象指針未初始化
}
// C++
class C {
public:
void f();
};
void test() {
C *pc = 0;
pc->f(); // warn: object pointer is null 對象指針為空
}
// Objective-C
......
- core.DivideZero (C, C++, ObjC)
檢查是否存在除零錯誤。
void test(int z) {
if (z == 0)
int x = 1 / z; // warn 除零錯誤
}
void test() {
int x = 1;
int y = x % 0; // warn 除零錯誤
}
- core.NullDereference (C, C++, ObjC)
檢查空指針的解引用錯誤。SuppressAddressSpaces選項會屏蔽帶地址空間的空指針解引用告警??梢允褂眠x項-analyzer-config core.NullDereference:SuppressAddressSpaces=false來關(guān)注該SuppressAddressSpaces。默認是開啟的。
// C
void test(int *p) {
if (p)
return;
int x = p[0]; // warn
}
// C
void test(int *p) {
if (!p)
*p = 0; // warn
}
// C++
class C {
public:
int x;
};
void test() {
C *pc = 0;
int k = pc->x; // warn
}
// Objective-C
......
- core.StackAddressEscape ?
檢查堆棧越界,在一個函數(shù)內(nèi)部聲明的局部變量存儲在??臻g上,不能把該變量的地址傳遞到函數(shù)外部。
char const *p;
void test() {
char const str[] = "string";
p = str; // warn StackAddressEscape告警
}
void* test() {
return __builtin_alloca(12); // warn StackAddressEscape告警
}
void test() {
static int *x;
int y;
x = &y; // warn StackAddressEscape告警
}
- core.UndefinedBinaryOperatorResult ?
檢查二元表達式中的未定義的值。
void test() {
int x;
int y = x + 1; // warn: left operand is garbage
}
- core.VLASize ?
檢查變長數(shù)組(Variable Length Arrays,VLA)的長度未定義或者零值。
void test() {
int x;
int vla1[x]; // warn: garbage as size
}
void test() {
int x = 0;
int vla2[x]; // warn: zero size
}
- core.uninitialized.ArraySubscript ?
檢查未初始化的值用作數(shù)組下標。
void test() {
int i, a[10];
int x = a[i]; // warn: array subscript is undefined
}
- core.uninitialized.Assign ?
檢查使用未初始化的值進行賦值。
void test() {
int x;
x |= 1; // warn: left expression is uninitialized 左表達式未初始化
}
- core.uninitialized.Branch ?
檢查未初始化的值用于條件分支。
void test() {
int x;
if (x) // warn
return;
}
- core.uninitialized.CapturedBlockVariable ?
檢查代碼塊捕獲未初始化的值。
void test() {
int x;
^{ int y = x; }(); // warn
}
- core.uninitialized.UndefReturn ?
檢查未初始化的值傳遞到外層函數(shù)。
int test() {
int x;
return x; // warn
}
(2)cplusplus檢查器
C++語言相關(guān)的檢查器。
- cplusplus.InnerPointer (C++)
檢查在re/deallocation之后使用的C容器的內(nèi)部指針。C標準庫中的許多容器方法會讓指向容器元素的引用無效,包含實際引用,迭代器,原生指針(raw pointers)。使用無效的引用會引起未定義行為,這通常是C++中內(nèi)存錯誤的源頭,也是該檢查致力于檢查出來的代碼缺陷。
該檢查器只是適合std::string對象,不能識別出復(fù)雜的用法,比如傳遞std::string_view這樣的unowned指針。
void deref_after_assignment() {
std::string s = "llvm";
const char *c = s.data(); // note: pointer to inner buffer of 'std::string' obtained here
s = "clang"; // note: inner buffer of 'std::string' reallocated by call to 'operator='
consume(c); // warn: inner pointer of container used after re/deallocation
}
const char *return_temp(int x) {
return std::to_string(x).c_str(); // warn: inner pointer of container used after re/deallocation
// note: pointer to inner buffer of 'std::string' obtained here
// note: inner buffer of 'std::string' deallocated by call to destructor
}
- cplusplus.NewDelete (C++)
檢查重復(fù)釋放double-free和釋放后使用UAF等內(nèi)存問題。追蹤被new/delete管理的內(nèi)存。
void f(int *p);
void testUseMiddleArgAfterDelete(int *p) {
delete p;
f(p); // warn: use after free 釋放后使用
}
class SomeClass {
public:
void f();
};
void test() {
SomeClass *c = new SomeClass;
delete c;
c->f(); // warn: use after free 釋放后使用
}
void test() {
int *p = (int *)__builtin_alloca(sizeof(int));
delete p; // warn: deleting memory allocated by alloca
// 釋放使用alloca申請的內(nèi)存
}
void test() {
int *p = new int;
delete p;
delete p; // warn: attempt to free released 重復(fù)釋放內(nèi)存
}
void test() {
int i;
delete &i; // warn: delete address of local 釋放局部變量的棧內(nèi)存
}
void test() {
int *p = new int[1];
delete[] (++p);
// warn: argument to 'delete[]' is offset by 4 bytes
// from the start of memory allocated by 'new[]'
// 傳遞給'delete[]'的參數(shù)相對'new[]'申請的內(nèi)存開始地址進行了4字節(jié)偏移
// 釋放的不是申請的內(nèi)存。
}
- cplusplus.NewDeleteLeaks (C++)
檢查內(nèi)存泄露memory leaks. 追蹤new/delete管理的內(nèi)存。
void test() {
int *p = new int;
} // warn 申請未釋放
- cplusplus.PlacementNewChecker (C++)
Check if default placement new is provided with pointers to sufficient storage capacity. 檢查缺省的定位new(placement new)指針操作,有足夠的存儲能力。
#include <new>
void f() {
short s;
long *lp = ::new (&s) long; // warn
}
- cplusplus.SelfAssignment (C++)
檢查C++的自賦值的copy 和 move賦值操作。 - cplusplus.StringChecker (C++)
檢查 std::string 操作。檢查從std::string對象構(gòu)造的cstring是否為空NULL。如果檢查器不能推斷出該指針為空,會假設(shè)其非空,用于滿足構(gòu)造。
該檢查器可以檢查SEI CERT C++編程規(guī)則STR51-CPP。不要從空指針創(chuàng)建std::string對象。
#include <string>
void f(const char *p) {
if (!p) {
std::string msg(p); // warn: The parameter must not be null
}
}
(3)deadcode檢查器
- deadcode.DeadStores ?
檢查存儲在變量里的值后續(xù)未被使用。
void test() {
int x;
x = 1; // warn
}
WarnForDeadNestedAssignments選項會使能檢查器來檢測嵌套的無用的賦值代碼,可以使用選項-analyzer-config deadcode.DeadStores:WarnForDeadNestedAssignments=false來關(guān)閉該選項。WarnForDeadNestedAssignments`選項默認開啟。
會對這樣的代碼進行告警,例如: if ((y = make_int())) { }。
(4)security檢查器
- security.FloatLoopCounter ?
浮點數(shù)作為循環(huán)變量 (CERT: FLP30-C, FLP30-CPP).
void test() {
for (float x = 0.1f; x <= 1.0f; x += 0.1f) {} // warn
}
- security.insecureAPI.UncheckedReturn ?
Warn on uses of functions whose return values must be always checked.
void test() {
setuid(1); // warn
}
- security.insecureAPI.bcmp ?
Warn on uses of the ‘bcmp’ function.
void test() {
bcmp(ptr0, ptr1, n); // warn
}
- security.insecureAPI.bcopy ?
Warn on uses of the ‘bcopy’ function.
void test() {
bcopy(src, dst, n); // warn
}
- security.insecureAPI.bzero ( C )
Warn on uses of the ‘bzero’ function.
void test() {
bzero(ptr, n); // warn
}
- security.insecureAPI.getpw ?
Warn on uses of the ‘getpw’ function.
void test() {
char buff[1024];
getpw(2, buff); // warn
}
- security.insecureAPI.gets ?
Warn on uses of the ‘gets’ function.
void test() {
char buff[1024];
gets(buff); // warn
}
- security.insecureAPI.mkstemp ?
Warn when ‘mkstemp’ is passed fewer than 6 X’s in the format string.
void test() {
mkstemp("XX"); // warn
}
- security.insecureAPI.mktemp ?
Warn on uses of the mktemp function.
void test() {
char *x = mktemp("/tmp/zxcv"); // warn: insecure, use mkstemp
}
- security.insecureAPI.rand ?
Warn on uses of inferior random number generating functions (only if arc4random function is available): drand48, erand48, jrand48, lcong48, lrand48, mrand48, nrand48, random, rand_r.
void test() {
random(); // warn
}
- security.insecureAPI.strcpy ?
Warn on uses of the strcpy and strcat functions.
void test() {
char x[4];
char *y = "abcd";
strcpy(x, y); // warn
}
- security.insecureAPI.vfork ?
Warn on uses of the ‘vfork’ function.
void test() {
vfork(); // warn
}
- security.insecureAPI.DeprecatedOrUnsafeBufferHandling ?
Warn on occurrences of unsafe or deprecated buffer handling functions, which now have a secure variant: sprintf, vsprintf, scanf, wscanf, fscanf, fwscanf, vscanf, vwscanf, vfscanf, vfwscanf, sscanf, swscanf, vsscanf, vswscanf, swprintf, snprintf, vswprintf, vsnprintf, memcpy, memmove, strncpy, strncat, memset
void test() {
char buf [5];
strncpy(buf, "a", 1); // warn
}
(5) unix檢查器
- unix.API ?
檢查UNIX/POSIX函數(shù)調(diào)用:open, pthread_once, calloc, malloc, realloc, alloca。
// Currently the check is performed for apple targets only.
void test(const char *path) {
int fd = open(path, O_CREAT);
// warn: call to 'open' requires a third argument when the
// 'O_CREAT' flag is set
}
void f();
void test() {
pthread_once_t pred = {0x30B1BCBA, {0}};
pthread_once(&pred, f);
// warn: call to 'pthread_once' uses the local variable
}
void test() {
void *p = malloc(0); // warn: allocation size of 0 bytes
}
void test() {
void *p = calloc(0, 42); // warn: allocation size of 0 bytes
}
void test() {
void *p = malloc(1);
p = realloc(p, 0); // warn: allocation size of 0 bytes
}
void test() {
void *p = alloca(0); // warn: allocation size of 0 bytes
}
void test() {
void *p = valloc(0); // warn: allocation size of 0 bytes
}
- unix.Malloc ?
檢查內(nèi)存泄露,重復(fù)釋放、釋放后使用等內(nèi)存代碼缺陷。追蹤被malloc()/free()管理的內(nèi)存。
void test() {
int *p = malloc(1);
free(p);
free(p); // warn: attempt to free released memory
}
void test() {
int *p = malloc(sizeof(int));
free(p);
*p = 1; // warn: use after free
}
void test() {
int *p = malloc(1);
if (p)
return; // warn: memory is never released
}
void test() {
int a[] = { 1 };
free(a); // warn: argument is not allocated by malloc
}
void test() {
int *p = malloc(sizeof(char));
p = p - 1;
free(p); // warn: argument to free() is offset by -4 bytes
}
- unix.MallocSizeof (C)
Check for dubious malloc arguments involving sizeof.
void test() {
long *p = malloc(sizeof(short));
// warn: result is converted to 'long *', which is
// incompatible with operand type 'short'
free(p);
}
- unix.MismatchedDeallocator (C, C++)
Check for mismatched deallocators.
// C, C++
void test() {
int *p = (int *)malloc(sizeof(int));
delete p; // warn
}
// C, C++
void __attribute((ownership_returns(malloc))) *user_malloc(size_t);
void test() {
int *p = (int *)user_malloc(sizeof(int));
delete p; // warn
}
// C, C++
void test() {
int *p = new int;
free(p); // warn
}
// C, C++
void test() {
int *p = new int[1];
realloc(p, sizeof(long)); // warn
}
// C, C++
template <typename T>
struct SimpleSmartPointer {
T *ptr;
explicit SimpleSmartPointer(T *p = 0) : ptr(p) {}
~SimpleSmartPointer() {
delete ptr; // warn
}
};
void test() {
SimpleSmartPointer<int> a((int *)malloc(4));
}
// C++
void test() {
int *p = (int *)operator new(0);
delete[] p; // warn
}
// Objective-C, C++
void test(NSUInteger dataLength) {
int *p = new int;
NSData *d = [NSData dataWithBytesNoCopy:p
length:sizeof(int) freeWhenDone:1];
// warn +dataWithBytesNoCopy:length:freeWhenDone: cannot take
// ownership of memory allocated by 'new'
}
- unix.Vfork ?
檢查vfork是否合適使用。
int test(int x) {
pid_t pid = vfork(); // warn
if (pid != 0)
return 0;
switch (x) {
case 0:
pid = 1;
execl("", "", 0);
_exit(1);
break;
case 1:
x = 0; // warn: this assignment is prohibited
break;
case 2:
foo(); // warn: this function call is prohibited
break;
default:
return 0; // warn: return is prohibited
}
while(1);
}
- unix.cstring.BadSizeArg ?
檢查傳遞給C字符串函數(shù)的size參數(shù)的錯誤模式。使用-Wno-strncat-size編譯器選項屏蔽其他strncat-相關(guān)的編譯告警。
void test() {
char dest[3];
strncat(dest, """""""""""""""""""""""""*", sizeof(dest));
// warn: potential buffer overflow
}
- unix.cstring.NullArg ?
檢查空指針作為參數(shù)傳遞給C字符串函數(shù):strlen, strnlen, strcpy, strncpy, strcat, strncat, strcmp, strncmp, strcasecmp, strncasecmp, wcslen, wcsnlen。
int test() {
return strlen(0); // warn
}
3、Experimental Checkers 實驗檢查器
(1)alpha.clone檢查器
- alpha.clone.CloneChecker (C, C++, ObjC)
檢查類似的重復(fù)代碼塊。
void log();
int max(int a, int b) { // warn
log();
if (a > b)
return a;
return b;
}
int maxClone(int x, int y) { // similar code here
log();
if (x > y)
return x;
return y;
}
(2)alpha.core檢查器
簡單看一個,其他更多檢查器,可以自行訪問官網(wǎng)文檔https://clang.llvm.org/docs/analyzer/checkers.html#alpha-core。
- alpha.core.C11Lock
類似alpha.unix.PthreadLock,檢查互斥鎖的mtx_t mutexeslocking/unlocking的上鎖和解鎖。
mtx_t mtx1;
void bad1(void)
{
mtx_lock(&mtx1);
mtx_lock(&mtx1); // warn: This lock has already been acquired
}
??想了解更多關(guān)于開源的內(nèi)容,請訪問:??
??51CTO 開源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??