2017幎のC ++プロゞェクトのトップ10゚ラヌ





写真1








ほが3か月間、2018幎はすでに窓の倖にありたした。぀たり、過去1幎間にC ++プロゞェクトでPVS-Studioアナラむザヌによっお怜出された䞊䜍10個の゚ラヌをコンパむルするずきが来たした少し遅れおいたす。 それでは始めたしょう



ご泚意 さらに興味がある堎合は、たずコヌドスニペットで゚ラヌを芋぀けおから、アナラむザヌの譊告ず説明を読んでください。 もっず面癜いず思いたす。



10䜍



゜ヌス メモ垳++5幎埌のコヌドチェック



最も有名なテキスト゚ディタの1぀であるNotepad ++のチェック䞭に゚ラヌが発芋されたした。



゚ラヌを含むコヌドスニペット



TCHAR GetASCII(WPARAM wParam, LPARAM lParam) { int returnvalue; TCHAR mbuffer[100]; int result; BYTE keys[256]; WORD dwReturnedValue; GetKeyboardState(keys); result = ToAscii(static_cast<UINT>(wParam), (lParam >> 16) && 0xff, keys, &dwReturnedValue, 0); returnvalue = (TCHAR) dwReturnedValue; if(returnvalue < 0){returnvalue = 0;} wsprintf(mbuffer, TEXT("return value = %d"), returnvalue); if(result!=1){returnvalue = 0;} return (TCHAR)returnvalue; }
      
      





PVS-Studioè­Šå‘Š  V560条件匏の䞀郚は垞に真です0xff。 babygrid.cpp 711



アナラむザヌは匏lParam >> 16&& 0xffが疑わしいず刀断したした 。 ToAscii関数に枡される2番目の匕数の倀は垞に0たたは1であり、結果の倀は巊の郚分匏- lParam >> 16のみに䟝存したす。 明らかに、&&挔算子の代わりに挔算子を䜿甚する必芁がありたした。



9䜍



出兞 Yandexの開発者に挚拶を送りたす



9䜍は、Yandexが開発したClickHouseプロゞェクトの゚ラヌです。



 bool executeForNullThenElse(....) { .... const ColumnUInt8 * cond_col = typeid_cast<const ColumnUInt8 *>(arg_cond.column.get()); .... if (cond_col) { .... } else if (cond_const_col) { .... } else throw Exception( "Illegal column " + cond_col->getName() + " of first argument of function " + getName() + ". Must be ColumnUInt8 or ColumnConstUInt8.", ErrorCodes::ILLEGAL_COLUMN); .... }
      
      





PVS-Studioè­Šå‘Š  V522ヌルポむンタヌ 'cond_col'の逆参照が行われる堎合がありたす。 FunctionsConditional.h 765



このコヌドでは、䟋倖をスロヌする必芁があるずきに゚ラヌ状況が誀っお凊理されたす。 cond_colポむンタヌに泚意しおください 。 そのために、 ifステヌトメントはポむンタヌがれロ以倖であるこずをチェックしたす。 䟋倖がスロヌされるelseブランチに制埡が到達した堎合、 cond_colポむンタヌは正確にnullです。 ただし、䟋倖メッセヌゞを生成する堎合、 cond_colは匏cond_col-> getNameで間接参照されたす。



8䜍



出兞 Firebird、MySQL、PostgreSQLのコヌド品質比范



8䜍は、Firebird、MySQL、PostgreSQLのコヌドの品質を比范したずきに、MySQLプロゞェクトで芋぀かった゚ラヌの1぀です。



゚ラヌを含むメ゜ッドのコヌド



 mysqlx::XProtocol* active() { if (!active_connection) std::runtime_error("no active session"); return active_connection.get(); }
      
      





PVS-Studioè­Šå‘Š  V596オブゞェクトは䜜成されたしたが、䜿甚されおいたせん。 「throw」キヌワヌドが欠萜しおいる可胜性がありたすthrow runtime_errorFOO; mysqlxtest.cc 509



アクティブな接続 Active_connection がない堎合は、 std :: runtime_errorタむプの䟋倖オブゞェクトが䜜成されたす...それだけです。 䜜成埌、メ゜ッドは単に削陀されたすが、メ゜ッドの実行は継続されたす。 明らかに、開発者は䟋倖をスロヌするためにthrowキヌワヌドを忘れおいたした。



7䜍



゜ヌス FreeBSDコヌドの56の朜圚的な脆匱性を䞀晩で芋぀ける方法



倕方に56の朜圚的な脆匱性を芋぀ける方法 もちろん、静的解析では



FreeBSDコヌドで芋぀かった問題の1぀



 int mlx5_core_create_qp(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp, struct mlx5_create_qp_mbox_in *in, int inlen) { .... struct mlx5_destroy_qp_mbox_out dout; .... err_cmd: memset(&din, 0, sizeof(din)); memset(&dout, 0, sizeof(dout)); din.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_QP); din.qpn = cpu_to_be32(qp->qpn); mlx5_cmd_exec(dev, &din, sizeof(din), &out, sizeof(dout)); return err; }
      
      





PVS-Studioè­Šå‘Š  V597コンパむラは、「dout」オブゞェクトのフラッシュに䜿甚される「memset」関数呌び出しを削陀できたした。 プラむベヌトデヌタを消去するには、memset_s関数を䜿甚する必芁がありたす。 mlx5_qp.c 159



匏memsetdout、0、sizeofdoutに泚意しおください。 開発者は、 doutに察応するメモリブロック内のデヌタを「消去」しお、倀をれロに蚭定したいず考えおいたした。 通垞、このアプロヌチは、メモリに「ハング」しないようにプラむベヌトデヌタをクリアする必芁がある堎合に䜿甚されたす。



ただし、 doutはそれ以䞊䜿甚されたせん sizeofdoutはカりントされたせん。これにより、コンパむラは䞊蚘のmemset関数呌び出しを削陀できたす。 このような最適化は、C / C ++の芳点から芋たプログラムの動䜜には圱響したせん。 その結果、クリアされるべきデヌタがメモリに残る堎合がありたす。



このトピックをさらに深く掘り䞋げるには、次の蚘事を読むこずをお勧めしたす。





6䜍



出兞 埅望のCryEngine Vテスト



このトップで取り䞊げるコヌド-CryEngineV。



 int CTriMesh::Slice(....) { .... bop_meshupdate *pmd = new bop_meshupdate, *pmd0; pmd->pMesh[0]=pmd->pMesh[1] = this; AddRef();AddRef(); for(pmd0=m_pMeshUpdate; pmd0->next; pmd0=pmd0->next); pmd0->next = pmd; .... }
      
      





PVS-Studioè­Šå‘Š  V529奇数セミコロン ';' 「for」挔算子の埌。 boolean3d.cpp 1314



このフラグメントがこのように曞き出されおいない堎合-短瞮され、コヌドの残りの郚分から分離されおいる堎合、アナラむザヌが芋぀けた疑わしいセクション-forルヌプを終了するシンボル ';'を芋぀けるのはそれほど簡単ではないこずに同意したす。 同時に、コヌドをフォヌマットする次の匏をシフトするこずも、シンボル ';'を瀺唆しおいたす。 ここは䞍芁です。匏pmd0-> next = pmd; サむクルの本䜓でなければなりたせん。 しかし、 forルヌプのロゞックから刀断するず、混乱を招くのはコヌドの誀ったフォヌマットであり、論理的な゚ラヌではありたせん。 ちなみに、CryEngineコヌドでは、コヌドのフォヌマットが修正されおいたす。



5䜍



出兞 Unreal Engine開発プロセスの䞀郚ずしおの静的分析。



Unreal Engineゲヌム゚ンゞンのコヌドでPVS-Studioが怜出した゚ラヌの修正䜜業䞭に、次の゚ラヌが発芋されたした。



 for(int i = 0; i < SelectedObjects.Num(); ++i) { UObject* Obj = SelectedObjects[0].Get(); EdObj = Cast<UEditorSkeletonNotifyObj>(Obj); if(EdObj) { break; } }
      
      





PVS-Studioè­Šå‘Š  V767ルヌプ内の定数むンデックスによる「SelectedObjects」配列の芁玠ぞの疑わしいアクセス。 skeletonnotifydetails.cpp 38



ルヌプでは、すべおの芁玠を調べお、その䞭でUEditorSkeletonNotifyObj型の最初の芁玠を芋぀けたいず考えたした。 しかし、ルヌプカりンタヌiの代わりにSelectedObjects [0] .Get匏で定数むンデックス0を䜿甚するこずにより、䞍幞なミスを犯したした。 その結果、最初の芁玠のみが垞にチェックされたす。



4䜍



出兞 Tizenオペレヌティングシステムの27,000゚ラヌ



Tizenオペレヌティングシステムずその䞭で䜿甚されおいるサヌドパヌティのコンポヌネントをチェックしおいるずきに゚ラヌが芋぀かりたした。 この蚘事は倧きく、倚くの興味深い゚ラヌ䟋が含たれおいたす-読むこずを匷くお勧めしたす。



ただし、特定の譊告に戻りたす。



 int _read_request_body(http_transaction_h http_transaction, char **body) { .... *body = realloc(*body, new_len + 1); .... memcpy(*body + curr_len, ptr, body_size); body[new_len] = '\0'; curr_len = new_len; .... }
      
      





PVS-Studioè­Šå‘Š  V527 「\ 0」倀が「char」型ポむンタヌに割り圓おられおいるのは奇劙です。 おそらく意味* body [new_len] = '\ 0'。 http_request.c 370



゚ラヌは匏本䜓[new_len] = '\ 0'にありたす。 bodyパラメヌタヌはそれぞれchar **型であり、匏body [new_len]の型はchar *であるこずに泚意しおください。 しかし、開発者は倧倱敗し、別の逆参照を忘れお、ポむンタヌに倀「\ 0」を曞き蟌もうずしたしたnullポむンタヌに倉換されたす。



これは2぀の問題に぀ながりたす。





正しいコヌドは次のずおりです。



 (*body)[new_len] = '\0';
      
      





3䜍



出兞 PVS-Studioは脆匱性の怜玢にどのように圹立ちたすか



だから私たちはトップ3に到達したした。 次のコヌドは、「PVS-StudioはCVE怜玢にどのように察凊したすか」ずいう質問ぞの回答を怜玢䞭に芋぀かりたした回答に぀いおは、䞊蚘の蚘事を参照しおください。 illumos-gateプロゞェクトのコヌド。



 static int devzvol_readdir(....) { .... char *ptr; .... ptr = strchr(ptr + 1, '/') + 1; rw_exit(&sdvp->sdev_contents); sdev_iter_datasets(dvp, ZFS_IOC_DATASET_LIST_NEXT, ptr); .... }
      
      





PVS-Studio è­Šå‘Š  V769 「strchrptr + 1、」/「+ 1」匏の「strchrptr + 1、」/「」ポむンタヌはnullptrである可胜性がありたす。 そのような堎合、結果の倀は無意味になり、䜿甚しないでください。



strchr関数は、最初の匕数で指定された文字列内の2番目の匕数で指定された文字の最初の出珟を指すポむンタヌを返したす。 そのような文字が芋぀からない堎合、 strchrはNULLを返したす 。 ただし、この事実は考慮されず、倀「1」が垞に戻り倀に远加されたす。 その結果、 ptrポむンタヌは垞にれロ以倖になりたす。぀たり、 ptr= NULLずいう圢匏をさらにチェックしおも、ポむンタヌの有効性に関する情報は埗られたせん。 その結果、特定の条件䞋で、このコヌドによりカヌネルパニックが発生したした。



識別子CVE-2014-9491はこの゚ラヌに割り圓おられたしたillumosのdevzvol_readdir関数はstrchr呌び出しの戻り倀をチェックしたせん。これにより、リモヌトの攻撃者が䞍特定のベクトルを介しおサヌビス拒吊 NULLポむンタヌの参照解陀ずパニックを匕き起こすこずができたす 。



CVE自䜓は2014幎に発芋されたずいう事実にもかかわらず、私たち自身の研究の過皋で、2017幎にこの゚ラヌを発芋したため、このトップになりたした。



2䜍



出兞 Unreal Engine開発プロセスの䞀郚ずしおの静的分析。



2番目にある゚ラヌが発芋されたした...はい、再びUnreal Engineで発芋されたした。 ずおも面癜かったので、抵抗できず、曞きたせんでした。



ご泚意 実際、アンリアル゚ンゞンに関する䞊蚘の蚘事からさらに2、3の゚ラヌを蚘述したすが、それでも同じプロゞェクトにあたり頻繁にアクセスしたくありたせん。 したがっお、䞊蚘の蚘事、特に譊告V714およびV709を自分で確認するこずを匷くお勧めしたす。



その埌、倚くのコヌドがありたすが、問題の本質を理解する必芁がありたす。



 bool FCreateBPTemplateProjectAutomationTests::RunTest( const FString& Parameters) { TSharedPtr<SNewProjectWizard> NewProjectWizard; NewProjectWizard = SNew(SNewProjectWizard); TMap<FName, TArray<TSharedPtr<FTemplateItem>> >& Templates = NewProjectWizard->FindTemplateProjects(); int32 OutMatchedProjectsDesk = 0; int32 OutCreatedProjectsDesk = 0; GameProjectAutomationUtils::CreateProjectSet(Templates, EHardwareClass::Desktop, EGraphicsPreset::Maximum, EContentSourceCategory::BlueprintFeature, false, OutMatchedProjectsDesk, OutCreatedProjectsDesk); int32 OutMatchedProjectsMob = 0; int32 OutCreatedProjectsMob = 0; GameProjectAutomationUtils::CreateProjectSet(Templates, EHardwareClass::Mobile, EGraphicsPreset::Maximum, EContentSourceCategory::BlueprintFeature, false, OutMatchedProjectsMob, OutCreatedProjectsMob); return ( OutMatchedProjectsDesk == OutCreatedProjectsDesk ) && ( OutMatchedProjectsMob == OutCreatedProjectsMob ); }
      
      





問題を理解するために必芁な次の重芁な点に泚意しおください。 倉数OutMatchedProjectsDesk 、 OutCreatedProjectsDesk 、およびOutMatchedProjectsMob 、 OutCreatedProjectsMobは、宣蚀時にれロで初期化され、 CreateProjectSetメ゜ッドに匕数ずしお枡されたす。



その埌、ステヌトメントステヌトメントreturnで倉数が比范されたす。 したがっお、 CreateProjectSetメ゜ッドは最埌の2぀の匕数を初期化する必芁がありたす。



それでは、゚ラヌを含むCreateProjectSetメ゜ッドを芋おみたしょう。



 static void CreateProjectSet(.... int32 OutCreatedProjects, int32 OutMatchedProjects) { .... OutCreatedProjects = 0; OutMatchedProjects = 0; .... OutMatchedProjects++; .... OutCreatedProjects++; .... }
      
      





PVS-Studioの譊告 

パラメヌタヌOutCreatedProjectsおよびOutMatchedProjectsは参照を䜜成するのを忘れ、その結果、察応する匕数の倀が単玔にコピヌされたす。 結果ずしお、䞊蚘のRunTestメ゜ッドの戻り倀は垞にtrueです 。これは、比范されるすべおの倉数が初期化䞭に指定された同じ倀-0を持っおいるためです。



正しいコヌドは次のずおりです。



 static void CreateProjectSet(.... int32 &OutCreatedProjects, int32 &OutMatchedProjects)
      
      





䞀䜍



゜ヌス 静的コヌド分析が倧奜き



この間違いを芋た途端、誰がトップをリヌドすべきかに぀いお疑う䜙地はありたせんでした。 䞀般的に、自分で芋おください。 䞊蚘のコヌドスニペットで゚ラヌが芋぀かるたで、問題の説明に決しおアクセスしないでください。 ずころで、プロゞェクト-StarEngine-は再びゲヌム゚ンゞンです。



 PUGI__FN bool set_value_convert( char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) { char buf[128]; sprintf(buf, "%d", value); return set_value_buffer(dest, header, header_mask, buf); }
      
      





さお、間違いを芋぀ける成功はどうですか :)



PVS-Studioè­Šå‘Š  V614未初期化バッファヌ 'buf'が䜿甚されたした。 'printf'関数の最初の実匕数を確認するこずを怜蚎しおください。 pugixml.cpp 3362



確かに質問がありたす「 printf コヌドでsprintf関数の呌び出しのみがある堎合、 printfアナラむザヌの譊告はどこから来たすか」



それが本質です sprintfは std :: printfに展開されるマクロです



 #define sprintf std::printf
      
      





その結果、初期化されおいないbufバッファヌがフォヌマット文字列ずしお䜿甚されたす。 すごいですね。 この間違いは、圓然のこずながら、1䜍になったず思いたす。



マクロ宣蚀を䜿甚しおヘッダヌファむルにリンクしたす 。



おわりに



収集されたバグをお楜しみください。 個人的には、圌らは私にずっお十分に興味深いようでした。 しかし、もちろん、あなたのビゞョンは私のビゞョンずは異なる可胜性がありたす。そのため、 ブログの蚘事を読むか、オヌプン゜ヌスプロゞェクトでPVS-Studioが怜出した゚ラヌのリストを芋お、「トップ10」を線集できたす。







写真2








たた、蚘事に蚘茉されおいるすべおの゚ラヌ および他の倚くの゚ラヌはPVS-Studioアナラむザヌを䜿甚しお怜出されたこずを思い出しおください。プロゞェクトで詊しおみるこずをお勧めしたす ダりンロヌドペヌゞぞのリンク 。











この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいセルゲむノァシリ゚フ。 2017幎のC ++プロゞェクトのバグのトップ10



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



All Articles