天天看點

linux靜态代碼檢查工具,linux下splint檢測C語言代碼品質

在linux下并沒有pclint,可以使用splint代替。splint使用一.splint介紹splint是一個靜态檢查C語言程式安全弱點和編寫錯誤的工具。splint會進行多種正常檢查,包括未使 用的變量,類型不一緻,使用未定義變量,無法執行的代碼,忽略傳回值,執行路徑未傳回,無限循環等錯誤。同 時通過在源碼中添加注記給出的附加資訊,使其可以進行功能更加強大的檢查。而注記,則是對檔案中的函數、變 量、參數以及類型進行假定的一種的程式化的注釋。二.splint的安裝下載下傳位址:

linux靜态代碼檢查工具,linux下splint檢測C語言代碼品質

檔案:splint-3.1.2-src.tar.gz

大小:2272KB

下載下傳:源碼包安裝:# tar zxvf splint-3.1.2.src.tgz# cd splint-3.1.2# mkdir /usr/local/splint# ./configure --prefix=/usr/local/splint# make install# vi ~/.bashrc添加:export LARCH_PATH=/usr/local/splint/share/splint/libexport LCLIMPORTDIR=/usr/local/splint/share/splint/import# source ~/.bashrc# export PATH=/usr/local/splint/bin/splint:$PATH三.splint的使用1.空引用錯誤在引用沒有指向任何記憶體位址的指針時,會導緻這種錯誤.也就是使用了一個沒有指派的指針.splint支援一種特别的注釋.這種注釋寫在C程式代碼中,用于對程式進行特殊說明.如下面這段程式.使用了進行了說明,表示說明*s的值可能會是NULL.//null.cchar firstChar1 ( char *s){return *s;}char firstChar2 ( char *s){if (s ==NULL) return '\0';return *s;}//END使用splint掃描這個程式時,會輸出:# splint null.cSplint 3.1.1 --- 28 Apr 2005null.c: (in function firstChar1)null.c:3:11: Dereference of possibly null pointer s: *snull.c:1:35: Storage s may become nullFinished checking --- 1 code warning found由于firstChar1和firstChar2都使用了null說明,表示指針s可能是個NULL值.是以,splint會對s值的使用情況進行檢查.因為firstChar2函數中,對s的值進行了NULL的判斷.是以,沒有對firstChar2函數的指針s輸出警告資訊.2.未定義的變量錯誤C語言中,要求先定義變量,而後才可使用.是以,當使用一個沒有定義的變量時,編譯就會出錯.如下例,使用說明的變量,表示必須進行定義.使用說明的變量,表示在 執行過此函數後,這個變量就進行了定義.// usedef.cextern void setVal ( int *x);extern int getVal ( int *x);extern int mysteryVal (int *x);int dumbfunc ( int *x, int i){if (i > 3) return *x;else if (i > 1)return getVal (x);else if (i == 0)return mysteryVal (x);else{setVal (x);return *x;}}// END使用splint檢查usedef.c$ splint usedef.cSplint 3.1.1 --- 28 Apr 2005usedef.c: (in function dumbfunc)usedef.c:7:19: Value *x used before definitionAn rvalue is used that may not be initialized to a value on some executionpath. (Use -usedef to inhibit warning)usedef.c:9:18: Passed storage x not completely defined (*x is undefined):getVal (x)Storage derivable from a parameter, return value or global is not defined.Use to denote passed or returned storage which need not be defined.(Use -compdef to inhibit warning)usedef.c:11:22: Passed storage x not completely defined (*x is undefined):mysteryVal (x)Finished checking --- 3 code warnings//錯誤原因: 由于程式中沒有對x進行定義,是以報未定義錯誤.但setVal()使用了 說明,是以在setVal(x);和return x;中,沒有報未定義錯誤.3.類型錯誤C語言中的資料類型較多,各個之間有些細微差别.splint也可以對變量類型進行檢查.示例1://bool.cint f (int i, char *s,bool b1, bool b2){if (i = 3) return b1;if (!i || s) return i;if (s) return 7;if (b1 == b2)return 3;return 2;}//END使用splint進行檢查:$ splint bool.cSplint 3.1.1 --- 28 Apr 2005bool.c: (in function f)bool.c:4:5: Test expression for if is assignment expression: i = 3The condition test is an assignment expression. Probably, you mean to use ==instead of =. If an assignment is intended, add an extra parentheses nesting(e.g., if ((a = b)) ...) to suppress this message. (Use -predassign toinhibit warning)// 錯誤原因: if語句中的條件表達式是一個指派語句.bool.c:4:5: Test expression for if not boolean, type int: i = 3Test expression type is not boolean or int. (Use -predboolint to inhibitwarning)// 錯誤原因: if語句中的條件表達式的傳回值,不是布爾型,而是整型.bool.c:4:8: Return value type bool does not match declared type int: b1Types are incompatible. (Use -type to inhibit warning)// 錯誤原因: 傳回值是布爾型,而不是整型.bool.c:5:6: Operand of ! is non-boolean (int): !iThe operand of a boolean operator is not a boolean. Use +ptrnegate to allow !to be used on pointers. (Use -boolops to inhibit warning)// 錯誤原因: "!"操作符的操作數不是布爾型,而是整型i.bool.c:5:11: Right operand of || is non-boolean (char *): !i || s// 錯誤原因: "||"操作符的右操作數不是布爾型,而是字元指針.bool.c:7:5: Use of == with boolean variables (risks inconsistency because ofmultiple true values): b1 == b2Two bool values are compared directly using a C primitive. This may produceunexpected results since all non-zero values are considered true, sodifferent true values may not be equal. The file bool.h (included insplint/lib) provides bool_equal for safe bool comparisons. (Use -boolcompareto inhibit warning)// 錯誤原因: 使用"=="對兩個布爾型進行比較.應該使用"&&".Finished checking --- 6 code warnings示例2://malloc1.c#include#includeint main(void){char *some_mem;int size1=1048576;some_mem=(char *)malloc(size1);printf("Malloed 1M Memory!\n");free(some_mem);exit(EXIT_SUCCESS);}//END使用splint檢查malloc1.c$ splint malloc1.cSplint 3.1.1 --- 28 Apr 2005malloc1.c: (in function main)malloc1.c:9:25: Function malloc expects arg 1 to be size_t gets int: size1To allow arbitrary integral types to match any integral type, use+matchanyintegral.Finished checking --- 1 code warning修改變量size1的定義為:size_t size1=1048576;再使用splint進行檢查.$ splint malloc1.cSplint 3.1.1 --- 28 Apr 2005Finished checking --- no warnings沒有檢查到錯誤.4.記憶體檢查緩沖區溢出錯誤是一種非常危險的C語言錯誤,大部分安全漏洞都與它有關.splint可以對緩沖區的使用進行檢查.報告溢出或越界錯誤.示例1://over.cint main(){int buf[10];buf[10] = 3;retrun 0;}//END使用splint進行檢查$ splint over.c +bounds +showconstraintlocationSplint 3.1.1 --- 21 Apr 2006Command Line: Setting +showconstraintlocation redundant with current valueover.c: (in function main)over.c:6:3: Likely out-of-bounds store:buf[10]Unable to resolve constraint:requires 9 >= 10needed to satisfy precondition:requires maxSet(buf @ over.c:6:3) >= 10A memory write may write to an address beyond the allocated buffer. (Use-likely-boundswrite to inhibit warning)Finished checking --- 1 code warning數組buf的大小是10位元組.最大可使用的元素位置為buf[9],但程式中使用了buf[10].是以報錯.示例2:// bound.cvoid updateEnv(char *str){char *tmp;tmp = getenv("MYENV");if(tmp != NULL) strcpy(str,tmp);}void updateEnvSafe(char *str, size_t strSize){char *tmp;tmp = getenv("MYENV");if(tmp != NULL){strncpy(str, tmp, strSize -1);str[strSize - 1] = '/0';}}//END$ splint bound.c +bounds +showconstraintlocationSplint 3.1.1 --- 21 Apr 2006Command Line: Setting +showconstraintlocation redundant with current valuebound.c: (in function updateEnv)bound.c:6:19: Possible out-of-bounds store:strcpy(str, tmp)Unable to resolve constraint:requires maxSet(str @ bound.c:6:26) >= maxRead(getenv("MYENV") @bound.c:5:9)needed to satisfy precondition:requires maxSet(str @ bound.c:6:26) >= maxRead(tmp @ bound.c:6:30)derived from strcpy precondition: requires maxSet() >=maxRead()A memory write may write to an address beyond the allocated buffer. (Use-boundswrite to inhibit warning)錯誤原因: 由于使用strcpy函數時,沒有指定複制字元串的長度,是以可能導緻緩沖區溢出.後面的updateEnvSafe函數,使用了strncpy進行字元串複制,避免了這種情況.到底需不需要編譯器之外的獨立的靜态代碼檢查工具呢?這個問題'仁者見仁,智者見智'。但是有一個結論我想大 家都會認可,那就是越是在開發周期早期發現的Bug,修複它所付出的代價就越小。而像lint這樣的靜态代 碼檢查程式恰恰是讓Bug在早期階段'顯露原型'的絕佳工具,而追求'lint- clean'[注1]境界的 代碼也向來是專家級程式員的嗜好。别忘了在'C專家程式設計'一書中曾經提到Sun OS的核心一直是保持'lint-clean' 狀态的,這就是榜樣!還等什麼?趕快學呀!^_^有人抱怨'不敢用lint工具, 太多的Warnings把快螢幕都淹沒了!',不過高手一般不這麼想,他會細 心琢磨這些Warnings背後的'暗示',并和lint工具溝通,利用 lint工具提供的互動方 法屏蔽掉一些經過分析認為不能成為錯誤的Warnings。久而久之,高手本身就成了一個lint程式, 就能夠很快的用肉眼發現代碼中的問題,并指出問題所在,如何解決!他還能告知如何嵌入一些Annotations 進而避免讓lint程式産生不必要的Warnings,這時這位高手對語言和程式的了解就又提高了一 個檔次了。其實使用ling工具不僅僅是為了提早發現程式中的Bug,其使用過程有助于你加深對程式的認識 和了解。的确事實就是這樣。Splint就是一款強大而且應用廣泛的開源lint工具。它的強大的代碼檢查能力固然讓人稱道,但是讓我更 欣賞的卻是它提供的'Annotations'機制。 Splint可以讓程式員在自己的代碼中嵌入相應的 Anotations,這些Anotations作為Splint分析代碼時的輸入以幫助Splint 産生對程式員更有用的資訊。下面是一些Splint的使用入門,更多詳細資訊請檢視'Splint manual'。1、最簡單的Splint使用方法>> splint *.c2、Splint輸出Warnings的基本格式:[,]: message[hint]:,: extra location information, if appropriate我們可以使用'+/-'來自定義其輸出格式,如'splint -showcol *c',則Splint不會在輸出資訊中顯示'列'資訊。3、使用flags控制splint的檢查範圍和輸出格式'+' -- 表明某個flag處于打開狀态,如'+unixlib';'-' -- 表明某個flag處于關閉狀态,如'-weak';4、使用.splintrc環境檔案如果不想每次使用splint的時候都手工輸入一堆'+/-',那麼你可以把這些 '+/-'預先寫到.splintrc檔案中,當splint執行的時候它會自動加 上這些flags的。預設的flags設定在'~/splintrc'檔案中,但是如果一旦splint的 目前工作路徑下也有.splintrc檔案,那麼這個.splintrc檔案中的flag設定會覆寫'~/splintrc'中 的 flags設定,但是指令行中的flags設定是具備最高優先級的,它會覆寫前面提到的任何一個檔案中的flags 設定。5、使用Annotations對于'Annotations'的作用,Java程式員并不陌生,但是C程式員則對這個不是那麼了解。C代碼 中的Annotations用來指導Splint生成恰當的代碼檢查報告。下面這個例子對比使用和不使用Annotations,Splint 的輸出的差别:void foo1() {int *p = NULL;}void foo2() {int *p = NULL;}splint testlint.cSplint 3.1.1 --- 28 Apr 2003testlint.c: (in function foo2)testlint.c:6:7: Variable p declared but not usedA variable is declared but never used. Use in front ofdeclaration to suppress message. (Use -varuse to inhibit warning)Finished checking --- 1 code warning可以看出沒使用Annotation的函數foo2被給出Warning了。Splint的Annotations 繁多,我們在平時做lint時可以多多接觸。'早用lint,勤用lint',這是C專家給我們的建議。'lint-clean'也許離你并不遙遠。[注1]'lint-clean' -- 程式能夠順利通過lint程式的檢查。splintsplint -I /usr/local/mysql/include/mysql/ +gnuextensions +trytorecover *.c