深度:規(guī)范的嵌入式項目要在設(shè)計時考慮缺陷
軟件測試是有效發(fā)現(xiàn)軟件缺陷的重要方法之一,嵌入式軟件測試按照是否執(zhí)行被測系統(tǒng),將測試類型分為靜態(tài)測試和動態(tài)測試:
- 靜態(tài)測試不需要編譯執(zhí)行源程序,對源程序進(jìn)行詞法語法、編程規(guī)范、數(shù)據(jù)流、控制流、度量等分析,獲取程序的結(jié)構(gòu)和特性,利用形式化方法驗證、證明程序是否符合安全規(guī)則,能夠較為全面的獲取程序的特征。
- 動態(tài)測試需要通過獲取程序的動態(tài)信息來分析軟件的缺陷,如分析程序的內(nèi)存狀態(tài)、覆蓋率和執(zhí)行結(jié)果,更有利于理解程序的動態(tài)行為特征。
很多代碼缺陷產(chǎn)生于程序運(yùn)行過程中,具有隱蔽性和不可預(yù)見性,比如數(shù)組越界,動態(tài)存儲分配,內(nèi)存溢出,指針非法引用,類型不一致的隱式轉(zhuǎn)換等,這些錯誤都是在編譯階段不能被編譯器檢查發(fā)現(xiàn)的錯誤。在大規(guī)模,復(fù)雜度極高的軟件中,完全依賴于人工檢查方式可能會因為人為等主觀原因造成問題被遺漏無法發(fā)現(xiàn)。將現(xiàn)有常見多發(fā)問題形成缺陷模式庫植入工具中,由工具自動化檢測代碼缺陷,能夠高效提升測試效率,避免問題重復(fù)發(fā)生,減輕測試人員工作量。
缺陷模式規(guī)則分類
2.1 規(guī)則分類2.1.1?? 按照錯誤類別分類為了進(jìn)一步研究軟件錯誤發(fā)生的機(jī)理,分布情況,針對C語言典型缺陷模式庫中的規(guī)則,按照規(guī)則檢查類型對其進(jìn)行分類。(1)? 數(shù)值檢查:除零錯誤、數(shù)組越界。(2)? 指針使用檢查:空指針解引用。(3)? 數(shù)據(jù)流檢查:變量定義未使用就再次賦值。(4)? 控制流檢查:if elseif分支語句末尾缺少else分支。(5)? 初始化檢查:使用前未初始化變量。(6)? 類型轉(zhuǎn)換檢查:數(shù)據(jù)類型不一致引入的隱式類型轉(zhuǎn)換。(7)? 操作符使用不當(dāng)檢查:關(guān)系表達(dá)式、邏輯表達(dá)式、條件判斷語句中的控制表達(dá)式禁止為賦值表達(dá)式“=”、使用連續(xù)的比較運(yùn)算,可能導(dǎo)致代碼執(zhí)行邏輯與預(yù)期不符。
2.1.2?? 按照嚴(yán)重程度分類根據(jù)故障出現(xiàn)可能引起的錯誤嚴(yán)重程度,對軟件或者系統(tǒng)造成影響的嚴(yán)重程度,給每條規(guī)則定義了優(yōu)先級高、中、低的屬性。(1)? 重要:除零錯誤;數(shù)組越界;空指針解引用。(2)? 中等:使用前未初始化變量;數(shù)據(jù)類型不一致引入的隱式類型轉(zhuǎn)換;關(guān)系表達(dá)式、邏輯表達(dá)式、條件判斷語句中的控制表達(dá)式禁止為賦值表達(dá)式“=”;使用連續(xù)的比較運(yùn)算,可能導(dǎo)致代碼執(zhí)行邏輯與預(yù)期不符。(3)? 輕微:if elseif分支語句末尾缺少else分支;變量定義未使用就再次賦值。
2.2 規(guī)則解析2.2.1?? 數(shù)值檢查錯誤發(fā)生在不斷運(yùn)行過程中,稱為運(yùn)行時錯誤,一旦程序中出現(xiàn)該類型錯誤,會導(dǎo)致計算得到的實際結(jié)果與預(yù)期結(jié)果完成不同,甚至引發(fā)計算機(jī)復(fù)位。數(shù)值檢查作為運(yùn)行時錯誤檢查的重要檢查內(nèi)容,會出現(xiàn)在某些特定的運(yùn)行條件下,即便是經(jīng)過嚴(yán)格測試的程序,仍有可能存在非預(yù)期的淺通路,引發(fā)軟件不安全問題。
(1)? 數(shù)組越界通過數(shù)組的下標(biāo)來得到數(shù)組內(nèi)指定索引的元素,稱作對數(shù)組的訪問。如果一個數(shù)組定義為n個元素,它占用一塊連續(xù)的內(nèi)存空間,對n個元素(下標(biāo)為0到n-1)的訪問都合法,如果對這n個之外的元素(如下標(biāo)n)進(jìn)行訪問,訪問到的是其它變量,是非法的,稱為“越界”。數(shù)組越界在運(yùn)行時的表現(xiàn)是不確定的,可能不會造成嚴(yán)重后果,也有可能導(dǎo)致程序崩潰。因此在使用數(shù)組時一定要檢查訪問是否越界,以保證程序的正確性。代碼示例見表1。
表1 ?數(shù)組越界代碼示例
(2)? 除零錯誤出現(xiàn)除零操作時會導(dǎo)致計算結(jié)果為一個極大值,超過數(shù)據(jù)類型能夠表示的最大范圍,就會發(fā)生溢出,計算機(jī)程序?qū)τ谝绯龅姆雷o(hù)處理可能是計算機(jī)復(fù)位。因此代碼中將整數(shù)和浮點(diǎn)數(shù)作為分母時都應(yīng)該進(jìn)行保護(hù),防止除零后數(shù)據(jù)溢出的異常情況發(fā)生。代碼示例見表2。
表2 ?除零錯誤代碼示例
2.2.2? ?空指針解引用空指針解引用是一種常見的動態(tài)內(nèi)存錯誤。指針變量可以指向堆地址、靜態(tài)變量和空地址單元,當(dāng)引用指向空地址單元的指針變量時,就會產(chǎn)生空指針引用故障,導(dǎo)致不可預(yù)見的錯誤,系統(tǒng)崩潰或者異常復(fù)位。因此,在解引用指針前,應(yīng)先判斷是否為NULL,如果是NULL則不要解引用。代碼示例見表3。
表3? 空指針解引用代碼示例
2.2.3? ?變量定義未使用就再次賦值這條規(guī)則屬于數(shù)據(jù)流分析規(guī)則,主要關(guān)注變量值的操作邏輯是否合理正確,如果不符合邏輯,就有可能隱藏代碼問題。代碼示例見表4。
表4 ?變量定義未使用就再次賦值代碼示例表
2.2.4? ?if elseif分支語句末尾缺少else分支這條規(guī)則屬于控制流分析規(guī)則,主要關(guān)注程序的結(jié)構(gòu)。需要在代碼解析的基礎(chǔ)上提取程序的控制流信息,程序的控制流用于決定分配給變量的特定值可能傳播到程序的哪些部分。如果存在某條路徑中變量值與預(yù)期不一致,就有可能隱藏代碼問題。代碼示例見表5。
表5 ?if elseif分支語句末尾缺少else分支代碼示例表
2.2.5? ?使用未初始化變量程序在執(zhí)行過程中變量位于內(nèi)存中,內(nèi)存中供用戶使用的存儲空間分為三部分:程序區(qū)、靜態(tài)存儲區(qū)、動態(tài)存儲區(qū)。全局變量全部存儲在靜態(tài)存儲區(qū);動態(tài)存儲區(qū)主要存放:函數(shù)的形參、自動變量(沒有加static的局部變量)、函數(shù)調(diào)用的現(xiàn)場保護(hù)和返回值。在使用動態(tài)存儲區(qū)變量前如果未對其賦初值就直接使用,由于變量值不確定,可能會發(fā)生無法預(yù)知的錯誤。代碼示例見表6。
表6 ?使用未初始化變量代碼示例表
2.2.6?? 數(shù)據(jù)類型不一致引入的隱式類型轉(zhuǎn)換當(dāng)賦值變量與被賦值變量類型不相同時,需要進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換的相關(guān)檢查規(guī)則。一般情況下,數(shù)據(jù)的類型的轉(zhuǎn)換通常是由編譯系統(tǒng)自動進(jìn)行的,不需要人工干預(yù),這種類型轉(zhuǎn)換稱為隱式類型轉(zhuǎn)換。但如果程序要求一定要將某一類型的數(shù)據(jù)轉(zhuǎn)換為另外一種類型,則可以利用強(qiáng)制類型轉(zhuǎn)換運(yùn)算符進(jìn)行轉(zhuǎn)換,這種強(qiáng)制轉(zhuǎn)換過程稱為顯式轉(zhuǎn)換。圖1? 編譯器自動提升規(guī)則C語言編譯器數(shù)據(jù)類型提升規(guī)則可以歸納為從長度小的數(shù)據(jù)類型向長度大的數(shù)據(jù)類型提升,稱為向上轉(zhuǎn)換;反之,稱為向下轉(zhuǎn)換。編譯器自動轉(zhuǎn)換規(guī)則見圖1所示。
如果出現(xiàn)向上隱式類型轉(zhuǎn)換,兩個數(shù)據(jù)進(jìn)行加法運(yùn)算時,小數(shù)對應(yīng)到大數(shù)有效位數(shù)超出有效數(shù)據(jù)位數(shù),會導(dǎo)致小數(shù)無法加到大數(shù)上,造成計算結(jié)果存在精度損失。
如果出現(xiàn)向下隱式類型轉(zhuǎn)換,大范圍變量的數(shù)據(jù)值超出了小范圍變量的能夠表示的有效范圍,編譯器會根據(jù)數(shù)據(jù)值在內(nèi)存中的表示形式經(jīng)過轉(zhuǎn)換后,將高位數(shù)據(jù)截斷丟棄造成轉(zhuǎn)換結(jié)果錯誤,因此編譯器不允許隱式向下轉(zhuǎn)換,如果必須要這么做,需要使用顯式的方法,使用顯式的方法也會存在問題,比如兩個數(shù)據(jù)相加得到的計算結(jié)果可能出現(xiàn)向上舍入或者向下舍入的情況。
例如,將高精度double類型變量轉(zhuǎn)換成float類型變量需要增加強(qiáng)制類型轉(zhuǎn)換。double型向float型轉(zhuǎn)換為向下轉(zhuǎn)換,C語言編譯器無法自動執(zhí)行向下轉(zhuǎn)換的操作,因此需要采用顯式類型轉(zhuǎn)換的方式,將double類型變量顯式轉(zhuǎn)換為float類型變量。代碼示例見表7。
表7 ?類型轉(zhuǎn)換代碼示例表
2.2.7? ?操作符使用不當(dāng)(1)? 關(guān)系表達(dá)式、邏輯表達(dá)式、條件判斷語句中的控制表達(dá)式禁止為賦值表達(dá)式“=”。在上述表達(dá)式中如果使用賦值號會導(dǎo)致條件判斷結(jié)果始終為真,代碼執(zhí)行邏輯與預(yù)期要求不符。代碼示例見表8。
表8 ?某些表達(dá)式中禁止使用賦值表達(dá)式代碼示例表(2)? 使用連續(xù)的比較運(yùn)算,可能導(dǎo)致代碼執(zhí)行邏輯與預(yù)期不符。比如連續(xù)的比較操作為:1
表9 ?使用連續(xù)比較運(yùn)算的代碼示例表
代碼抽樣及結(jié)果分析采用代碼抽樣的方法驗證和分析靜態(tài)分析工具SpecChecker對缺陷模式庫中規(guī)則的檢查情況SpecChecker是一款具有檢查編程規(guī)范、故障缺陷分析和共享數(shù)據(jù)變量分析等功能的靜態(tài)分析工具。使用該工具的故障缺陷分析功能對隨機(jī)抽樣代碼進(jìn)行檢查,給出工具分析結(jié)果,對工具和規(guī)則提出建議,明確代碼設(shè)計時的注意事項。
3.1 抽樣代碼情況及指標(biāo)抽樣20w代碼嵌入式C代碼,處理器類型有C51、GCC、DSP,選擇以下幾個指標(biāo)作為對工具的衡量指標(biāo),以工具發(fā)現(xiàn)問題為核心指標(biāo)。(1)? 工具提示:工具提示出違反某規(guī)則的總數(shù);(2)? 確認(rèn)為問題:工具提示違反規(guī)則位置,經(jīng)人工確認(rèn)確實存在程序問題;(3)? 工具誤報:工具提示違反規(guī)則位置,經(jīng)人工確認(rèn)不存在違反規(guī)則或者任何錯誤;(4)? 工具漏報:代碼違反規(guī)則要求,工具未給出提示。
3.2 工具分析結(jié)果表10? 缺陷模式檢查結(jié)果
3.3 建議根據(jù)表10結(jié)果,從幾個方面:漏報規(guī)則、誤報規(guī)則、定位準(zhǔn)確規(guī)則、提示性信息規(guī)則、代碼改進(jìn)規(guī)則、未檢出規(guī)則幾個方面給出改進(jìn)建議。
(1)? 漏報規(guī)則:工具能夠較好的發(fā)現(xiàn)整數(shù)為0做分母的“除零錯誤”,準(zhǔn)確率較高,但是未能檢出分母為浮點(diǎn)數(shù)0.0的情況。在需求分析、代碼設(shè)計階段應(yīng)考慮分母是否可能為零,如果可能為零,應(yīng)進(jìn)行除零保護(hù)。
(2)? 誤報規(guī)則:工具對于“變量賦值后未使用再次賦值”存在一定程度的誤報,該條規(guī)則給出了較多提示性內(nèi)容,但并未發(fā)現(xiàn)真正的程序問題。建議工具對于局部變量定義時賦初值后未使用再次賦值的情況不進(jìn)行提示。規(guī)則描述中也可更進(jìn)一步明確什么場景下的代碼邏輯是存在缺陷的。
(3)? 定位準(zhǔn)確規(guī)則:工具對于“數(shù)組越界”、“使用未初始化變量”、“關(guān)系表達(dá)式、邏輯表達(dá)式、條件判斷語句中的控制表達(dá)式禁止為賦值表達(dá)式“=””、“使用連續(xù)的比較運(yùn)算,可能導(dǎo)致代碼執(zhí)行邏輯與預(yù)期不符”,檢查結(jié)果較為準(zhǔn)確。
(4)? 提示性信息規(guī)則:“if elseif分支語句末尾缺少else分支”未發(fā)現(xiàn)出現(xiàn)實質(zhì)性問題,一般情況下需要結(jié)合代碼的處理邏輯分析是否存在問題。
(5)? 代碼改進(jìn)規(guī)則:“將double類型數(shù)據(jù)轉(zhuǎn)換為float類型”出現(xiàn)問題的大多數(shù)情況是存在一定程度的數(shù)據(jù)精度損失,會造成計算結(jié)果存在一定的誤差,建議代碼不進(jìn)行這類操作。
(6)? 未檢出規(guī)則:目前未見“空指針解引用”的錯誤發(fā)生,一旦發(fā)生后果比較嚴(yán)重可能造成計算機(jī)復(fù)位,開發(fā)和測試人員應(yīng)引起重視,避免代碼中存在指針未判斷是否為空就直接使用的情況出現(xiàn)。
END
來源:StrongerHuang版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系刪除。
▍