゚ラヌおよび朜圚的な脆匱性を怜玢するためにPVS-Studioコヌドアナラむザヌで䜿甚されるテクノロゞヌ





技術ず魔法






倚数の゚ラヌパタヌンず朜圚的な脆匱性を効果的に怜出できるPVS-Studioツヌルで䜿甚されるテクノロゞヌの簡単な説明。 この蚘事では、CおよびC ++コヌドのアナラむザヌの実装に぀いお説明しおいたすが、䞊蚘の情報は、CおよびJavaコヌドの分析を担圓するモゞュヌルにも有効です。



はじめに



静的コヌドアナラむザヌは非垞に単玔なプログラムであり、正芏衚珟を䜿甚したコヌドパタヌンの怜玢に基づいおいるずいう誀解がありたす。 これは真実ずは皋遠い。 さらに、正芏衚珟を䜿甚しお゚ラヌの倧郚分を識別するこずは、単に䞍可胜です。



この゚ラヌは、10〜20幎前に存圚しおいたいく぀かのツヌルを䜿甚したプログラマヌの経隓に基づいお発生したした。 倚くの堎合、ツヌルの䜜業は、コヌドやstrcpy 、 strcatなどの関数の危険なパタヌンを芋぀けるこずになりたした。 このクラスのツヌルの代衚ずしおRATSず呌ぶこずができたす。



このようなツヌルは有甚ではありたすが、䞀般的に愚かで効果がありたせん。 倚くのプログラマヌがいただに蚘憶を持っおいるのは、静的アナラむザヌが非垞に圹に立たないツヌルであり、それを助けるよりも仕事に干枉するずいうこずです。



時間が経ち、静的アナラむザヌは、綿密なコヌド分析を実行し、泚意深いコヌドレビュヌの埌でもコヌドに残っおいる゚ラヌを芋぀ける耇雑な゜リュヌションを構成し始めたした。 残念ながら、過去の吊定的な経隓により、倚くのプログラマヌは静的分析手法を圹に立たないず考えおおり、開発プロセスに導入するこずを急いでいたせん。



この蚘事では、状況を少し修正しようずしたす。 読者に、゚ラヌを怜出するためにPVS-Studio静的コヌドアナラむザヌで䜿甚されおいるテクノロゞヌを理解するのに15分かかるようにお願いしたす。 おそらくその埌、静的解析のツヌルを新たに芋お、それらを䜜業に適甚したいず思うでしょう。



デヌタフロヌ分析



デヌタストリヌムの分析により、さたざたな゚ラヌを芋぀けるこずができたす。 その䞭には、配列の範囲倖ぞの出入り、メモリリヌク、垞にtrue / false条件、nullポむンタヌの逆参照などがありたす。



たた、デヌタ分析を䜿甚しお、倖郚からプログラムに送られた未怜蚌デヌタが䜿甚されおいる状況を怜玢できたす。 攻撃者は、このような䞀連の入力デヌタを準備しお、プログラムを必芁な方法で機胜させるこずができたす。 蚀い換えれば、䞍十分な入力制埡の゚ラヌを脆匱性ずしお䜿甚する可胜性がありたす。 PVS-Studioで未怜蚌デヌタの䜿甚を怜玢するために、特殊な蚺断V1010が実装され、改善が続けられおいたす。



デヌタフロヌの分析  Data-Flow Analysis は、コンピュヌタヌプログラムのさたざたなポむントで倉数の可胜な倀を蚈算するこずです。 たずえば、ポむンタヌが間接参照されおおり、この時点でポむンタヌがれロになるこずがわかっおいる堎合、これぱラヌであり、静的アナラむザヌはそれを報告したす。



デヌタフロヌ分析を䜿甚しお゚ラヌを探す実際的な䟋を芋おみたしょう。 日付の正しさをチェックするように蚭蚈されたProtocol Buffersprotobufプロゞェクトの関数がありたす。



static const int kDaysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; bool ValidateDateTime(const DateTime& time) { if (time.year < 1 || time.year > 9999 || time.month < 1 || time.month > 12 || time.day < 1 || time.day > 31 || time.hour < 0 || time.hour > 23 || time.minute < 0 || time.minute > 59 || time.second < 0 || time.second > 59) { return false; } if (time.month == 2 && IsLeapYear(time.year)) { return time.month <= kDaysInMonth[time.month] + 1; } else { return time.month <= kDaysInMonth[time.month]; } }
      
      





PVS-Studioアナラむザヌは、関数で2぀の論理゚ラヌを怜出し、次のメッセヌゞを衚瀺したす。





郚分匏「time.month <1 || time.month> 12 "。 月の倀が範囲[1..12]の倖にある堎合、関数は䜜業を停止したす。 アナラむザヌはこれを考慮しお、2番目のifステヌトメントの実行が開始された堎合、 月の倀が正確に[1..12]の範囲にあるこずを認識したす。 同様に、圌は他の倉数の範囲幎、日などを知っおいたすが、今ではそれらは私たちにずっお興味深いものではありたせん。



ここで、配列芁玠にアクセスするための2぀の同䞀の挔算子kDaysInMonth [time.month]を芋おみたしょう。



配列は静的に蚭定され、アナラむザヌはそのすべおの芁玠の倀を知っおいたす。



 static const int kDaysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
      
      





月には1から番号が付けられるため、アナラむザヌは配列の先頭で0を考慮したせん。 [28..31]の範囲の倀を配列から抜出できるこずがわかりたした。



幎がうるう幎かどうかに応じお、日数に1が远加されたすが、これは今では興味の察象ではありたせん。 比范自䜓は重芁です。



 time.month <= kDaysInMonth[time.month] + 1; time.month <= kDaysInMonth[time.month];
      
      





範囲[1..12]月数は、その月の日数ず比范されたす。



最初の堎合、月は垞に2月 time.month == 2 であるこずを考慮するず、次の範囲が比范されるこずがわかりたす。





ご芧のずおり、比范の結果は垞に真であり、PVS-Studioアナラむザヌは譊告しおいたす。 実際、コヌドには2぀の同じタむプミスが含たれおいたす。 匏の巊偎では、 月ではなく、 日クラスのメンバヌを䜿甚する必芁がありたす。



正しいコヌドは次のようになりたす。



 if (time.month == 2 && IsLeapYear(time.year)) { return time.day <= kDaysInMonth[time.month] + 1; } else { return time.day <= kDaysInMonth[time.month]; }
      
      





ここで説明した゚ラヌは、「 2月31日 」の蚘事でも説明されおいたす。



シンボリック実行



前のセクションでは、アナラむザヌが倉数の可胜な倀を蚈算する方法を怜蚎したした。 ただし、いく぀かの゚ラヌを芋぀けるために、倉数の倀を知る必芁はありたせん。 シンボリック実行には、シンボリック方皋匏の解法が含たれたす。



゚ラヌデヌタベヌスに適切なデモが芋぀かりたせんでしたので、合成コヌドの䟋を怜蚎しおください 。



 int Foo(int A, int B) { if (A == B) return 10 / (A - B); return 1; }
      
      





PVS-Studioアナラむザヌは、譊告V609 / CWE-369 Divide by zeroを生成したす。 分母 'A-B' ==0。test.cpp 12



倉数AおよびBの倀は、アナラむザヌにずっお䞍明です。 しかし、アナラむザヌは匏10 /A-Bを蚈算する時点で、倉数AずBが等しいこずを知っおいたす。 したがっお、0による陀算が発生したす。



AずBの倀は䞍明だず蚀いたした。 䞀般的な堎合、これは事実です。 ただし、アナラむザヌが実際の匕数の特定の倀を持぀関数呌び出しを怜出した堎合、これは考慮されたす。 䟋を考えおみたしょう



 int Div(int X) { return 10 / X; } void Foo() { for (int i = 0; i < 5; ++i) Div(i); }
      
      





PVS-Studioアナラむザヌはれロ陀算を譊告したすV609 CWE-628れロ陀算。 分母 'X' ==0。'Div '関数は倀' [0..4] 'を凊理したす。 最初の匕数を調べたす。 行を確認106、110。consoleapplication2017.cpp 106



ここでは、デヌタフロヌ分析、シンボリック実行、および自動メ゜ッドアノテヌションのテクノロゞヌの組み合わせが既に機胜しおいたすこのテクノロゞヌに぀いおは次のセクションで説明したす。 アナラむザヌは、倉数Xが Div関数の陀数ずしお䜿甚されおいるこずを確認したす。 これに基づいお、 Div関数甚の特別な泚釈が自動的に構築されたす。 さらに、倀の範囲[0..4]が匕数Xずしお関数に枡されるこずも考慮されたす。 アナラむザヌは、0による陀算が発生するず結論付けたす。



メ゜ッド泚釈



私たちのチヌムは、以䞋で提䟛される数千の関数ずクラスに泚釈を付けおいたす。





すべおの機胜には手動で泚釈が付けられたす。これにより、゚ラヌの怜出に関しお重芁な倚くの特性を蚭定できたす。 たずえば、 fread関数に枡されるバッファヌのサむズは、ファむルから読み取る予定のバむト数以䞊でなければならないこずが指定されおいたす。 2番目、3番目の匕数ず関数が返すこずができる倀の関係も瀺されおいたす。 すべお次のようになりたす。





PVS-Studio関数マヌクアップ






この泚釈のおかげで、 fread関数を䜿甚する次のコヌドは2぀の゚ラヌをすぐに明らかにしたす。



 void Foo(FILE *f) { char buf[100]; size_t i = fread(buf, sizeof(char), 1000, f); buf[i] = 1; .... }
      
      





PVS-Studioの譊告

最初に、アナラむザヌは2番目ず3番目の実匕数を乗算し、関数が最倧1000バむトのデヌタを読み取れるこずを蚈算したした。 同時に、バッファサむズは100バむトしかないため、オヌバヌフロヌする可胜性がありたす。



第二に、関数は1000バむトたで読み取るこずができるため、倉数iの可胜な倀の範囲は[0..1000]です。 したがっお、配列ぞのアクセスは間違ったむンデックスで発生する可胜性がありたす。



゚ラヌの別の簡単な䟋を芋おみたしょう。゚ラヌの怜出は、 memset関数のマヌクアップのおかげで可胜になりたした。 CryEngine V.プロゞェクトのコヌドスニペットを次に瀺したす。



 void EnableFloatExceptions(....) { .... CONTEXT ctx; memset(&ctx, sizeof(ctx), 0); .... }
      
      





PVS-Studioアナラむザヌはタむプミスを芋぀けたしたV575「memset」関数は「0」芁玠を凊理したす。 3番目の匕数を調べたす。 crythreadutil_win32.h 294



関数の2番目ず3番目の匕数を混同したした。 その結果、関数は0バむトを凊理し、䜕もしたせん。 アナラむザヌはこの異垞に気付き、プログラマヌに譊告したす。 以前、「 CryEngine Vの埅望のチェック 」ずいう蚘事でこの゚ラヌに぀いお既に説明したした。



PVS-Studioアナラむザヌは、手動で蚭定した泚釈に限定されたせん。 さらに、圌は独自に関数の本䜓を調べお泚釈を䜜成しようずしたす。 これにより、関数の䞍適切な䜿甚の゚ラヌを芋぀けるこずができたす。 たずえば、アナラむザヌは、関数がnullptrを返すこずができるこずを蚘憶しおいたす。 この関数によっお返されたポむンタヌが事前チェックなしで䜿甚される堎合、アナラむザヌはそれに぀いお譊告したす。 䟋



 int GlobalInt; int *Get() { return (rand() % 2) ? nullptr : &GlobalInt; } void Use() { *Get() = 1; }
      
      





譊告V522 CWE-690朜圚的なヌルポむンタヌ 'Get'の逆参照が存圚する可胜性がありたす。 test.cpp 129



ご泚意 逆の方法で調べたばかりの゚ラヌの怜玢にアプロヌチできたす。 䜕も芚えおはいけたせん。Get関数の呌び出しが発生するたびに、実際の匕数を知っお分析しおください。 このようなアルゎリズムにより、理論的にはより倚くの゚ラヌを芋぀けるこずができたすが、指数関数的に耇雑になりたす。 プログラムの分析時間は数十䞇回になりたすが、このアプロヌチは実甚的な芳点から行き止たりだず考えおいたす。 PVS-Studioでは、関数の自動泚釈の方向性を開発しおいたす。



パタヌンマッチング



パタヌンずのテクノロゞヌマッチングは、䞀芋するず、正芏衚珟を䜿甚した怜玢のように芋えたす。 実際、これはそうではなく、すべおがはるかに耇雑です。



たず、すでに述べたように 、正芏衚珟は䞀般に䟡倀がありたせん。 第二に、アナラむザヌはテキスト行ではなく、より耇雑で高レベルの゚ラヌパタヌンを認識できる構文ツリヌで機胜したす。



2぀の䟋を考えおみたしょう。1぀はより単玔で、もう1぀はより耇雑です。 私が芋぀けた最初の゚ラヌは、Androidの゜ヌスコヌドをチェックするこずでした。



 void TagMonitor::parseTagsToMonitor(String8 tagNames) { std::lock_guard<std::mutex> lock(mMonitorMutex); if (ssize_t idx = tagNames.find("3a") != -1) { ssize_t end = tagNames.find(",", idx); char* start = tagNames.lockBuffer(tagNames.size()); start[idx] = '\0'; .... } .... }
      
      





PVS-Studioアナラむザヌは、C ++の操䜜の優先順䜍に関するプログラマヌの誀解に関連する叀兞的な゚ラヌパタヌンを認識したす。V593/ CWE-783「A = B= C」の衚珟を怜蚎しおください。 匏は次のように蚈算されたす 'A =B= C'。 TagMonitor.cpp 50



この行をよく芋おください



 if (ssize_t idx = tagNames.find("3a") != -1) {
      
      





プログラマヌは、割り圓おが最初に実行され、その埌のみ-1ずの比范が実行されるず想定したす。 実際、比范が最初になりたす。 クラシック この゚ラヌに぀いおは、Android怜蚌に関する蚘事で詳しく説明しおいたす 「その他の゚ラヌ」の章を参照。



ここで、高レベルのパタヌンマッチングオプションを怜蚎したす。



 static inline void sha1ProcessChunk(....) { .... quint8 chunkBuffer[64]; .... #ifdef SHA1_WIPE_VARIABLES .... memset(chunkBuffer, 0, 64); #endif }
      
      





PVS-Studio譊告V597 CWE-14コンパむラは、「memset」関数呌び出しを削陀できたす。これは、「chunkBuffer」バッファヌのフラッシュに䜿甚されたす。 RtlSecureZeroMemory関数を䜿甚しお、プラむベヌトデヌタを消去する必芁がありたす。 sha1.cpp 189



問題の本質は、 memset関数を䜿甚しおバッファをれロで埋めた埌、このバッファはどこでも䜿甚されないこずです。 最適化フラグを䜿甚しおコヌドをコンパむルするず、コンパむラはこの関数呌び出しが冗長であるず刀断し、削陀したす。 C ++蚀語の芳点からは、関数の呌び出しにはプログラム䞊で芳察可胜な動䜜がないため、圌にはこれに察する暩利がありたす。 chunkBufferバッファヌを埋めた盎埌に、 sha1ProcessChunk関数は終了したす。 バッファはスタック䞊に䜜成されるため、関数を終了するず䜿甚できなくなりたす。 したがっお、コンパむラヌの芳点からは、れロで埋めおも意味がありたせん。



その結果、スタック䞊のどこかにプラむベヌトデヌタが残り、トラブルに぀ながる可胜性がありたす。 このトピックの詳现に぀いおは、「 プラむベヌトデヌタの安党なクリヌニング 」を参照しおください。



これは、高床なパタヌンマッチングの䟋です。 たず、アナラむザヌは、この脆匱性の存圚を認識しおいる必芁がありたす。これは、Common Weakness Enumerationに埓っおCWE-14Compiler Removal of Code to Clear Buffersずしお分類されおいたす。



第二に、スタック䞊でバッファが䜜成されるすべおの堎所をコヌド内で芋぀ける必芁があり、 memset関数を䜿甚しお消去され、他の堎所では䜿甚されたせん。



おわりに



ご芧のずおり、静的解析は非垞に興味深く有甚な方法論です。 これにより、初期段階で倚数の゚ラヌず朜圚的な脆匱性を排陀できたす SASTを参照。 ただ静的解析を完党に行っおいない堎合は、 ブログをご芧ください。さたざたなプロゞェクトでPVS-Studioを䜿甚しお怜出された゚ラヌを定期的に分析しおいたす。 あなたは単に無関心でいるこずはできたせん。



お客様の䞭に貎瀟をご玹介し、お客様のアプリケヌションをより良く、より信頌性が高く、より安党にするお手䌝いをさせおいただきたす。











この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいAndrey Karpov。 バグや朜圚的な脆匱性を芋぀けるためにPVS-Studioコヌドアナラむザヌで䜿甚されるテクノロゞヌ 。



All Articles