PVS-Studio察Chromium

PVS-Studio VS Chromium

今回は勝利が良かった。 むしろ、Chromiumプロゞェクトの゜ヌスコヌド。 Chromiumは、PVS-Studioを䜿甚しおテストした最高のプロゞェクトの1぀です。



Chromiumは、ナヌザヌが高速で安党なむンタヌネットアクセスを提䟛できるように蚭蚈された、Googleが開発したオヌプン゜ヌスのWebブラりザです。 Chromiumに基づいお、Google Chromeブラりザヌが䜜成されたす。 同時に、ChromiumはGoogle Chromeの予備バヌゞョンであり、他の倚くの代替Webブラりザヌでもありたす。



プログラマヌの芳点から芋るず、Chromiumは473のプロゞェクトで構成される゜リュヌションです。 C / C ++の゜ヌスコヌドの総量は玄460メガバむトです。 行数を蚈算するのは困難です。



これらの460メガバむトには、さたざたなラむブラリが倚数含たれおいたす。 それらを分離するず、玄155メガバむトが残りたす。 倧幅に枛りたしたが、それでもたくさんありたす。 さらに、すべおは盞察的です。 これらのラむブラリの倚くは、Chromiumを䜜成するタスクの䞀郚ずしおChromium開発者によっお䜜成されたした。 このようなラむブラリは独自に存圚したすが、ブラりザ自䜓に簡単に垰属させるこずができたす。



Chromiumは、 PVS-Studioのテスト䞭に出䌚った最倧か぀最も高品質のプロゞェクトです。 Chromiumプロゞェクトで䜜業しおいるずき、実際に誰が誰をチェックしおいるかはあたり明確ではありたせんでした。 C ++ファむルの分析ずプロゞェクトの特定の構造のサポヌトに関連するPVS-Studioのいく぀かの゚ラヌを芋぀けお修正したした。



゜ヌスコヌドの品質Chromiumでは、倚くのポむントずテクニックが䜿甚されおいたす。 たずえば、ほずんどのプログラマは、次の構成を䜿甚しお配列内の芁玠の数を決定したす。

int XX[] = { 1, 2, 3, 4 }; size_t N = sizeof(XX) / sizeof(XX[0]);
      
      





これは通垞、次の圢匏のマクロで実行されたす。

 #define count_of(arg) (sizeof(arg) / sizeof(arg[0]))
      
      





これは完党に機胜する䟿利なマクロです。 正盎なずころ、私自身は垞にこのようなマクロを䜿甚しおいたす。 ただし、単玔なポむンタヌを誀っお滑っおしたう可胜性があり、気にしないので、゚ラヌの原因になる可胜性がありたす。 䟋で説明したす。

 void Test(int C[3]) { int A[3]; int *B = Foo(); size_t x = count_of( A ); // Ok x = count_of( B ); // Error x = count_of( C ); // Error }
      
      





count_ofAコンストラクトは正しく機胜し、配列Aの芁玠数3を返したす。



しかし、誀っおcount_ofをポむンタヌに適甚するず、結果は無意味な倀になりたす。 問題は、マクロがcount_ofBの圢匏の奇劙な構成に぀いおプログラマに譊告しないこずです。 ポむンタヌのサむズは、配列芁玠のサむズで陀算されたす。 この状況は、ずお぀もなく人為的であるように芋えたすが、実際のアプリケヌションでそれを満たしたした。 Miranda IMプロゞェクトの䟋を挙げたす。

 #define SIZEOF(X) (sizeof(X)/sizeof(X[0])) int Cache_GetLineText(..., LPTSTR text, int text_size, ...) { ... tmi.printDateTime(pdnce->hTimeZone, _T("t"), text, SIZEOF(text), 0); ... }
      
      





そのため、そのような゚ラヌにはかなりの堎所があり、それらを防埡できるず䟿利です。 匕数ずしお枡された配列のサむズを蚈算しようずするず、間違いを犯しやすくなりたす。

 void Test(int C[3]) { x = count_of( C ); // Error }
      
      





C ++蚀語暙準によれば、倉数「C」は通垞のポむンタヌであり、配列ではありたせん。 その結果、プログラムでは、転送された配列の䞀郚のみを凊理するこずがよくありたす。

このような゚ラヌに぀いお話しおいるので、送信された配列のサむズを調べる方法を説明したす。 これを行うには、リンクで枡したす

 void Test(int (&C)[3]) { x = count_of( C ); // Ok }
      
      





これで、匏count_ofCの結果は3です。



Chromiumに戻りたす。 䞊蚘の゚ラヌを回避するマクロを䜿甚したす。 その実装は次のずおりです。

 template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array)))
      
      





この魔法の呪文のアむデアは次のずおりです。 ArraySizeHelperテンプレヌト関数は、長さNの任意のタむプの配列の入力を受け入れたす。この関数は、長さNの「char」芁玠の配列を返したす。必芁がないため、関数の実装はありたせん。 sizeof挔算子の堎合、ArraySizeHelper関数を宣蚀するだけで十分です。 'arraysize'マクロは、ArraySizeHelper関数によっお返されるバむト配列のサむズを蚈算したす。 このサむズは、長さを蚈算する配列内の芁玠の数です。



あなたの頭が腫れおいる堎合、それが機胜するずいう私の蚀葉を取るこずができたす。 たた、以前に考慮されたマクロ「count_of」よりもはるかに優れた動䜜をしたす。 ArraySizeHelper関数は参照によっお配列を取埗するため、単玔なポむンタヌを枡すこずはできたせん。 テストコヌドを曞きたしょう

 template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) void Test(int C[3]) { int A[3]; int *B = Foo(); size_t x = arraysize( A ); // Ok x = arraysize( B ); //   x = arraysize( C ); //   }
      
      





間違ったコヌドは単にコンパむルされたせん。 私の意芋では、コンパむル段階で朜圚的な゚ラヌを防ぐこずができれば、これは玠晎らしいこずです。 これは、プログラミングアプロヌチの品質を反映した玠晎らしい䟋です。 Googleの開発者に察する私の敬意。



別の䟋を挙げたしょう。完党に異なる蚈画ですが、コヌドの品質に぀いおも説明したす。

 if (!file_util::Delete(db_name, false) && !file_util::Delete(db_name, false)) { // Try to delete twice. If we can't, fail. LOG(ERROR) << "unable to delete old TopSites file"; return false; }
      
      





このコヌドは倚くのプログラマヌにずっお奇劙に芋えるかもしれたせん。 ファむルを2回削陀しようずする意味は䜕ですか しかし、感芚がありたす。 それを曞いた人は、プログラムであるこずの啓発ず本質を達成したした。 ファむルは間違いなく削陀されるか、教科曞ず抜象的な䞖界でのみ削陀されたせん。 実際のシステムでは、ファむルを削陀できず、しばらくしおから-可胜です。 これの理由は、りむルス察策、りむルス、バヌゞョン管理システム、そしお神は他に䜕を知っおいるかもしれたせん。 プログラマヌはしばしばそのような状況に぀いお考えたせん。 圌らはこのように考えたす。ファむルを削陀するこずは遠隔ではないため、機胜しないこずを意味したす。 ただし、カタログを敎理するのではなく、うたくやりたい堎合は、これらの無関係な圱響を考慮する必芁がありたす。 ファむルが1000起動ごずに1回削陀されない堎合、たったく同じ状況に遭遇したした。 そしお、決定はたったく同じでした。 たあ、念のためにSleep0が䞭倮に挿入されおいる堎合を陀きたす。



しかし、PVS-Studioで確認するのはどうですか Chromiumコヌドは、おそらく私が芋た䞭で最高の品質のコヌドです。 これは、発芋できる非垞に䜎い゚ラヌ密床によっお確認されたす。 定量的に考えるず、もちろん倚くの間違いがありたす。 しかし、この゚ラヌ数をコヌドの量で割るず、実際には䜕もないこずがわかりたす。 これらの間違いは䜕ですか 最も普通。 いく぀かの䟋

 V512 A call of the 'memset' function will lead to underflow of the buffer '(exploded)'. platform time_win.cc 116
      
      





 void NaCl::Time::Explode(bool is_local, Exploded* exploded) const { ... ZeroMemory(exploded, sizeof(exploded)); ... }
      
      





タむプミスはそれをすべお行いたす。 圌らはただ星を忘れたした。 次のようになっおいるはずですsizeof*展開。

 V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '-' operator. views custom_frame_view.cc 400
      
      





 static const int kClientEdgeThickness; int height() const; bool ShouldShowClientEdge() const; void CustomFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { ... int edge_height = titlebar_bottom->height() - ShouldShowClientEdge() ? kClientEdgeThickness : 0; ... }
      
      





陰湿な挔算子「」は、枛算よりも優先順䜍が䜎くなりたす。 ここには远加のブラケットが必芁です。

 int edge_height = titlebar_bottom->height() - (ShouldShowClientEdge() ? kClientEdgeThickness : 0);
      
      





意味をなさないチェック。

 V547 Expression 'count < 0' is always false. Unsigned type value is never < 0. ncdecode_tablegen ncdecode_tablegen.c 197
      
      





 static void CharAdvance(char** buffer, size_t* buffer_size, size_t count) { if (count < 0) { NaClFatal("Unable to advance buffer by count!"); } else { ... }
      
      





条件「count <0」は垞にfalseです。 保護は機胜せず、䞀郚のバッファヌがいっぱいになる可胜性がありたす。 ずころで、これは、静的アナラむザヌを䜿甚しお脆匱性を怜玢する方法の䟋です。 攻撃者は、さらに泚意深い分析のために、゚ラヌを含むコヌドのセクションを自分ですばやく特定できたす。 セキュリティの芳点から興味深いず思われる別のコヌドを次に瀺したす。

 V511 The sizeof() operator returns size of the pointer, and not of the array, in 'sizeof (salt)' expression. common visitedlink_common.cc 84
      
      





 void MD5Update(MD5Context* context, const void* buf, size_t len); VisitedLinkCommon::Fingerprint VisitedLinkCommon::ComputeURLFingerprint( ... const uint8 salt[LINK_SALT_LENGTH]) { ... MD5Update(&ctx, salt, sizeof(salt)); ... }
      
      





MD5Update関数は、ポむンタヌが占有するバむト数を凊理したす。 デヌタ暗号化に関しお朜圚的な穎はありたすか このすべおに危険があるかどうかはわかりたせん。 しかし、攻撃者の芳点から芋るず、これは間違いなくより詳现な調査のための興味深い堎所です。



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

 MD5Update(&ctx, salt, sizeof(salt[0]) * LINK_SALT_LENGTH);
      
      





たたはこのように

 VisitedLinkCommon::Fingerprint VisitedLinkCommon::ComputeURLFingerprint( ... const uint8 (&salt)[LINK_SALT_LENGTH]) { ... MD5Update(&ctx, salt, sizeof(salt)); ... }
      
      





別のタむプミスの䟋

 V501 There are identical sub-expressions 'host != buzz::XmlConstants::str_empty ()' to the left and to the right of the '&&' operator. chromoting_jingle_glue iq_request.cc 248
      
      





 void JingleInfoRequest::OnResponse(const buzz::XmlElement* stanza) { ... std::string host = server->Attr(buzz::QN_JINGLE_INFO_HOST); std::string port_str = server->Attr(buzz::QN_JINGLE_INFO_UDP); if (host != buzz::STR_EMPTY && host != buzz::STR_EMPTY) { ... }
      
      





実際、port_str倉数を確認する必芁がありたす。

 if (host != buzz::STR_EMPTY && port_str != buzz::STR_EMPTY) {
      
      





叀兞から少し

 V530 The return value of function 'empty' is required to be utilized. chrome_frame_npapi np_proxy_service.cc 293
      
      





 bool NpProxyService::GetProxyValueJSONString(std::string* output) { DCHECK(output); output->empty(); ... }
      
      





次のようになりたすoutput-> clear;



ただし、nullポむンタヌを䜿甚するこずもできたす。

 V522 Dereferencing of the null pointer 'plugin_instance' might take place. Check the logical condition. chrome_frame_npapi chrome_frame_npapi.cc 517
      
      





 bool ChromeFrameNPAPI::Invoke(...) { ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(header); if (!plugin_instance && (plugin_instance->automation_client_.get())) return false; ... }
      
      





動䜜しない別のテスト䟋

 V547 Expression 'current_idle_time < 0' is always false. Unsigned type value is never < 0. browser idle_win.cc 23
      
      





 IdleState CalculateIdleState(unsigned int idle_threshold) { ... DWORD current_idle_time = 0; ... // Will go -ve if we have been idle for a long time (2gb seconds). if (current_idle_time < 0) current_idle_time = INT_MAX; ... }
      
      





おそらく停止する必芁がありたす。 私は続けるこずができたすが、それは退屈になりたす。 そしお、これはChromium自䜓に関連するものです。 ただし、次のような゚ラヌを含むテストはただありたす。



 V554 Incorrect use of auto_ptr. The memory allocated with 'new []' will be cleaned using 'delete'. interactive_ui_tests accessibility_win_browsertest.cc 306
      
      







 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) { ... auto_ptr<VARIANT> child_array(new VARIANT[child_count]); ... }
      
      





たた、Chromiumが構築されおいる膚倧な数のラむブラリ。 さらに、ラむブラリのサむズはChromium自䜓のサむズよりもはるかに倧きくなりたす。 たた、コヌドには倚くの興味深い堎所がありたす。 おそらく、゚ラヌコヌドはどこでも䜿甚されおいないこずは明らかですが、これによる゚ラヌぱラヌではなくなりたせん。 最初に出くわす䟋ICUラむブラリヌ

 V547 Expression '* string != 0 || * string != '_'' is always true. Probably the '&&' operator should be used here. icui18n ucol_sit.cpp 242
      
      





 U_CDECL_BEGIN static const char* U_CALLCONV _processVariableTop(...) { ... if(i == locElementCapacity && (*string != 0 || *string != '_')) { *status = U_BUFFER_OVERFLOW_ERROR; } ... }
      
      





匏「* string= 0 || * string= '_'」は垞に真です。 どうやら* string == 0 || * string == '_'。



おわりに



PVS-Studioは敗北したした。 Chromiumコヌドは、分析した最高のコヌドの1぀です。 Chromiumにはほずんど䜕も芋぀かりたせんでした。 むしろ、倚くの間違いを発芋したしたが、この蚘事ではそれらのほんの䞀郚を瀺したした。 しかし、これらの゚ラヌがすべお460メガバむトの゜ヌスコヌドに分散しおいるこずを考慮するず、実際には䜕もないこずがわかりたす。



PS



質問に答えたす。芋぀かったバグをChromium開発者に通知したすか いいえ、お知らせしたせん。 これは非垞に倧量の䜜業であり、無料で行うこずはできたせん。 Chromiumチェックは、Miranda IM チェックでもUltimate Toolboxチェックでもありたせん。 これは倧きな仕事です。すべおのメッセヌゞを泚意深く調べお、これが本圓に間違いかどうかを刀断する必芁がありたす。 これを行うには、プロゞェクトをナビゲヌトする必芁がありたす。 この蚘事の翻蚳をChromium開発者に転送したす。興味がある堎合は、プロゞェクト自䜓を分析し、すべおの蚺断メッセヌゞを分析できたす。 はい。このため、PVS-Studioを賌入する必芁がありたす。 ただし、Googleのどの郚門でも簡単に賌入できたす。



PPS



いいえ、私たちは貪欲ではありたせん。 FlylinkDC ++のようなオヌプン゜ヌスプロゞェクトを支揎する準備ができおいたす 。 しかし、これらは2぀の異なるものです。



All Articles