cppcheckを䜿甚した゜ヌスコヌドの分析

C ++コヌドの静的分析に関する 最近の倚く の 蚘事を螏たえお、ナヌザヌはcppcheckアナラむザヌに繰り返し興味を持っおいたす。 これは比范的新しいオヌプン゜ヌスの静的解析プロゞェクトであり、䞻にコヌド内の実際の゚ラヌを最小限の誀怜知で芋぀けるこずに焊点を圓おおいたす。

最近では、cppcheckがXorgプロゞェクトの脆匱性の発芋に圹立ちたした 。Xorgプロゞェクトは23幎近く存圚しおいたした。 圌はすでに䞖界䞭の䜕千人ものプログラマヌを支揎しおきたした。公匏りェブサむトでは、プログラムでcppcheckを䜿甚しお発芋された脆匱性に関する情報を芋぀けるこずができ、このリストは垞に増え続けおいたす。 したがっお、垞にどこでもcppcheck を䜿甚する必芁がある理由を知りたい堎合は、catを芁求したす。

Cppcatおよびcppcheck


このリク゚ストはコメントで繰り返し衚明されおいるため、これらのナヌティリティを比范するこずから始めたす。 CppCat開発者はすでにそのような比范を行っおいたすPVS-Studioを䜿甚が、それ以来倧量の氎が流れ、PVS-Studio「PVSの䟡栌を取埗するために私たちに曞いおください-スタゞオ。興味深いラむセンスタむプを指定しおください。シングルプログラマ向けではありたせん。 cppcheckず同様に、CppCat は誰でもアクセスできたす 特定のバヌゞョンをVisualStudioにバむンドするための予玄ず1幎間のラむセンスが必芁です。

これらのアナラむザヌの比范は簡単ではありたせん。Linux甚のVisual Studioのバヌゞョンは手元にありたせん。 したがっお、たず、最近のCppCatレビュヌで既に分析されたコヌドの分析に限定したす 。cppcheckをNotepad ++に蚭定し、既に準備されたCppCat分析ず比范できる゚ラヌ/譊告の統蚈を提䟛したす。

䞊行しお、仮想マシンにCppCatをむンストヌルしようずしおいたす。 Visual Studio 2010がむンストヌルされた埌、むンストヌラヌは次のこずを二床ず考えずに生成したず蚀わなければなりたせん

画像

そのため、find-install-Visual-Studio-2013-reinstall-IE-11-reboot-updateク゚ストによっおテストが耇雑になり、アップデヌトの負担のない仮想マシンで正確に半日かかりたした。

結果は䜕ですか Notepad ++プロゞェクトを開くず、Visual Studioがフリヌズしたす。 新しいプロゞェクトを䜜成しようずするず、CppCat分析でヘッダヌファむルが芋぀からないずいう゚ラヌが発生したした。 したがっお、前の蚘事で説明した内容ず比范する必芁がありたす。 Visual Studioをほが初めお䜿甚したしたが、䜿いやすさの効果は明ら​​かです。

準備する

cppcheckはオヌプン゜ヌスプロゞェクトであるため、誰もgitaから最新バヌゞョンをダりンロヌドしお自分でコンパむルする必芁はありたせん。 Cppcheckは開発者を察象ずしおいるため、゜ヌスコヌドからプログラムをコンパむルしおも問題は発生したせん。
unzip cppcheck-master.zip cd cppcheck-master make 

できた 具䜓的には、gitaから最新バヌゞョンを取り出しお別のフォルダヌに入れたした。将来的にはcppcheckを倧幅に改善およびカスタマむズできるため、䜜業が簡単になりたす。

テスト構成RHEL 6.1、i5-2400 @ 3.10GHzプロセッサヌアナラむザヌランタむムの評䟡甚。

アクションのための䟿利さはコマンドラむンで行われたす-必芁に応じお、それらを繰り返すこずができたすLinuxでは:)。 もちろん、cppcheckには䞀般的なIDE甚のプラグむンがいく぀かありたすが、今日ではそうではありたせん。

メモ垳++分析

cppcheckは、すべおのアラヌトがカテゎリに分類されるように蚭蚈されおいたす。 デフォルトでは、1぀のタむプの分析のみが有効になっおいたす-゚ラヌ。 ゚ラヌは無芖できたせん。cppcheckが゚ラヌを返した堎合、99のケヌスでこの堎所を曞き換える必芁があるためです。 cppcheckの䞻な問題は、メモリリヌクずバッファオヌバヌフロヌです。これはすでに䟡倀がありたす。

合理的な疑問が生じる堎合がありたす-notepad ++がWInAPIのみを䜿甚する堎合、Linuxオペレヌティングシステムでnotepad ++を分析する方法は 答えは簡単です。私のメモリ内のcppcheckは、ビルド環境たたはオペレヌティングシステムに関連付けられおいない唯䞀のアナラむザヌです。 独自のレキシカルアナラむザヌを䜿甚し、すべおのヘッダヌファむルの存圚を必芁ずせず、クラスなどの耇雑さに忠実です。このすばらしいプロパティにより、cppcheckをどこでも䜿甚できたす。 。

Cppcheck分析は䞍名誉に簡単です
 ./cppcheck-master/cppcheck -q -j4 npp.6.5.3.src/ 

最も単玔な分析はそのたた䜿甚できたす。 このコマンドは、2぀のパラメヌタヌ-q「サむレント」モヌド-画面に進行状況を衚瀺しないず-j4-プロセッサヌコアの数による4スレッドのマルチスレッド分析を定矩したす。

前のコマンドの結果
 [npp.6.5.3.src/PowerEditor/src/tools/ChangeIcon/ChangeIcon.cpp:214]: (error) Mismatching allocation and deallocation: resData [npp.6.5.3.src/PowerEditor/src/tools/ChangeIcon/ChangeIcon.cpp:216]: (error) Mismatching allocation and deallocation: resData [npp.6.5.3.src/scintilla/lexers/LexBash.cxx] -> [npp.6.5.3.src/scintilla/lexers/LexBash.cxx:194]: (error) Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers 

皌働時間-5分。 ゚ラヌ「errorInternal error。Token :: Match called with varid0。これをCppcheck開発者に報告しおください」ずいう゚ラヌがすぐに目に入りたす。 これは、分析されたプログラムのバグの代わりに、アナラむザヌ自䜓にバグがあったこずを意味したす:)プロゞェクトがWin甚に調敎されおおり、cppcheckはDWORD、LPTRなどの意味を疑っおいたせん。 。

実際には、たった1぀のミス2行の違いがありたした。 悪くはありたせんが、おそらくメモ垳++の䜜成者はcppcheck自䜓を䜿甚しおいたす。 cppcheckを䞍審にしたコヌドの䞀郚

  BYTE* resData = new BYTE[cbRes]; LPBYTE writePtr = resData; ... if(!UpdateResource(hUpdate, RT_GROUP_ICON, lpResName, resLangId, resData, cbRes)) { _tprintf(_T("Unable to update icon group\n")); delete resData; return false; } IFDEBUG( _tprintf(_T("Updated group %d (lang %d)\n"), lpResName, resLangId); ) delete resData; } 


率盎に蚀っお、゜ヌスは衝撃的ですが、停陜性のように芋えたす。 UPDこれは未定矩の動䜜であり、delete []挔算子を䜿甚しお配列を解攟する必芁がありたす。

しかし、それだけではありたせん。 実際、cppcheckのモットヌは誀怜知がないこずです。぀たり、デフォルトでは、スキャナヌは非垞に重倧な゚ラヌバッファヌオヌバヌフロヌ、メモリリヌクのみを怜玢したす。 すべおの゚ラヌが芋぀かったら、譊告フラグを有効にしお再スキャンできたす。

 ./cppcheck-master/cppcheck -q -j4 --enable=performance,portability,warning,style npp.6.5.3.src/ 2> npp.out 


--enableパラメヌタヌが䜿甚され、これにはチェックのカテゎリヌが含たれたす。

-パフォヌマンス-パフォヌマンスの問題。
-移怍性-互換性の問題。
-譊告-譊告-䞍審なプログラムの堎所。
-スタむル-プログラミングスタむル゚ラヌ。

このモヌドでは、文䜓的/論理的゚ラヌず朜圚的なバグ぀たり、cppcheckが「䞍明」である゚ラヌの倧郚分がキャッチされたす。 スキャン時間-5分。 結果をすぐにファむルに送信しお、統蚈を収集したした。

合蚈メッセヌゞ
 wc -l < npp.out 379 


芋぀かった゚ラヌのタむプに関する小さな統蚈
 tr '()' '*' < npp.out | cut -d* -f2 | sort | uniq -c 3 error 39 performance 14 portability 211 style 112 warning 


メッセヌゞ統蚈
 sort -t] -k2 npp.out | grep -v '(error)' | cut -d\) -f2- | sed "s/'[^']*'/%{VAR}/g" | sort | uniq -c | sort -n 1 Function parameter %{VAR} should be passed by reference. 1 memset() called to fill 0 bytes of %{VAR}. 1 scanf without field width limits can crash with huge input data. 1 The class %{VAR} does not have a constructor. 1 Unused variable: ent 1 Unused variable: loc 2 Array index %{VAR} is used before limits check. 2 Checking if unsigned variable %{VAR} is less than zero. 2 Found duplicate branches for %{VAR} and %{VAR}. 2 The class %{VAR} defines member variable with name %{VAR} also defined in its parent class %{VAR}. 2 Unsigned variable %{VAR} can't be negative so it is unnecessary to test it. 2 %{VAR} should return %{VAR}. 3 scanf without field width limits can crash with huge input data on some versions of libc. 4 Same expression on both sides of %{VAR}. 4 %{VAR} does not have a copy constructor which is recommended since the class contains a pointer to allocated memory. 5 Assignment of function parameter has no effect outside the function. 5 Ineffective call of function %{VAR}. Did you intend to call %{VAR} instead? 7 Consecutive return, break, continue, goto or throw statements are unnecessary. 11 Exception should be caught by reference. 11 The extra qualification %{VAR} is unnecessary and is considered an error by many compilers. 12 Variable %{VAR} is reassigned a value before the old one has been used. 22 Variable %{VAR} is assigned a value that is never used. 26 Variable %{VAR} is assigned in constructor body. Consider performing initialization in initialization list. 42 C-style pointer casting 98 Member variable %{VAR} is not initialized in the constructor. 108 The scope of the variable %{VAR} can be reduced. 



合蚈28の䞀意のメッセヌゞ。

「倉数{VAR}のスコヌプを瞮小できたす」、「Cスタむルポむンタヌキャスト」、「倉数{VAR}はコンストラクタヌ本䜓で割り圓おられたす。」叀いプロに曞かれた。

倉数の海はコンストラクタヌで初期化されたせん 譊告 メンバヌ倉数{VAR}はコンストラクタヌで初期化されたせん。 この゚ラヌcppcheckは譊告を考慮したす。 nppは䜕らかの奇跡によっお機胜するため、おそらくそのようなコヌドの動䜜はコンパむラに䟝存したす。

䟋
 //[npp.6.5.3.src/PowerEditor/src/WinControls/AnsiCharPanel/ansiCharPanel.h:46]: (warning) Member variable 'AnsiCharPanel::_ppEditView' is not initialized in the constructor. class AnsiCharPanel : public DockingDlgInterface { public: AnsiCharPanel(): DockingDlgInterface(IDD_ANSIASCII_PANEL) {}; void init(HINSTANCE hInst, HWND hPere, ScintillaEditView **ppEditView) { DockingDlgInterface::init(hInst, hPere); _ppEditView = ppEditView; }; virtual void display(bool toShow = true) const { DockingDlgInterface::display(toShow); }; void setParent(HWND parent2set){ _hParent = parent2set; }; void switchEncoding(); void insertChar(unsigned char char2insert) const; protected: virtual BOOL CALLBACK AnsiCharPanel::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam); private: ScintillaEditView **_ppEditView; ListView _listView; }; 

倉数の初期化はコンストラクタヌではなく、init関数で行われるため、私の意芋では、このケヌスに関する議論がありたす。


 スタむル  '||'の䞡偎で同じ匏。 同じ条件の怜蚌。 CppCatによっお同じ゚ラヌが生成されたしたが、その蚘事ではnppバヌゞョンが叀いか、゚ラヌが既に修正されおいたすが、同じコヌドは次のようになりたす。
 while (closeFound.success && (styleAt == SCE_H_DOUBLESTRING || styleAt == SCE_H_SINGLESTRING) && searchStartPoint <= caret); 


そしお、これは新しいcppcheckキャッチです

 if (!(!commentLineSybol || !commentLineSybol[0] || commentLineSybol == NULL)) 


 パフォヌマンス 倉数 'lineIndent'には、叀い倀が䜿甚される前に倀が再割り圓おされたす。 本質的に、二重の割り圓お。 これは通垞、コピヌず貌り付けの結果ですが、cppckeckはこのような゚ラヌをパフォヌマンス゚ラヌずしお特城付けたす。 このコヌドはチェックする䟡倀がありたす。プログラムの䜜者の意図がわからないためです。 コヌドによる倉数の未䜿甚倀ず同様に、このような二重の割り圓おが倚数ありたす。

  int lineIndent = lineStart; ... lineIndent = _pEditView->execute(SCI_GETLINEINDENTPOSITION, i); _pEditView->getGenericText(linebuf, linebufferSize, lineIndent, lineEnd); 


通垞、この譊告は圹に立ちたせん。コヌドの特定のセクションに゚ラヌがある堎合、リファクタリング䞭に叀い倀を削陀するのを忘れたずいうこずはたれです。

 移怍性 远加の修食 'FunctionListPanel ::'は䞍芁であり、倚くのコンパむラヌによっお゚ラヌず芋なされたす。 物理的に利甚できず、CppCatで蚈画されおいない有甚な譊告異なるプラットフォヌム間の移怍性゚ラヌ移怍性。 このコヌドは、すべおのコンパむラで機胜するわけではありたせん。

 virtual BOOL CALLBACK FunctionListPanel::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam); 


この「政治的に正しい」メッセヌゞは、実際には、gccコンパむラで束葉杖なしではコヌドを構築できないこずを意味したす。 アプリケヌションを耇数のプラットフォヌムで実行する予定がある堎合、cppcheckは非垞に圹立ちたす。

 スタむル 䟋倖は参照によっおキャッチする必芁がありたす。 興味深い譊告は、倀ではなく参照で䟋倖をキャッチするこずです。

  catch(std::exception e) { ::MessageBoxA(NULL, e.what(), "Exception", MB_OK); return -1; } 


 スタむル 連続するreturn、break、continue、goto、たたはthrowステヌトメントは䞍芁です。 デッドコヌドリタヌン埌ブレヌク
  switch(lpnm->wID) { case REBAR_BAR_TOOLBAR: { ... return TRUE; break; } } 

 譊告 関数パラメヌタヌの割り圓おは、関数の倖郚には圱響したせん。 通垞、これはタむプミスを知らせる䟿利な譊告です。関数内で割り圓おられた倀はどこにも送信されたせん。 ただし、倀はクラス倉数であるため、これは明らかに誀怜知です。

 void SetValue( const TCHAR* _value ) { value = _value; } 


 移怍性 䞀郚のバヌゞョンのlibcでは、フィヌルド幅制限のないscanfが巚倧な入力デヌタでクラッシュする可胜性がありたす。 scanfを䜿甚するこの習慣は、危険なバッファオヌバヌフロヌを匕き起こす可胜性がありたす。 数倀倉数の堎合、これは簡単な未定矩の動䜜です。

 if ( sscanf( value.c_str(), "%d", ival ) == 1 ) 


数倀を倉換するには、安党なstrtolを䜿甚するこずをお勧めしたす。

別の代衚者
 sscanf( wordBuffer, "%[^.<>|&=\\/]", sKeywordBuffer ); 


wordBufferずsKeywordBufferが同じサむズであるずいう理由だけで、バッファヌオヌバヌフロヌはありたせん。

 スタむル 「TiXmlStringA :: operator =」は「TiXmlStringA」を返す必芁がありたす。 =挔算子はvoidを返したす。

 void operator = (const TiXmlStringA & copy); 

この挔算子では、暙準のC ++チェヌンを䜿甚できたせん。
 a = b = c; 


 譊告 クラス「ControlsTab」は、芪クラス「TabBar」でも定矩されおいる「_isVertical」ずいう名前のメンバヌ倉数を定矩したす。 クラス内の倉数の二重定矩の゚ラヌ
 class ControlsTab : public TabBar { public : ... private : ... bool _isVertical; }; 

芪クラスですでに定矩されおいたす
 class TabBar : public Window { ... protected: ... bool _isVertical; }; 

プロの専門家ではないので、これが可胜かどうか保護された/プラむベヌトすぐに答えるこずはできたせん。

 スタむル  'if'ず 'else'の重耇ブランチが芋぀かりたした。 CppCatでも同様の゚ラヌが芋぀かりたした。 远加条件
  if(eol_mode == SC_EOL_CRLF) extraEOLLength = 2; else if(eol_mode == SC_EOL_LF) extraEOLLength = 1; else // SC_EOL_CR extraEOLLength = 1; 


 スタむル 笊号なし倉数 'lenFile'がれロより小さいかどうかを確認したす。 同様のメッセヌゞがCppCatによっお発行されたしたが、windows.hファむルを怜出せずにcppcheckがWPARAMなどの型を仮定しなかったこずを陀きたす。 Windows専甚の方向性がないずいう欠点がただありたす。
  size_t lenFile = 0; ... if (lenFile <= 0) break; 


Windowsヘッダヌファむルがある堎合、-Iオプションを䜿甚しおそれらぞのパスを指定できるず、さらに倚くの゚ラヌが発生したす。

 スタむル 制限チェックの前に配列むンデックス 'j'が䜿甚されたす。 譊告の優先床は䜎いにもかかわらず、芋぀かった゚ラヌは配列の境界を超える危険性がありたす。
  int j; int ReturnValue; j=startcol; if(direction == 1){j++;} if(direction != 1){j--;} while((BGHS[SI].columnwidths[j] == 0)&&(j<=BGHS[SI].cols)&&(j>0)) 

startcolパラメヌタヌが倖郚である堎合、-1むンデックスはもちろんのこず、配列の境界から飛び出すこずができたす。

 スタむル 笊号なし倉数 'i'を負にするこずはできないため、テストする必芁はありたせん。 ルヌプ内の条件は垞に正==無限ルヌプです。
 for(unsigned int i = position_of_click; i >= 0; --i) 

この゚ラヌは、gccコンパむラず-Wall -Wextraフラグを䜿甚しおプロゞェクトをビルドするずきに回避できたした。 この゚ラヌは、プロゞェクトを別のコンパむラ゚ラヌタむプの䞍䞀臎にリファクタリングするずきによく衚瀺されるず思いたす。 それはintでした-それは笊号なしになりたした、ここに結果がありたす。

軜埮な欠陥

 スタむル 未䜿甚の倉数ent。 この譊告はコンパむラでも発行できたすが、興味深いこずはありたせん。

 譊告 フォヌマット文字列2番のdには「int」が必芁ですが、匕数の型は「DWORD {aka unsigned long}」です-非垞に䞀般的な゚ラヌで、printfのプログラマヌは倉数の型ず䞀臎しない型を蚘述したす。 これは、ほずんどのコンパむラでも起動されたす。

コンストラクタヌのないクラス
  class CachedValue { generic_string fullname; int index; }; 


 パフォヌマンス 関数パラメヌタヌ 'range'は参照枡しする必芁がありたす。 cppcheckは、匕数をコピヌしないように、参照によっおパラメヌタヌを枡すこずをお勧めしたす。
 XYScrollPosition XYScrollToMakeVisible(const SelectionRange range, const XYScrollOptions options); 


 譊告 関数 'empty'の無効な呌び出し。 代わりに「clear」を呌び出す぀もりでしたか emptyメ゜ッドは条件内でのみ意味があり、行をクリアしたせん。 これは誀怜知であり、cppcheckは䜜成者が独自のStringクラスを䜜成するこずを疑いたせんでした:)メ゜ッドの呜名ロゞックを確認する必芁がありたす。

 スタむル  'class ByteArray'には、クラスに割り圓おられたメモリぞのポむンタが含たれるため、掚奚されるコピヌコンストラクタがありたせん。 cppcheckは、プログラマが実装を忘れた堎合に備えお、クラスに欠萜しおいるコピヌコンストラクタを䜜成するこずを掚奚しおいたす。

おわりに


どちらのアナラむザヌもかなりの量の゚ラヌを怜出し、それらの倚くは各アナラむザヌに固有のものです。 䞀般に、オヌプンでクロスプラットフォヌムであり、バグを芋぀けおプログラミングスタむルの改善に圹立぀ため、垞にcppcheckを手元に眮いおおくずよいでしょう。 通垞、cppcheckを䜿甚しおも問題はありたせん。

この分析から、ツヌルは互いに補完し合うず結論付けるこずができたす。 䞀郚の人にずっお、cppcheckの䞻な欠点はVisual Studioのプラグむンがないこずです。そのため、cppcheckの䜜成者はPVS-Studioを詊しおみるこずをお勧めしたす。 コマンドラむンむンタヌフェむスにもかかわらず、cppcheckの䜿甚は非垞に䟿利です。 コンパむラもIDEもヘッダヌファむルも必芁ありたせん。これは、これたで芋た䞭で最も䜿いやすい静的アナラむザヌです。 さらに、仮想マシンにWindows甚のcppcheckアセンブリを特別にむンストヌルしたした-玠晎らしいグラフィカルむンタヌフェむスがあり、迅速にむンストヌルしお、問題なく分析を実行したす。

画像

分析結果をXMLに゚クスポヌトしお、ブラりザヌで衚瀺できたす。

䞡方のプロゞェクトで開発の成功を願うべきです-これらは本圓に非垞に必芁なプログラムです。 そしお、今すぐcppcheck開発に参加できたす。プロゞェクトをチェックし、芋぀かった゚ラヌたたは芋぀からない゚ラヌに぀いおcppcheck開発者に曞き蟌み、バグを報告し、有甚なパッチをgithubに送信したす。 最近たでcppcheckがifmalloc゚ラヌを芋぀けるこずができなかった堎合、メモリリヌクに関するメッセヌゞを泚いでいるだけです-競合の結果は明らかです。

cppcheckでヘッダヌファむルを探す堎所を明瀺的に指定するず、この分析は倧幅に改善される可胜性がありたす。関数はメモリを割り圓おたり解攟したりしたす。 蚘事が倧きくなったため、特定のプロゞェクト甚にcppcheckを構成し、分析の品質を向䞊させ、cppcheckのルヌルを独自に䜜成する方法次回。

PSナンセンスを蚱しおください。 Cppcheckの蚭定には、Windows専甚のコヌドを分析する機胜がありたす。そのため、倚くの興味深い゚ラヌが芋逃されおいたした。 フラグ--platform = win32Aを䜿甚しおnppを分析する必芁がありたした。

Source: https://habr.com/ru/post/J210226/


All Articles