1. 程式人生 > >軟體測試程式碼靜態分析(splint)

軟體測試程式碼靜態分析(splint)

轉載

http://www.cnblogs.com/bangerlee/archive/2011/09/07/2166593.html

程式碼靜態分析工具——splint的學習與使用

引言

最近在專案中使用了靜態程式分析工具PC-Lint,體會到它在專案實施中帶給開發人員的方便。PC-Lint是一款針對C/C++語言、windows平臺的靜態分析工具,FlexeLint是針對其他平臺的PC-Lint版本。由於PC-Lint/FlexeLint是商業的程式分析工具,不便於大家對其進行學習和使用,因而下面我將介紹一個針對C語言的開源程式靜態分析工具——splint。

靜態程式分析

先來說說什麼是“靜態程式分析(Static program analysis)”,靜態程式分析是指使用自動化工具軟體對程式原始碼進行檢查,以分析程式行為的技術,應用於程式的正確性檢查、安全缺陷檢測、程式優化等。它的特點就是不執行程式,相反,通過在真實或模擬環境中執行程式進行分析的方法稱為“動態程式分析(Dynamic program analysis)”。

那在什麼情況下需要進行靜態程式分析呢?靜態程式分析往往作為一個多人蔘與的專案中程式碼審查過程的一個階段,因編寫完一部分程式碼之後就可以進行靜態分析,分析過程不需要執行整個程式,這有助於在專案早期發現以下問題:變數聲明瞭但未使用、變數型別不匹配、變數在使用前未定義、不可達程式碼、死迴圈、陣列越界、記憶體洩漏等。下圖說明了靜態程式分析在進行專案編碼過程中所處的位置:



從上圖可以知道,靜態分析工具在程式碼通過編譯之後再對程式碼進行分析。我們會問:靜態分析工具與編譯器相比,所做的工作有什麼不同?靜態分析工具相比編譯器,對程式碼進行了更加嚴格的檢查,像陣列越界訪問、記憶體洩漏、使用不當的型別轉換等問題,都可以通過靜態分析工具檢查出來,我們甚至可以在分析工具的分析標準裡定義程式碼的編寫規範,在檢測到不符合編寫規範的程式碼時丟擲告警,這些功能都是編譯器沒有的。



既然靜態分析工具發揮了不小的作用,何不在編譯器裡兼備靜態分析的功能?對於這個問題,S. C. Johnson(他是最古老的靜態分析工具Lint的作者)在其1978年發表的論文《Lint, a C Program Checker》中給出了他的答案:“Lint與C編譯器在功能上的分離既有歷史原因,也有現實的意義。編譯器負責把C源程式快速、高效地轉變為可執行檔案,不對程式碼做型別檢查(特別是對分別編譯的程式),有益於做到快速與高效。而Lint沒有“高效”的要求,可以花更多時間對程式碼進行更深入、仔細的檢查。”



針對空指標提取、未定義變數使用、型別轉換、記憶體管理、函式介面定義等,我們可以在靜態分析工具裡制定不同的檢測標準,以下曲線圖說明了在使用splint進行分析時,檢測標準與splint執行的開銷所對應的關係,從另一個角度看,也說明了靜態分析工具與編譯器的關係:



splint

掌握了“靜態分析”等概念之後,我們再來看splint。

在Linux命令列下,splint的使用很簡單,檢測檔案*.c,只要這樣使用就可以了:
splint *.c

1.splint訊息

我們通過以下例子來認識典型的splint告警資訊:

1 //splint_msg.c
2 int func_splint_msg1(void)
3 {
4 int a;
5 return0;
6 }
7 int func_splint_msg2(void)
8 {
9 int* a = (int*)malloc(sizeof(int));
10 a = NULL;
11 return0;
12 }


執行splint splint_msg.c之後,我們來看輸出的告警資訊:

splint_msg.c: (in function func_splint_msg1)
splint_msg.c:4:6: Variable a declared but not used
A variable is declared but never used. Use /*@

[email protected]*/ in front of
declaration to suppress message. (Use -varuse to inhibit warning)

splint_msg.c: (in function func_splint_msg2)
splint_msg.c:10:2: Fresh storage a (type int *) not released before assignment:
a = NULL
A memory leak has been detected. Storage allocated locally is not released
before the last reference to it is lost. (Use -mustfreefresh to inhibit
warning)
splint_msg.c:9:37: Fresh storage a created

Finished checking --- 2 code warnings


藍色字型部分:給出告警所在函式名,在函式的第一個警告訊息報告前列印;

紅色字型部分:訊息的正文,檔名、行號、列號顯示在的警告的正文前;

黑色字型部分:是有關該可疑錯誤的詳細資訊,包含一些怎樣去掉這個訊息的資訊;

綠色字型部分:給出格外的位置資訊,這裡訊息給出了是在哪裡申請了這個可能洩露的記憶體。

2.檢查控制

splint提供了三種方式可進行檢查的控制,分別是.splintrc配置檔案、flags標誌和格式化註釋。

flags:splint支援幾百個標誌用來控制檢查和訊息報告,使用時標誌前加’+‘或’-’,'+'標誌開啟這個標誌,'-'表示關閉此標誌,下面例子展示了flags標誌的用法:
splint -showcol a.c //在檢測a.c時,告警訊息中列數不被列印
splint -varuse a.c //在檢測a.c時,告警訊息中未使用變數告警不被列印

.splintrc配置檔案:在使用原始碼安裝splint之後,.splintrc檔案將被安裝在主目錄下,.splintrc檔案中對一些標誌作了預設的設定,命令列中指定的flags標誌會覆蓋.splintrc檔案中的標誌。

格式化註釋:格式化註釋提供一個型別、變數或函式的格外的資訊,可以控制標誌設定,增加檢查效果,所有格式化註釋都以/*@開始,@*/結束,比如在函式引數前加/*@
[email protected]
*/,表示該引數可能是NULL,做檢測時,splint會加強對該引數的值的檢測。

3.檢測分析內容

1.解引用空指標(Null Dereferences)

在Unix作業系統中,解引用空指標將導致我們在程式執行時產生段錯誤(Segmentation fault),一個簡單的解引用空指標例子如下:

1 //null_dereferences.c
2 int func_null_dereferences(void)
3 {
4 int* a = NULL;
5 return*a;
6 }


執行splint null_dereference.c命令,將產生以下告警訊息:

null_dereference.c: (in function func_null_dereferences)
null_dereference.c:5:10: Dereference of null pointer a: *a
A possibly null pointer is dereferenced. Value is either the result of a
function which may return null (in which case, code should check it is not
null), or a global, parameter or structure field declared with the null
qualifier. (Use -nullderef to inhibit warning)
null_dereference.c:4:11: Storage a becomes null

Finished checking --- 1 code warnin


2.型別(Types)

我們在程式設計中經常用到強制型別轉換,將有符號值轉換為無符號值、大範圍型別值賦值給小範圍型別,程式執行的結果會出無我們的預料。

1 //types.c
2 void splint_types(void)
3 {
4 short a =0;
5 long b =32768;
6 a = b;
7 return;
8 }


執行splint types.c命令,將產生以下告警訊息:
types.c: (in function splint_types)
types.c:6:2: Assignment of longint to shortint: a = b
To ignore type qualifiers in type comparisons use +ignorequals.

Finished checking ---1 code warning

3.記憶體管理(Memory Management)

C語言程式中,將近半數的bug歸功於記憶體管理問題,關乎記憶體的bug難以發現並且會給程式帶來致命的破壞。由記憶體釋放所產生的問題,我們可以將其分為兩種:
當尚有其他指標引用的時候,釋放一塊空間

1 //memory_management1.c
2 void memory_management1(void)
3 {
4 int* a = (int*)malloc(sizeof(int));
5 int* b = a;
6 free(a);
7 *b =0;
8 return;
9 }


在上面這個例子中,指標a與b指向同一塊記憶體,但在記憶體釋放之後仍對b指向的內容進行賦值操作,我們來看splint
memory_management1.c的結果:

memory_management1.c: (in function memory_management1)
memory_management1.c:7:3: Variable b used after being released
Memory is used after it has been released (either by passing as an only param
or assigning to an only global). (Use -usereleased to inhibit warning)
memory_management1.c:6:7: Storage b released
memory_management1.c:7:3: Dereference of possibly null pointer b: *b
A possibly null pointer is dereferenced. Value is either the result of a
function which may returnnull (in which case, code should check it is not
null), or a global, parameter or structure field declared with the null
qualifier. (Use -nullderef to inhibit warning)
memory_management1.c:5:11: Storage b may become null

Finished checking ---2 code warnings


檢查結果中包含了兩個告警,第一個指出我們使用了b指標,而它所指向的記憶體已被釋放;第二個是對解引用空指標的告警。
當最後一個指標引用丟失的時候,其指向的空間尚未釋放

1 //memory_management2.c
2 void memory_management2(void)
3 {
4 int* a = (int*)malloc(sizeof(int));
5 a = NULL;
6 return;
7 }


這個例子中記憶體尚未釋放,就將指向它的唯一指標賦值為NULL,我們來看splint memory_management2.c的檢測結果:

memory_management2.c: (in function memory_management2)
memory_management2.c:5:2: Fresh storage a (type int*) not released before assignment:
a = NULL
A memory leak has been detected. Storage allocated locally is not released
before the last reference to it is lost. (Use -mustfreefresh to inhibit
warning)
memory_management2.c:4:37: Fresh storage a created

Finished checking ---1 code warning


splint丟擲一個告警:型別為int*的a在進行a = NULL賦值前沒有釋放新分配的空間。

4.快取邊界(Buffer Sizes)

splint會對陣列邊界、字串邊界作檢測,使用時需要加上+bounds的標誌,我們來看下面的例子:

1 //bounds1.c
2 void bounds1(void)
3 {
4 int a[10];
5 a[10] =0;
6 return;
7 }


使用splint +bounds bounds1.c命令對其進行檢測,結果如下:

bounds1.c: (in function bounds1)
bounds1.c:5:2: Likely out-of-bounds store: a[10]
Unable to resolve constraint:
requires 9>=10
needed to satisfy precondition:
requires maxSet(a @ bounds1.c:5:2) >=10
A memory write may write to an address beyond the allocated buffer. (Use
-likelyboundswrite to inhibit warning)

Finished checking ---1 code warning


告警訊息提示陣列越界,訪問超出我們申請的buffer大小範圍。再看一個例子:

1 //bounds2.c
2 void bounds2(char* str)
3 {
4 char* tmp = getenv("HOME");
5 if(tmp != NULL)
6 {
7 strcpy(str, tmp);
8 }
9 return;
10 }


不對這個例子進行詳細檢查,可能我們不能發現其中隱含的問題,執行splint +bounds bounds2.c之後,會丟擲如下告警:

bounds2.c: (in function bounds2)
bounds2.c:7:3: Possible out-of-bounds store: strcpy(str, tmp)
Unable to resolve constraint:
requires maxSet(str @ bounds2.c:7:10) >= maxRead(getenv("HOME") @
bounds2.c:4:14)
needed to satisfy precondition:
requires maxSet(str @ bounds2.c:7:10) >= maxRead(tmp @ bounds2.c:7:15)
derived from strcpy precondition: requires maxSet(<parameter 1>) >=
maxRead(<parameter 2>)
A memory write may write to an address beyond the allocated buffer. (Use
-boundswrite to inhibit warning)

Finished checking ---1 code warning


告警訊息提示我們:在使用strcpy(str, tmp)進行字串複製時,可能出現越界錯誤,因為str的大小可能不足以容納環境變數“HOME”對應的字串。綠色字型的內容指示瞭如何消除告警訊息。



小結

這裡僅給出了splint檢查的4種檢測:解引用空指標、型別、記憶體管理、快取邊界,除此之外,splint還對巨集(Macros)、函式介面(Function Interfaces)、控制流(Control Flow)等內容作檢測,很多檢測標誌和格式化註釋都未在本文中提到,更詳細的內容請檢視splint使用手冊。



不管pc-lint、splint等靜態程式分析工具的功能多麼強大,它們對程式的檢查也有疏漏的地方,工具的使用並不能提高我們的程式設計能力,我們更應該通過它們學習各種編碼錯誤和程式碼隱患,憑積累的編碼知識把程式隱患扼殺在搖籃裡。



你在專案實施中是否遇到過隱藏的bug,導致返工呢?在你的專案中是否使用了靜態程式分析工具,它起到多大的作用?說出來與大家一塊分享吧~

相關推薦

軟體測試程式碼靜態分析(splint

轉載 http://www.cnblogs.com/bangerlee/archive/2011/09/07/2166593.html 程式碼靜態分析工具——splint的學習與使用 引言 最近在專案中使用了靜態程式分析工具PC-Lint,體會到它在專案實施中帶給開發人員

軟體測試基礎知識(摘

一、軟體測試基本概念   1.軟體=程式+文件,軟體測試=程式測試+測試文件。   “軟體”是指能夠實現某種功能的指令集合,“文件”是指軟體在開發、使用和維護過程中產生的圖文集合。   2.軟體分類     按功能分:系統軟體、應用軟體     按技術架構分:單機版軟體、C/S結構軟體、B/S結構軟

高效程式碼靜態分析 Scientific Toolworks Understand

原文地址:https://www.luochenzhimu.com/archives/4920.html 原始碼閱讀工具(Scientific Toolworks Understand)的特色 1、支援多語言:Ada, C, C++, C#, Java, FORTRAN, Delphi, J

《Python程式設計從入門到實踐》記錄之測試程式碼(unitttest模組

unittest模組提供了測試程式碼工具。 單元測試:用於核實函式的某個方面沒有問題 測試用例:一組單元測試,這些單元測試一起核實函式在各種情形下的行為都符合要求。 函式編寫測試用例時,要匯入模組unittest和要測試的函式,再建立一個繼承unittest.TestCase的類,並

軟體測試學習教程(一-學習路線圖

寫這些,一些是個人總結與學習,一些是彙總他人經驗與智慧,他山之石可以攻玉,無論如何,知識共享才是最終目的,希望可以為想入軟體測試行業的小白明確學習的方向,也可以幫助工作多年的中高階工程師能查漏補缺,更歡迎能對文章體現的不當之處予以糾正,對不足之處加以補充,為後來者開闢道路,指

軟體測試學習教程(三-測試理論

今天一起學習軟體測試理論:軟體測試目的,軟體測試定義,軟體測試原則,軟體測試分類,軟體測試方法,測試基本流程。 軟體測試定義:軟體測試(英語:software testing),描述一種用來促進鑑定軟體的正確性、完整性、安全性和質量的過程。換句話說,軟體測試是一種實際輸出與

軟體測試基礎知識(1

軟體測試: 從執行狀態來看分為:動態測試、靜態測試; 從按執行過程來看分為:手工測試、自動化測試; 從內容劃分來看:功能測試、易用性測試、相容性測試、文件測試等, 從用例設計來看:黑盒測試、白盒測試、灰盒測試; 按開發:單元測試、系統測試、整合測試、驗

ET·ci — 全自動軟體測試排程(持續整合平臺

• 概述 ET·ci 提供了業界領先的編譯 - 測試 - 釋出解決方案,包括:自動提取配置庫程式碼進行自動構建 , 自動排程靜態測試工具(如 QAC)進行靜態測試,自動排程單元測試工具(如 Tessy)開展動態測試,自動排程 HIL 自動化測試系統等。使得開發、

程式碼靜態分析--試用SpecChecker

將整個程式碼工程檔案匯入SpecChecker後,其提供如下分析功能: 安全規則檢查。依據制定的程式設計規則(MISRA C、企業自定程式設計規範等)對程式碼進行規則檢查。違背規則的語句會在結果中給出,並指出違背的規則項。 軟體質量度量。軟體質量度量的分析依賴

軟體測試的藝術(一軟體測試的重要原則

軟體測試是為了發現錯誤而執行程式的過程,這就說明目的是為了發現錯誤,動作是執行程式。 在進行軟體測試的過程中,要遵循很多的原則,以下是幾個重要的測試原則: 1)      測試用例中一個必需部分是對預期輸出或結果進行定義。          對於軟體測試,必須是有明確的定義輸入資料和條件,同時對輸出結果有準確

軟體測試之bug分析定位技巧

檢視壓力——tail -f as.log | grep '^NOTICE' | awk '{print $3}' | uniq -c 排除日誌中的特定內容——grep -v 'pattern' as.log 只輸出感興趣的內容——grep -o 'proctime:toal:\d+' as.log

軟體測試面試題(2

12.您是否瞭解以往所工作的企業的軟體開發過程?如果瞭解,請試述一個完整的開發過程需要完成哪些工作?分別由哪些不同的角色來完成這些工作?您在以往的測試工作中都曾經具體從事過哪些工作?其中最擅長哪部分工作? 開發過程---需求調研(需求人員)、需求分析(需求人員)、概要設計(

軟體測試經典測試題(4

在您以往的工作中,一條軟體缺陷(或者叫Bug)記錄都包含了哪些內容?如何提交高質量的軟體缺陷(Bug)記錄? 一條Bug記錄最基本應包含: bug編號; bug嚴重級別,優先順序; bug產生的模組; 首先要有bug摘要,闡述bug大體的內容; bug對應的版本; bug

軟體測試理論基礎(二

軟體測試的過程管理一.軟體測試的各個過程(PDCA)1.測試需求的分析和確定  2.測試計劃  3.測試設計  4.測試執行 5.測試記錄和缺陷跟蹤 6.迴歸測試 7.測試總結和報告二.測試需求1.需求規格說明書的檢查要點      正確性:對照原始需求檢查需求規格說明書  

軟體測試基本方法(七之驗收測試

驗收測試是在功能測試和系統測試之後進行的,所以驗收測試的前提條件是系統或軟體產品已通過了內部測試。然後和使用者一起驗收軟體,在真實環境下執行軟體,看是否存在與使用者需求不一致的問題或違背產品規格書的要

軟體測試學習筆記(二軟體測試基本技術

一、簡介 任何工程產品都可以使用白盒測試和黑盒測試兩種方法之一進行測試。 1.1 黑盒測試 黑盒測試:已知產品的功能設計規格和使用者手冊,可以進行測試證明每個功能是否實現、每個實現了的功能是否符合要求,以及產品的效能是否滿足使用者的要求。   軟體的黑盒測試意味著測試要

軟體測試基本方法(六之整合測試和系統測試

在軟體開發中,經常會遇到這樣的情況,單元測試時確認每個模組都能單獨工作,但這些模組整合在一起之後會出現有些模組不能正常工作。例如,在chrome環境下用js寫了一個實時捕捉video中特定區域的模組,

有關軟體測試的問答(轉

11、黑盒測試有前途嗎?   專家分析:對於黑盒測試來說其實研究透了,你也非常厲害,像自動化測試、效能測試它們有時候還是需要有些功能測試的功底的。 12、在測試資源不足的前提下, 是否可以用粗顆粒度的用例和測試人員跑場景流程過程中的經驗來取代大量的用例呢?例如: 用例只有一個,但是跑的過程中利用測試者的觀

python 介面自動化測試--程式碼實現(八

用例讀入資料庫: #! /usr/bin/python # coding:utf-8 import sys,os from Engine import DataEngine reload(sys) sys.setdefaultencoding( "utf-8") fro

軟體測試行業趨勢分析和思考

4.1   軟體測試的概述 軟體測試(英語:software testing),描述一種用來促進鑑定軟體的正確性、完整性、安全性和質量的過程。 換句話說,軟體測試是一種實際輸出與預期輸出間的稽核或者比較過程[4]。 不管是傳統企業還是IT企業,都會有這樣的一崗位,叫QC(QUALITY CONTROL