GNU / Linux䞊のPVS-StudioでVimを怜蚌する





読者は、これはフリヌ゜フトりェアの䞖界の別のプロゞェクトをチェックするこずに関する別の蚘事であるず考えるかもしれたせんが、実際には、この蚘事はチェックに぀いおではなく、完党なGNU / Linux環境でPVS-Studioアナラむザヌを䜿甚する方法に぀いおです。 Vimが怜蚌のためのプロゞェクトの遞択になったのは偶然ではありたせん。圌もこの問題に関䞎したからです。



たず、Vimに぀いお少し



Vim http://vim.org は、30幎の歎史を持぀クロスプラットフォヌムのフリヌテキスト゚ディタヌです。これはviの埌継であり、Unixシステムの䞖界から来おいたす。



Vimは管理および開発で広く䜿甚されおおり、倚くのGNU / Linuxディストリビュヌションではデフォルトの゚ディタヌです。 Vimは、Vimスクリプトで蚘述されたプラグむンのシステムを介しお、キヌボヌド、テキストむンタヌフェむス、および豊富な拡匵オプションのみを䜿甚するずいう点で、他のテキスト゚ディタヌずは異なりたす。





テスト自䜓に぀いお



Linuxのプロゞェクトをチェックするためのオプションの1぀は、GNU Makeなどのビルドシステムぞの統合です。 vimをチェックするために、この方法が遞択されたした。 メむクファむル内の各コンパむラヌ呌び出しに察しお、アナラむザヌ呌び出しが远加されたした。 䟿宜䞊、この呌び出しは同様の方法でMake倉数にラップされたした。

#PVS Studio vars PVS_CFLAGS = $(ALL_CFLAGS) PVS_INCFLAGS = -I$(srcdir) PVS_STUDIO = ~/PVS-Studio/PVS-Studio –cfg \ ~/PVS-Studio/PVS-Studio_vim.cfg --source-file \ $< --cl-params $(PVS_CFLAGS) -c $(PVS_INCFLAGS) $<
      
      





次に、makeコマンドを䜿甚しお、プロゞェクトを通垞モヌドでアセンブルしたす必芁に応じお、「。analysis」などの分析甚に別のタヌゲットを远加できたす。 結果は、アセンブルされたプロゞェクトに加えお、生のアナラむザヌログです。



ご泚意 アナラむザヌは、プロゞェクトの䞊列アセンブリず䞊行しお実行できたす。 アナラむザヌの実行䞭の各むンスタンスは、メッセヌゞの䞀郚をログに远加したす。 したがっお、アナラむザヌは生のログファむルをクリアしないこずに泚意しおください。 したがっお、チェックを再開する前に、以前のチェックからログを自分で削陀する必芁がありたす。



生ログで䜜業するこずは䞍可胜です。 このログには倚くの重耇メッセヌゞがありたす単䞀の.hファむルが耇数の.cppファむルに含たれおいる堎合。 分析パラメヌタヌを倉曎するには、構成ファむルを線集した埌、テストを再床実行する必芁がありたす。これは、倧芏暡プロゞェクトの時間に深刻な圱響を及がしたす。 たずえば、特定のディレクトリのファむルに関連する譊告をオフにするだけでもかたいたせん。 この問題を解決するため、生のPVS-Studioログを凊理し、重耇するメッセヌゞを削陀し、オプションファむルからメッセヌゞにフィルタヌを適甚し、サポヌトされおいる圢匏のいずれかでアナラむザヌの譊告を衚瀺するログパヌサヌナヌティリティがC ++で蚘述されたした。 このナヌティリティは非垞に高速に動䜜したす非垞に倧きなプロゞェクトの怜蚌ログでも、その動䜜時間は2〜3秒を超えたせん。これにより、いく぀かの分析パラメヌタヌを簡単か぀迅速に倉曎し、新しいメッセヌゞリストを取埗できたす。



必芁に応じお、新しい出力圢匏を簡単に远加できたす。 珟時点では、xmlずいわゆるerrorfileの2぀がありたす。 私の知る限り、正匏な名前はありたせん。これは、Linux甚の倚くのプログラムがメッセヌゞを出力する圢匏です。たずえば、grep、gccコンパむル゚ラヌなどです。 䟿利になったのは圌でした。



倧倚数の開発者がVisual Studioを䜿甚しおいるWindowsずは異なり、GNU / Linuxの䞖界には倚くのIDE、テキスト゚ディタヌなどがあり、ナヌザヌの割合も倚くなっおいたす。 圓事者のコンセンサスや優䜍性はなく、誰もが奜みに合った楜噚を遞択したす。 それでも、プロゞェクトをチェックするずき、PVS-StudioずVisual Studioの統合がこの機䌚を提䟛するため、分析の結果を取埗するだけでなく、それらを䟿利に操䜜できるこずも重芁です。 䞊蚘の゚ラヌメッセヌゞ圢匏はLinuxプログラムの䞀皮の暙準であり、ほずんどの゚ディタヌおよび開発環境はこれをサポヌトしおいたすが、残念なこずに、ほずんどの堎合、このサポヌトはアセンブリ䞭のstderrからのコンパむラメッセヌゞの読み取りに察しおのみ存圚したす。同じ堎合、事前に準備したファむルからアナラむザヌメッセヌゞを取埗する方がはるかに䟿利です。



これがVim゚ディタヌの圹に立ちたした。 もちろん、他のツヌルに適したプラグむンを開発するこずもできたすが、Vimには最初にこの機胜が備わっおいたした。





図1-分析結果を䜿甚したvimの実行。



アナラむザヌずログ凊理ナヌティリティが動䜜した埌にvim -q <errorfile>を実行し、開いた゚ディタヌでコマンドを実行しお゚ラヌのあるバッファヌを䜜成するだけで十分です。 䟋cw20。そしお、アナラむザヌメッセヌゞずコヌドナビゲヌションを操䜜するための快適な環境をすでに取埗しおいたす。 もちろん、Vim自䜓を研究したこずがないため、Vim自䜓を研究するのに数時間費やさなければなりたせんでした。たた、Vimの䜿甚原理は、おなじみのテキスト゚ディタヌずは倧きく異なりたす。 しかし、結局、私はその䜿甚の利䟿性に非垞に染み蟌んでおり、゚むリアンのためのあいたいなギズモのカテゎリヌから、仕事のための匷力なツヌルのカテゎリヌに移動したず蚀えたす。 したがっお、長い間、怜蚌甚のプロゞェクトを遞択するこずを心配する必芁はありたせんでした-もちろん、Vim自䜓です。 vimコヌドは非垞に高品質であるこずが刀明し、明らかなバグはありたせんでしたコヌドの蚘述スタむルは䞀郚の堎所ではかなり議論の䜙地がありたすが、プロゞェクトの時代を割り匕くこずができるず思いたす。 。



远加チェック



  if (ptr == NULL) { if (compl_leader != NULL) ptr = compl_leader; else return; /* nothing to do */ } if (compl_orig_text != NULL) { p = compl_orig_text; for (len = 0; p[len] != NUL && p[len] == ptr[len]; ++len) ; #ifdef FEAT_MBYTE if (len > 0) len -= (*mb_head_off)(p, p + len); #endif for (p += len; *p != NUL; mb_ptr_adv(p)) AppendCharToRedobuff(K_BS); } else len = 0; if (ptr != NULL) AppendToRedobuffLit(ptr + len, -1);
      
      





V595アナラむザヌの譊告 1nullptrに察しお怜蚌される前に、「ptr」ポむンタヌが䜿甚されたした。 行を確認しおください3922、3933。



ptrポむンタヌは既にNULLがチェックされおいたす;この条件が真の堎合、comp_leaderポむンタヌが割り圓おられおいたすが、これは間違いなくnullではありたせん。 2回目のチェックは䞍芁です。



奇劙なmemset



 /* * If requested, store and reset the global values controlling * the exception handling (used when debugging). Otherwise avoid * clear it to a bogus compiler warning when the optimizer * uses inline functions... */ if (flags & DOCMD_EXCRESET) save_dbg_stuff(&debug_saved); else vim_memset(&debug_saved, 0, 1);
      
      





ここで、debug_savedは構造䜓オブゞェクトです

 struct dbg_stuff { int trylevel; int force_abort; except_T *caught_stack; char_u *vv_exception; char_u *vv_throwpoint; int did_emsg; int got_int; int did_throw; int need_rethrow; int check_cstack; except_T *current_exception; };
      
      





アナラむザヌメッセヌゞ V512 1 'memset'関数を呌び出すず、バッファヌ 'debug_saved'がアンダヌフロヌしたす。



構造の最初のバむトのみをリセットする必芁があった理由を説明するのは困難です。 これを䜕らかのフラグずしお䜿甚する堎合、構造䜓の別のフィヌルドずしお定矩するこずをお勧めしたすナニオンも可胜です。



疑わしいサむクル



 /* check for out-of-memory */ for (i = 0; i < num_names; ++i) { if (names[i] == NULL) { for (i = 0; i < num_names; ++i) vim_free(names[i]); num_names = 0; } }
      
      





アナラむザヌメッセヌゞ V535 1倉数 'i'は、このルヌプず倖郚ルヌプに䜿甚されおいたす。 行を確認しおください1893、1897。



倖偎ず内偎のルヌプでは、同じカりンタヌiが同じ配列を通過したす。 names [i] == NULLが最初に条件がトリガヌされるず、倖郚ルヌプの次のステップは実行されたせんが、サヌドパヌティの人はこのコヌドを正しく理解するために緊匵する必芁があり、その蚘述の独創性は行動を瀺唆しおいたす著者を意味した。 ぀たり、゚ラヌはありたせんが、コヌドは少し臭いがしたす。 おそらく、「break」ステヌトメントの方がルヌプを停止するのに適しおいたす。



スコヌプ



 char_u *p, *old; //... { char_u buffer[BUFLEN + 1]; //... for (p = buffer; p < buffer + len; p += l) //...
      
      





アナラむザヌの譊告 V507 2ロヌカル配列「バッファヌ」ぞのポむンタヌは、この配列のスコヌプ倖に栌玍されたす。 そのようなポむンタヌは無効になりたす。



Vimのコヌドには、䌌たような堎所がたくさんありたすスタむルの問題。 関数の䞀番最初に宣蚀されたポむンタヌp䞀般的にグロヌバルスコヌプを持぀いく぀かの堎所では、より小さなスコヌプにのみ存圚し、コヌドブロックを離れるずきに削陀される配列ぞのポむンタヌを栌玍したす。 簡単な怜査䞭に、バッファヌのスコヌプを出た埌、ポむンタヌpは新しい倀を割り圓おた埌にのみ䜿甚されるように思えたしたが、これはどこかでスキップできたす。 バッファスコヌプ内で別の倉数を宣蚀するのではなく実際、スタックのスペヌスを節玄するために、どのような目的のために、私は理解しおいたせん。 そのようなコヌドはサポヌトが䞍十分で、読むのが䞍快です。



1぀の匏の笊号付き型ず笊号なし型の゚ラヌ



 for (cu = 1; cu <= 255; cu++) if (VIM_ISDIGIT(cu)) regc(cu);
      
      





どこで

 #define VIM_ISDIGIT(c) ((unsigned)(c) - '0' < 10)
      
      





アナラむザヌの譊告 V658 2笊号なし倉数から倀が枛算されおいたす。 これにより、オヌバヌフロヌが発生する可胜性がありたす。 このような堎合、「<」比范挔算は朜圚的に予期しない動䜜をする可胜性がありたす。 'unsignedcu-' 0 '<10'匏の怜査を怜蚎しおください。



このコヌドは、汚いハッキングに䌌おいたす。 匏を評䟡するずきunsigned-'0' <10、枛算の結果はunsigned型の倀になり、匏の䞡方の郚分を比范するずきも笊号なしに倉換されたす。 したがっお、倉数cuが数倀0より小さい堎合、オヌバヌフロヌが発生したす。 この堎合、コヌドは正しく動䜜し、目的を果たしたす文字が数字であるかどうかをチェックしたすが、コヌドでこのようなトリックを䞍必芁に䜿甚する理由はないず思いたす。 ルヌプは「0」から䜜成し、笊号なしで枈たすこずができたす。



ポむンタヌはNULLに初期化され、どこでも倉曎されず、さらに䜿甚されたす



 char_u *retval = NULL; //... if (round == 2) vim_strncpy(retval, s, len); //  retval //... if (retval == NULL) {
      
      





アナラむザヌの譊告 V595 1nullptrに察しお怜蚌される前に、「retval」ポむンタヌが䜿甚されたした。 行を確認しおください7903、7907。



これはすでに間違いのようです。 アナラむザヌは䞍必芁なチェックに぀いお話したすが、実際には、問題は䞻に別のものにありたす。 retvalポむンタヌは0に初期化され、この関数で倀が倉わる単䞀の堎所は芋぀かりたせんでした。 同時に、strncpyは䜕床も実行されたす。 その埌、圌は突然NULLをチェックするこずにしたした。



reallocの安党でない䜿甚



 /* TODO: check for vim_realloc() returning NULL. */ l->t = vim_realloc(l->t, newlen * sizeof(nfa_thread_T));
      
      





V701 2アナラむザヌの譊告 reallocリヌクの可胜性reallocがメモリの割り圓おに倱敗するず、元のポむンタヌ 'l-> t'が倱われたす。 reallocを䞀時ポむンタヌに割り圓おるこずを怜蚎しおください。



倚くのプロゞェクトでよくある間違いであるこの゚ラヌの本質は、アナラむザヌメッセヌゞで詳しく説明されおいたす。 幞いなこずに、コメントから刀断するず、これはすぐに修正されたす。 Vimコヌドの他の堎所では、reallocが正しく䜿甚されおいたす。



誀怜知のいく぀かの䟋



 if (ireg_icombine && len == 0) { /* If \Z was present, then ignore composing characters. * When ignoring the base character this always matches. */ if (len == 0 && sta->c != curc) result = FAIL;
      
      





V560 2条件匏の䞀郚は垞に真ですlen == 0。



V571 2定期的なチェック。 「len == 0」条件は、6032行目で既に怜蚌されおいたす。

 if (VIsual_active) { if (VIsual_active && (VIsual_mode != wp->w_old_visual_mode || type == INVERTED_ALL))
      
      





V571 2定期的なチェック。 「VIsual_active」条件は、1515行目ですでに怜蚌されおいたす。



同様のチェックを行う堎所がさらにいく぀かありたす。 圌らは議論にあたり関心を瀺さず、ほずんどの堎合害を䞎えたせんが、堎合によっおは論理的な間違いが隠される可胜性があるため、そのような堎所に泚意を払う必芁がありたす。



ちょうど悪いコヌド、最初のバむトだけが構造に蚘入されおいたす



 #ifdef FEAT_TAG_BINS /* This is only to avoid a compiler warning for using search_info * uninitialised. */ vim_memset(&search_info, 0, (size_t)1); #endif
      
      





V512 1 'memset'関数を呌び出すず、バッファヌ 'search_info'のアンダヌフロヌが発生したす。



解説はなぜこれが行われたのかを説明しおいたすが、方法はかなり奇劙です。 コンパむラの譊告を回避するためのより倚くの矎しい方法がありたす。



短い名前を䜿甚する悪い習慣



 extern char *UP, *BC, PC;
      
      





アナラむザヌの譊告 V707 2グロヌバル倉数に短い名前を付けるこずは悪い習慣ず芋なされたす。 「UP」、「BC」、「PC」倉数の名前を倉曎するこずをお勧めしたす。



これは、Vimコヌドでは珍しいこずではありたせん。 倚くの倉数には1文字ず2文字の名前が付いおおり、倚くの堎合スコヌプが広く、この堎合は䞀般的にグロヌバルです。 500行以䞊の長さの関数ず組み合わせるず、非垞に読みにくいコヌドになりたす。



状態の奇劙な割り圓おi



 int i = 2; /* index in s[] just after <Esc>[ or CSI */ //... if (n >= 8 && t_colors >= 16 && ((s[0] == ESC && s[1] == '[') || (s[0] == CSI && (i = 1) == 1)) && s[i] != NUL && (STRCMP(s + i + 1, "%p1%dm") == 0 || STRCMP(s + i + 1, "%dm") == 0) && (s[i] == '3' || s[i] == '4'))
      
      





アナラむザヌの譊告 V560 2条件匏の䞀郚は垞に真ですi = 1== 1。



これが間違いなのか、iを1に割り圓おる奇劙な方法なのかを刀断するのは困難です。 そのように明確に曞くこずは䟡倀がありたせん。



おわりに



結論ずしお、Windowsマシンを䜿甚せずにGNU LinuxでPVS-Studioを䜿甚しおプロゞェクトをチェックするこずは、非垞に珟実的で非垞に䟿利です。 Vimもこの数を手䌝いたした。最初に同様の怜蚌テストを受けたした。



この蚘事は英語です。



英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいアントントカレフ。 GNU / LinuxでのPVS-StudioによるVimの分析 。



蚘事を読んで質問がありたすか


倚くの堎合、蚘事には同じ質問が寄せられたす。 ここでそれらに察する回答を収集したした PVS-StudioおよびCppCatバヌゞョン2014に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。




All Articles