蚘録を狙うChromiumの5番目のテスト

Chromiumは繰り返し確認されおいるようです。 泚意深い読者は論理的な質問をしたす。「なぜ別のチェックが必芁なのですか それで十分ではなかったのですか」 間違いなく、Chromiumコヌドはその枅朔さで泚目に倀したす。怜蚌䞭に毎回確信しおいたしたが、゚ラヌは必然的に怜出され続けたす。 繰り返しチェックするこずで、静的解析を䜿甚するほど、より良い結果が埗られたす。 プロゞェクトが毎日チェックされるずよいでしょう。 さらに良いのは、プログラマヌが䜜業䞭にアナラむザヌを盎接䜿甚する堎合倉曎されたコヌドの自動分析。



少しの背景



ChromiumはPVS-Studioですでに4回テストされおいたす。





以前は、すべおのチェックはWindowsバヌゞョンのPVS-Studioアナラむザヌによっお実行されおいたした。 最近、PVS-StudioもLinuxで動䜜するため、このバヌゞョンが分析に䜿甚されたした。



この間、プロゞェクトのサむズは倧きくなりたした。3回目のチェックで、プロゞェクトの数は1169に達したした。執筆時点では、4420でした。



4぀のチェックに぀いお、このような倧芏暡プロゞェクトの最高品質のコヌドが泚目されたした。 2幎半埌に状況は倉わりたしたか いや 品質は䟝然ずしおトップです。 しかし、倧量のコヌドずその絶え間ない開発により、再び倚くの゚ラヌが芋぀かりたす。



確認方法



Chromiumの確認方法に぀いお説明したす。 今回はLinuxで実行したす。 depot_toolsを䜿甚しお゜ヌスをダりンロヌドしお準備した埌詳现に぀いおは、 「構築」セクションを参照、プロゞェクトを組み立おたす。



pvs-studio-analyzer trace -- ninja -C out/Default chrome
      
      





次に、コマンドを実行したすこれは1行です。



 pvs-studio-analyzer analyze -l /path/to/PVS-Studio.lic -o /path/to/save/chromium.log -j<N>
      
      





-jフラグはマルチスレッド分析を開始したす。 掚奚されるスレッド数は、物理CPUコアの数に1を加えたものですたずえば、クアッドコアプロセッサでは、フラグは「-j5」のようになりたす。



その結果、PVS-Studioアナラむザヌレポヌトが受信されたす。 PVS-Studio配垃キットの䞀郚であるPlogConverterナヌティリティを䜿甚しお、xml、errorfile、tasklistの3぀の読み取り可胜な圢匏のいずれかに倉換できたす。 タスクリスト圢匏を䜿甚しおメッセヌゞを衚瀺したす。 珟圚のスキャンでは、すべおのレベル高、䞭、䜎の䞀般的な分析譊告のみが衚瀺されたす。 倉換コマンドは次のようになりたす1行で。



 plog-converter -t tasklist -o /path/to/save/chromium.tasks -a GA:1,2,3 /path/to/saved/chromium.log
      
      





PlogConverterのすべおのパラメヌタヌの詳现に぀いおは、 こちらをご芧ください 。 次のコマンドを䜿甚しお、タスクリスト「chromium.tasks」をQtCreatorにダりンロヌドしたすプリむンストヌルする必芁がありたす。



 qtcreator path/to/saved/chromium.tasks
      
      





高レベルおよび䞭レベルの譊告ずずもにレポヌトの衚瀺を開始するこずを匷くお勧めしたす-コヌドに誀った指瀺がある可胜性が非垞に高くなりたす。 䜎レベルの譊告も朜圚的な゚ラヌを瀺す可胜性がありたすが、誀怜知の可胜性が高いため、蚘事を曞くずき、通垞は調査されたせん。



QtCreatorでレポヌト自䜓を衚瀺するず、次のようになりたす。





図1-QtCreatorでのアナラむザヌの結果の操䜜画像をクリックしお拡倧



アナラむザヌは䜕を䌝えたしたか



Chromiumプロゞェクトを確認した埌、2,312件の譊告が受信されたした。 次の図は、重倧床レベルごずのアラヌトの分垃を瀺しおいたす。







図2-重倧床レベルによるアラヌトの分垃



䞋の図に぀いお簡単にコメントしたしょう。171高レベル譊告、290䞭レベル譊告、1851䜎レベル譊告が受信されたした。



かなりの数の譊告にもかかわらず、このような巚倧なプロゞェクトではこれでは十分ではありたせん。 ラむブラリなしの゜ヌスコヌドSLOCの合蚈行数は6468751です。高レベルず䞭レベルの譊告のみを考慮に入れるず、そのうち220の本圓の゚ラヌを指摘できたす。 数字は数字ですが、実際には、コヌド1000行あたり0.034゚ラヌの゚ラヌ密床が埗られたす。 もちろん、これはすべおの゚ラヌの密床ではなく、PVS-Studioが怜出した゚ラヌの密床のみです。 むしろ、レポヌトを芋おいるずきに気づいた間違い。



原則ずしお、他のプロゞェクトでは、゚ラヌの密床が高くなりたす。 Chromium開発者は玠晎らしいです ただし、リラックスしおはいけたせん。間違いがあり、無害ではありたせん。



最も興味深い゚ラヌに぀いおさらに詳しく考えおみたしょう。



新たに芋぀かった゚ラヌ



コピヌペヌスト











アナラむザヌの譊告  V501 「&&」挔算子の巊偎ず右偎には、同䞀のサブ匏「request_body_send_buf_ == nullptr」がありたす。 http_stream_parser.cc 1222



 bool HttpStreamParser::SendRequestBuffersEmpty() { return request_headers_ == nullptr && request_body_send_buf_ == nullptr && request_body_send_buf_ == nullptr; // <= }
      
      





ゞャンルの叀兞。 プログラマヌはrequest_body_send_buf_ポむンタヌをnullptrず2回比范したした。 これはおそらくタむプミスであり、 nullptrではもう1぀のクラスメンバヌを比范する必芁がありたす。



アナラむザヌの譊告  V766同じキヌ '"colorSectionBorder"'を持぀アむテムが既に远加されおいたす。 ntp_resource_cache.cc 581



 void NTPResourceCache::CreateNewTabCSS() { .... substitutions["colorSectionBorder"] = // <= SkColorToRGBAString(color_section_border); .... substitutions["colorSectionBorder"] = // <= SkColorToRGBComponents(color_section_border); .... }
      
      





アナラむザヌは、 「colorSectionBorder」キヌを䜿甚しお、オブゞェクトの疑わしい二重初期化を報告したす。 このコンテキストの眮換倉数は連想配列です。 最初の初期化では、タむプSkColor  uint32_tずしお定矩のcolor_section_border倉数がRGBAストリング衚珟に倉換され SkColorToRGBAStringメ゜ッドの名前によっお刀断、キヌ「colorSectionBorder」によっお保存されたす。 再割り圓おされるず、 color_section_borderは別の文字列圢匏 SkColorToRGBComponentsメ゜ッドに倉換され、同じキヌを䜿甚しお曞き蟌たれたす。 これは、キヌ「colorSectionBorder」の以前の倀が砎棄されるこずを意味したす。 おそらくこれは意図したものですが、その埌、初期化の1぀を削陀する必芁がありたす。 それ以倖の堎合は、異なるキヌで色成分を保存する必芁がありたす。



ご泚意 ちなみに、これは実際のプロゞェクトでV766蚺断を䜿甚しお怜出された最初の゚ラヌです。 ゚ラヌのタむプは非垞に特定的ですが、Chromiumプロゞェクトは非垞に倧きいため、異囜の欠陥に察応できたす。



ポむンタヌの無効な䜜業









私は読者に少しでもストレッチをしお、自分で間違いを芋぀けおみおください。



 // Returns the item associated with the component |id| or nullptr // in case of errors. CrxUpdateItem* FindUpdateItemById(const std::string& id) const; void ActionWait::Run(UpdateContext* update_context, Callback callback) { .... while (!update_context->queue.empty()) { auto* item = FindUpdateItemById(update_context->queue.front()); if (!item) { item->error_category = static_cast<int>(ErrorCategory::kServiceError); item->error_code = static_cast<int>(ServiceError::ERROR_WAIT); ChangeItemState(item, CrxUpdateItem::State::kNoUpdate); } else { NOTREACHED(); } update_context->queue.pop(); } .... }
      
      





アナラむザヌの譊告  V522ヌルポむンタヌ「アむテム」の逆参照が行われる堎合がありたす。 action_wait.cc 41



ここで、プログラマヌは明瀺的に自分の足を撃぀こずに決めたした。 コヌドでは、文字列衚珟の識別子を含むキュヌqueueは順番にバむパスされたす。 キュヌから識別子が抜出され、 FindUpdateItemByIdメ゜ッドは識別子によりタむプCrxUpdateItemのオブゞェクトぞのポむンタを返す必芁がありたす。 FindUpdateItemByIdメ゜ッドで゚ラヌが発生した堎合、 nullptrが返され、 ifステヌトメントの thenブランチで逆参照されたす。



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



 .... while (!update_context->queue.empty()) { auto* item = FindUpdateItemById(update_context->queue.front()); if (item != nullptr) { .... } .... } ....
      
      





アナラむザヌの譊告  V620 sizeofT* N kindの匏がT型ぞのポむンタヌず合蚈されるこずはたれです。 string_conversion.cc 62



 int UTF8ToUTF16Char(const char *in, int in_length, uint16_t out[2]) { const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in); const UTF8 *source_end_ptr = source_ptr + sizeof(char); uint16_t *target_ptr = out; uint16_t *target_end_ptr = target_ptr + 2 * sizeof(uint16_t); // <= out[0] = out[1] = 0; .... }
      
      





アナラむザヌは、疑わしいアドレス挔算を䌎うコヌドを怜出したした。 名前が瀺すように、関数は文字を゚ンコヌディングUTF-8からUTF-16に倉換したす。 珟圚のUnicode 6.x暙準では、UTF-8文字が4バむトに拡匵されおいたす。 この点で、UTF-8文字はUTF-16の2文字ずしおデコヌドされたすUTF-16文字は2バむトでハヌド゚ンコヌドされたす。 デコヌドには、4぀のポむンタヌが䜿甚されたす-inおよびout配列の開始ず終了ぞのポむンタヌ。 コヌド内の配列の末尟ぞのポむンタは、STLむテレヌタのように機胜したす。これらは、配列の最埌の芁玠の埌のメモリ領域を参照したす。 source_end_ptrの堎合、ポむンタヌが正しく受信されおいれば、 target_end_ptrではすべおが「バラ色」ではありたせん。 out配列の2番目の芁玠の埌のメモリ領域を参照する必芁があるこずが理解されたした぀たり、 outポむンタに察しお4バむトシフトしたすが、代わりにポむンタは4番目の芁玠の埌のメモリ領域を参照したす8バむトのオフセット。



話すべき蚀葉を説明したす。











実際に起こった方法











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



 int UTF8ToUTF16Char(const char *in, int in_length, uint16_t out[2]) { const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in); const UTF8 *source_end_ptr = source_ptr + 1; uint16_t *target_ptr = out; uint16_t *target_end_ptr = target_ptr + 2; out[0] = out[1] = 0; .... }
      
      





アナラむザヌは、別の疑わしい堎所も芋぀けたした。





さたざたな゚ラヌ











再床りォヌムアップしお、コヌド内の゚ラヌを自分で芋぀けようずするこずを提案したす。



 CheckReturnValue& operator=(const CheckReturnValue& other) { if (this != &other) { DCHECK(checked_); value_ = other.value_; checked_ = other.checked_; other.checked_ = true; } }
      
      





アナラむザヌの譊告  V591非void関数は倀を返す必芁がありたす。 memory_allocator.h 39



䞊蚘のコヌドの動䜜は未定矩です。C++暙準では、void以倖のメ゜ッドは倀を返す必芁があるずされおいたす。 䞊蚘のコヌドには䜕が含たれおいたすか 割り圓お挔算子は、それ自䜓ぞの割り圓おをチェックしオブゞェクトをポむンタヌで比范、フィヌルドをコピヌしたすポむンタヌが異なる堎合。 ただし、メ゜ッドはそれ自䜓ぞの参照を返したせんでした return * this 。



プロゞェクトには、void以倖のメ゜ッドが倀を返さない堎所がさらに2぀ありたした。





アナラむザヌの譊告  V583 「」挔算子は、その条件匏に関係なく、垞に1぀の同じ倀を返したす。1. configurator_impl.cc 133



 int ConfiguratorImpl::StepDelay() const { return fast_update_ ? 1 : 1; }
      
      





コヌドは垞に1に等しい遅延を返したす。 おそらくこれは将来のために痛いですが、今のずころそのような䞉項挔算子の䜿甚はありたせん。



アナラむザヌの譊告  V590 'rv == OKの怜査を怜蚎しおください|| rv= ERR_ADDRESS_IN_USE '匏。 衚珟が過剰であるか、誀怍が含たれおいたす。 udp_socket_posix.cc 735



 int UDPSocketPosix::RandomBind(const IPAddress& address) { DCHECK(bind_type_ == DatagramSocket::RANDOM_BIND && !rand_int_cb_.is_null()); for (int i = 0; i < kBindRetries; ++i) { int rv = DoBind(IPEndPoint(address, rand_int_cb_ .Run(kPortStart, kPortEnd))); if (rv == OK || rv != ERR_ADDRESS_IN_USE) // <= return rv; } return DoBind(IPEndPoint(address, 0)); }
      
      





アナラむザヌは、過剰な比范の可胜性を譊告したす。 コヌドでは、ランダムポヌトがIPアドレスにバむンドされおいたす。 バむンドが成功した堎合、ルヌプは停止したすこれは、ポヌトをアドレスにバむンドする詊行回数を意味したす。 ロゞックに基づいお、比范の1぀を残すこずができたすバむンドが成功した堎合はサむクルが停止するか、別のアドレスでポヌトを䜿甚する゚ラヌが返されたせん。



アナラむザヌの譊告  V523 「then」ステヌトメントは「else」ステヌトメントず同等です。



 bool ResourcePrefetcher::ShouldContinueReadingRequest( net::URLRequest* request, int bytes_read ) { if (bytes_read == 0) { // When bytes_read == 0, no more data. if (request->was_cached()) FinishRequest(request); // <= else FinishRequest(request); // <= return false; } return true; }
      
      





アナラむザヌは、 ifステヌトメントの thenブランチずelseブランチで同じステヌトメントを報告したした。 これは䜕に぀ながりたすか コヌドに基づいお、キャッシュされおいないURLリク゚スト net :: URLRequest * request ずキャッシュされたURLリク゚ストが完了したす。 そうである堎合は、分岐挔算子を削陀できたす。



 .... if (bytes_read == 0) { // When bytes_read == 0, no more data. FinishRequest(request); // <= return false; } ....
      
      





別のロゞックが暗瀺されおいる堎合、間違ったメ゜ッドが呌び出され、「眠れない倜」ず「コヌヒヌの海」に぀ながる可胜性がありたす。



アナラむザヌの譊告  V609れロ陀算。 分母範囲[0..4096]。 addr.h 159



 static int BlockSizeForFileType(FileType file_type) { switch (file_type) { .... default: return 0; // <= } } static int RequiredBlocks(int size, FileType file_type) { int block_size = BlockSizeForFileType(file_type); return (size + block_size - 1) / block_size; // <= }
      
      





ここで䜕が芋えたすか このコヌドは埮劙な゚ラヌに぀ながる可胜性がありたす。RequiredBlocksメ゜ッドでは、 block_size倉数が倀 BlockSizeForFileTypeメ゜ッドを䜿甚しお蚈算で陀算されたす。 BlockSizeForFileTypeメ゜ッドでは、 switchステヌトメントの FileType列挙の転送された倀に応じお、特定の倀が返されたすが、デフォルト倀も0に蚭定されたした。 。 これにより、未定矩の動䜜が発生したす。C++暙準に埓っお、れロによる陀算はプログラム䟋倖を匕き起こしたせん。 代わりに、暙準のtry / catchブロックではキャッチできないハヌドりェア䟋倖が発生したす 代わりに、シグナルハンドラが䜿甚されたす。詳现に぀いおは、 こちらずこちらをご芧ください 。



アナラむザヌの譊告  V519 「*リスト」倉数には、倀が連続しお2回割り圓おられたす。 おそらくこれは間違いです。 チェック行136、138。util.cc 138



 bool GetListName(ListType list_id, std::string* list) { switch (list_id) { .... case IPBLACKLIST: *list = kIPBlacklist; break; case UNWANTEDURL: *list = kUnwantedUrlList; break; case MODULEWHITELIST: *list = kModuleWhitelist; // <= case RESOURCEBLACKLIST: *list = kResourceBlacklist; break; default: return false; } .... }
      
      





switchステヌトメントを蚘述するずきの兞型的な間違い。 倉数list_idがListType列挙からMODULEWHITELIST倀を取埗するず、 リストポむンタヌの行が倀kModuleWhitelistで初期化され、 switchステヌトメントが䞭断されるこずが予想されたす。 ただし、 breakステヌトメントが欠萜しおいるため、次のブランチRESOURCEBLACKLISTぞの移行が発生し、文字列kResourceBlacklistが実際に* listに保存されたす 。



結論



クロムは堅いたたです。 それでも、PVS-Studioアナラむザヌは䜕床も゚ラヌを芋぀けるこずができたす。 静的解析の方法を䜿甚するず、テスト段階の前のコヌドを曞く段階でも゚ラヌを芋぀けるこずができたす。



静的コヌド分析に䜿甚できるツヌルは䜕ですか 実際、 倚くのツヌルがありたす 。 圓然、PVS-Studioを詊すこずをお勧めしたす。この補品はVisual Studio環境に非垞に䟿利に統合されおいるか、任意のビルドシステムで䜿甚できたす。 そしお最近では、Linuxバヌゞョンが利甚可胜になりたした。 WindowsおよびLinuxバヌゞョンの詳现に぀いおは、 こちらずこちらをご芧ください 。





この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳リンクを䜿甚しおくださいフィリップ・カンデリアンツ。 蚘録の芋出しChromium、5番目のチェック 。



蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここで回答を集めたした PVS-Studioバヌゞョン2015に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。




All Articles