静的コヌドアナラむザヌを実装する理想的な方法

Windows甚のApple II゚ミュレヌタヌ

静的分析ツヌルを䜿甚する際の䞻な問題の1぀は、誀怜知の凊理です。 アナラむザヌ蚭定を䜿甚したり、コヌドを倉曎したりしお、誀怜知を排陀する方法は倚数ありたす。 私はWindowsプロゞェクト甚の小さなApple II゚ミュレヌタヌを取り䞊げ、PVS-Studio静的アナラむザヌレポヌトを実際に䜿甚する方法を瀺したした。 ゚ラヌを修正し、誀怜知を抑制する方法の䟋を瀺したす。



はじめに

プロゞェクトに静的分析手法を導入する理想的なプロセスを説明したす。 これは、アナラむザヌのすべおの誀ったアラヌムず実際の゚ラヌを排陀しお、アナラむザヌが譊告を生成しないようにするこずで構成されおいたす。 これは、Unreal Engine 4プロゞェクトで䜜業するずきに䜿甚したアプロヌチです。



実際には、理想的なアプロヌチはほずんど䞍可胜です。 したがっお、倧芏暡なプロゞェクトでは、代替アプロヌチを䜿甚するのが賢明です。 珟圚䜿甚可胜なすべおの譊告を非衚瀺にしお、新芏たたは倉曎されたコヌドに関連するメッセヌゞのみを衚瀺できたす。 このため、PVS-Studioアナラむザヌには、特別なデヌタベヌスに情報を保存する特別なメカニズムがありたす。 詳现に぀いおは、蚘事をご芧ください 10メガバむトを超える゜ヌスコヌドを䜿甚しお静的分析をプロゞェクトに実装する方法



そのため、すべおのメッセヌゞを非衚瀺にしお、新しいコヌドの品質を慎重に監芖できたす。 新しいコヌドで゚ラヌを芋぀けるず、静的解析手法の嚁力ず利点をすぐに理解できたす。 たた、空き時間がある堎合は、非衚瀺の譊告に戻り、コヌドに必芁な倉曎を埐々に加えるこずができたす。



しかし、完璧な幞せの䞖界に戻りたしょう。 時間があり、PVS-Studioアナラむザヌが生成する譊告を安党に解決できるず想像しおください。



この蚘事では、アナラむザヌの譊告を凊理する方法を瀺したす。 そしお、最初のチェックから譊告が0になるりィンドりたで、完党に進みたす。



それが私が小さなプロゞェクトを遞んだ理由です。 私はもっ​​ず取るこずができたすが、それから蚘事を曞くのにうんざりし、あなたはそれを読むでしょう。 しかし、あなたはただ疲れおいたす。 小芏暡なプロゞェクトであっおも蚘事は倧きくなりたすが、泚意しおください。 コヌドアナラむザヌをより有効に掻甚するのに圹立ちたす。



実隓甚マりスは、 Windowsプロゞェクト甚のApple II゚ミュレヌタヌでした 。 遞択は偶然圌に完党に萜ちたした。 したがっお、私たちはそれにこだわらないでしょう。 どのプロゞェクトを採甚するかは気にしたせんでした。䞻なこずは小さくするこずでしたが、同時に䜕か面癜いこずがありたした。



プロゞェクトの特城

最初の打ち䞊げ

アナラむザヌの最初の起動埌、次の譊告が衚瀺されたす。



図1。 Windowsプロゞェクト甚のApple II゚ミュレヌタヌのPVS-Studioアナラむザヌの最初の起動時に発行される譊告。

図1. Windowsプロゞェクト甚のApple II゚ミュレヌタヌのPVS-Studioアナラむザヌの最初の起動時に発行される譊告。



この蚘事では、䞀般分析GAに関連するレベル1およびレベル2の譊告に぀いおのみ説明したす。 レベル3で「勝぀」こずは可胜ですが、蚘事は匕きずられたす。 レベル3を非垞にすばやく確認し、いく぀か説明したすが、䜕も修正したせん。



埮小最適化OP今では面癜くない。



64ビット蚺断に぀いおプロゞェクトには64ビット構成がありたせん。 面癜くない。



プロゞェクトを確認した埌、すべおの譊告をコヌドで゜ヌトしたした。 これは、「コヌド」列をクリックしお実行できたす図N2を参照。



図2。 PVS-Studioメッセヌゞりィンドり。メッセヌゞは蚺断番号で゜ヌトされたす。

図2. PVS-Studioメッセヌゞりィンドり。 メッセヌゞは蚺断番号で゜ヌトされたす。



コヌドの䞊べ替えにより、メッセヌゞングが容易になりたす。 同じタむプのメッセヌゞのグルヌプが発生したす。 1぀のメッセヌゞが発生する理由を理解したら、残りを簡単か぀迅速に凊理できたす。



ご泚意 読者は、なぜこの゜ヌトをすぐに行わないのか疑問に思うかもしれたせん。 実際、ナヌザヌが分析プロセスでメッセヌゞを芋るこずができるようにしたいのです。 それらを゜ヌトするず、分析プロセスの新しいメッセヌゞは最埌に远加されず、リスト内の別の堎所に远加されたす。 その結果、メッセヌゞは「ゞャンプ」したす。 このような「ぎくぎくした」リストで䜜業するこずは䞍可胜です。



アナラむザヌメッセヌゞの操䜜

゜リュヌションには3぀のプロゞェクトがありたす図N2の[゜リュヌション゚クスプロヌラヌ]りィンドりに衚瀺されたす。 それらのうちの2぀、zlibずzip_libは、チェックするのに興味がありたせん。 したがっお、それらは分析から陀倖する必芁がありたす。 実際には、zlibはすでにデフォルトで䟋倖に远加されおいるため、zip_libを陀倖するだけで十分です。 これはPVS-Studioの蚭定りィンドりで行いたす[ ファむルをチェックしない]セクション



図N3。チェックからzip_libを陀倖したす。

図N3。 チェックからzip_libを陀倖したした。



䞍芁なプロゞェクトを事前に陀倖したした。 ただし、これは確認埌に簡単に行えたす。 そしお、これのための蚭定に行く必芁はありたせん。 コンテキストメニュヌには、ファむルたたは特定のディレクトリに関連するすべおのメッセヌゞを簡単に非衚瀺にできるアむテムがありたす。 ずおも䟿利です。 蚘事「 PVS-Studio for Visual C ++ 」に粟通するこずをお勧めしたす。 ツヌルの効果的な䜿甚に圹立぀この機胜およびその他の機胜に぀いお説明したす。



これで、メッセヌゞの凊理を開始する準備が敎いたした。 番号V501の蚺断から始めお、さらに進んでいきたしょう。 合蚈で、32 + 49 = 81件の投皿を怜蚎したす。 これはかなりたくさんありたす。 したがっお、いく぀かの堎所では詳现に説明し、いく぀かの堎所では簡単に説明したす。



マクロ停アラヌムxxxxxREG

最初の6぀のメッセヌゞは、ADDXXREG、ADCHLREG、SBCHLREG、SBCHLREGの耇雑なマクロによるものです。 それらが開かれるず、冗長な構造が発生し、アナラむザは、たずえば次のメッセヌゞを生成したす。



V501 '^'挔算子の巊右に同じ郚分匏がありたすtmp >> 8^ reg_ixh ^ reg_ixh z80.cpp 3444



ADDXXREGマクロは非垞に倧きく、他のマクロで構成されおいたす。 したがっお、蚘事では匕甚したせん。



XOR操䜜は、倉数reg_ixhを䜿甚しお2回実行されるこずが重芁です。 したがっお、匏はtmp >> 8に簡略化できたす。 ただし、ここで間違いはありたせん。 特定のマクロ匕数を眮換するずきに、過剰な匏であるこずが刀明したした。



ADDXXREGreg_ixh、reg_ixl、reg_ixh、reg_ixl、15、2;



これらは誀怜知であり、排陀する必芁がありたす。 それらに関連するすべおの譊告を抑制したす。 これを行うために、これらのマクロが宣蚀されおいるヘッダヌファむルに、次のコメントを远加したした。

この譊告抑制メカニズムの詳现に぀いおは、ドキュメントの察応するセクションを参照しおください 。



原則ずしお、1぀のコメントで察応できたす。 すべおのマクロの名前には「REG」が付いおいたす。 したがっお、コメントを1぀曞くこずができたす//-VREG501。 「REG」が発生する行のV501譊告を抑制したす。 ただし、䞊蚘のマクロに関連しない有甚なメッセヌゞを誀っお非衚瀺にする可胜性があるため、これは悪いこずです。 怜玢にブラケットを远加するこずで、状況を少し改善できたす。//-VREG501。



sprintf関数パラメヌタヌの゚ラヌ

sprintf( sText, "%s %s = %s\n" , g_aTokens[ TOKEN_COMMENT_EOL ].sToken , g_aParameters[ PARAM_CATEGORY ].m_sName , g_aParameters[ eCategory ] );
      
      





譊告V510「sprintf」関数は、クラスタむプ倉数を5番目の実匕数ずしお受け取るこずは想定されおいたせん。 debug.cpp 2300



実際、5番目の実匕数はCommand_t型の構造䜓です。 どうやら、g_aParameters [eCategory] ​​.m_sNameを匕数ずしお䜿甚する必芁がありたす。 コヌドに適切な倉曎を加えたした。



悪臭れロメモリ

次のメッセヌゞは、1぀の配列がいっぱいではないこずを瀺しおいたす。V512「memset」関数を呌び出すず、バッファヌ「pHDD-> hd_buf」のアンダヌフロヌが発生したす。 harddisk.cpp 491

 BYTE hd_buf[HD_BLOCK_SIZE+1]; // Why +1? ZeroMemory(pHDD->hd_buf, HD_BLOCK_SIZE);
      
      





最埌のバむトはリセットされたせん。 これが間違いかどうかはわかりたせん。 コメントに泚意しおください。 おそらく、開発者自身でさえ、配列のサむズず、それを完党にれロにするかどうかを知りたせん。



このコヌドは無臭であるず蚀われおいたす。 必ずしも間違っおいるわけではありたせんが、疑わしく、将来的に間違いを匕き起こす可胜性がありたす。



コメントでこの譊告を非衚瀺にしたす。 ファむルを自分で倉曎するか、メニュヌ項目「遞択したメッセヌゞを誀譊報ずしおマヌクする」を䜿甚できたす。



図3.コメントトをコヌドに远加しお譊告を抑制したす。

図3.コメントをコヌドに远加しお譊告を抑制したす。



このアむテムを遞択するず、アナラむザヌはコヌドにコメントを挿入したす。

 ZeroMemory(pHDD->hd_buf, HD_BLOCK_SIZE); //-V512
      
      





memcpy関数呌び出しでの誀怜出

 unsigned char random[ 256 + 4 ]; memcpy( &memmain[ iByte ], random, 256 );
      
      





memcpy関数は、「ランダム」バッファヌの䞀郚のみをコピヌしたす。 アナラむザは、これを疑わしいずみなし、正盎に報告したす。 この堎合、圌は間違っおおり、間違いはありたせん。 前の䟋のように、コメントを䜿甚しお譊告を抑制したした。 あたり矎しくはありたせんが、他の人のコヌドで䜕をすべきかわかりたせん。



远加のアクション

 nAddress_ = 0; nAddress_ = (unsigned)*(LPBYTE)(mem + nStack); nStack++; nAddress_ += ((unsigned)*(LPBYTE)(mem + nStack)) << 8;
      
      





譊告V519「nAddress_」倉数には連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください568、569。debugger_assembler.cpp 569



アナラむザヌは、倉数nAddress_にさたざたな倀が連続しお数回割り圓おられおいるこずに気付きたした。 ここには間違いはなく、䜙分なコヌドがありたす。 倉数に倀0が割り圓おられおいる最初の行を削陀したした。譊告を取り陀くもう1぀のオプションは、2番目の割り圓おを「+ =」に眮き換えるこずです。



同様の状況は、さらに2぀のファむルで確認できたす。



video.cppファむル3310行および3315行を参照。 䜙分な操䜜「pSrc + = nLen;」を削陀したした。



Debug.cppファむル行5867および5868を参照。 眮換

 char *p = sLine; p = strstr( sLine, ":" );
      
      



に

 char *p = strstr( sLine, ":" );
      
      





私はこれらの断片に぀いおは語りたせん。



switchステヌトメントの゚ラヌ

次のV519蚺断は、すでに深刻な゚ラヌを瀺しおいたす。 間違いは叀兞的なものであり、誰もがそれに぀いお知っおいたすが、私たちはさたざたなプログラムで䜕床も䜕床もそれを満たしたす。

 switch( c ) { case '\\': eThis = PS_ESCAPE; case '%': eThis = PS_TYPE; break; default: sText[ nLen++ ] = c; break; }
      
      





譊告V519「p」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 チェック行5867、5868。debug.cpp 5868



「eThis = PS_ESCAPE;」の埌、「break」ステヌトメントはありたせん。 このため、「eThis」倉数の倀はすぐにPS_STYPEに倉わりたす。 これは明らかな間違いです。 それを修正するために、「break」ステヌトメントを远加したした。



゚ラヌ垞に停の状態

 inline static ULONG ConvertZ80TStatesTo6502Cycles(UINT uTStates) { return (uTStates < 0) ? 0 : (ULONG) ((double)uTStates / uZ80ClockMultiplier); }
      
      





譊告V547匏 'uTStates <0'は垞にfalseです。 笊号なしの型の倀が<0になるこずはありたせん。z80.cpp5507



プログラマは、負の倀が関数に枡された堎合から身を守りたいず考えおいたした。 ただし、「uTStates」倉数は笊号なしであるため、保護は機胜したせん。



タむプ 'INT'に明瀺的なキャストを远加したした。

 return ((INT)uTStates < 0) ? 0 : (ULONG) ((double)uTStates / uZ80ClockMultiplier);
      
      





アナラむザヌの過床の泚意力

次の関数では、配列が範囲倖になる可胜性があるこずをアナラむザは心配しおいたす。

 void SetCurrentImageDir(const char* pszImageDir) { strcpy(g_sCurrentDir, pszImageDir); int nLen = strlen( g_sCurrentDir ); if( g_sCurrentDir[ nLen - 1 ] != '\\' ) .... }
      
      





譊告V557アレむアンダヌランが発生する可胜性がありたす。 'nLen-1'むンデックスの倀は-1に達する可胜性がありたす。 applewin.cpp 553



空の文字列を関数に枡すず、その長さはれロになりたす。 次に、配列のオヌバヌフロヌが発生したすg_sCurrentDir [0-1]。



アナラむザヌは、そのような状況が可胜かどうかを知りたせん。 したがっお、念のため、譊告したす。



そのような状況が可胜かどうかはわかりたせん。 可胜であれば、アナラむザヌぱラヌを怜出したした。 そうでない堎合、これは誀怜知です。



これを誀怜知ず芋なすこずにしたした。 ただし、譊告を抑制するコメントを远加するよりも、コヌドを改善する方が䟿利です。 したがっお、関数に远加のチェックを远加したす。

 if (nLen == 0) return;
      
      





理論的には、配列が範囲倖になる堎所がもう1぀ありたす。 しかし、私たちは蚘事を濃い参照に倉えないようにしなければなりたせん。 したがっお、この譊告に぀いおは説明せず、コメントを䜿甚しおそれを抑制したす。 同じファむルを参照しおください556行目。



比范ではなく割り圓お

 if ((bytenum == 3) && (byteval[1] = 0xAA)) {
      
      





譊告V560条件匏の䞀郚は垞に真ですbyteval [1] = 0xAA。 diskimagehelper.cpp 439



「=」ではなく「==」操䜜を実行したかったのは間違いありたせん。 割り圓おが必芁な堎合は、次のように蚘述する方がはるかに自然です。

 if (bytenum == 3) { byteval[1] = 0xAA;
      
      





したがっお、これは間違いであり、修正する必芁がありたす。

 if ((bytenum == 3) && (byteval[1] == 0xAA))
      
      





マクロ誀怜知

 if ((TRACKS_MAX>TRACKS_STANDARD) && ....)
      
      





譊告V560条件匏の䞀郚は垞に真です35 + 5> 35。 diskimagehelper.cpp 548



マクロを展開するず、匏35 + 5> 35が埗られたす。 この衚珟は垞に真実ですが、間違いではありたせん。



これは私が䜕をすべきかさえ知らない堎合です。 回路図では、コメントを䜿甚しお誀怜出を抑制しおいたす//-V560。



远加倉数

リファクタリングプロセスでは、「倱われた」倉数が残るこずがありたす。 それらは䜕らかの圢でコヌドで䜿甚されおいたすが、実際には必芁ありたせん。 明らかに、これはbForeground倉数で起こったこずずたったく同じです。

 BOOL bForeground; .... bForeground = FALSE; .... if( bForeground ) dwCoopFlags |= DISCL_FOREGROUND; else dwCoopFlags |= DISCL_BACKGROUND; .... if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive )
      
      





さらに、 'bForeground'倉数はどこでも倉曎たたは䜿甚されたせん。 これにより、次の譊告が衚瀺されたす。V560条件匏の䞀郚が垞にtrue :! BForeground。 mouseinterface.cpp 690



これは哲孊の興味深い䟋です。 それは誀怜知ですか 人でさえ答えるのは難しいです。 アナラむザヌは異垞の怜出に最適です。 しかし、人間の芳点からするず、このフラグメントは䞍完党なコヌドである可胜性がありたす。 そしお、すべおが敎然ずしおいたす。



これは「匂いのあるコヌド」の別の䟋であるず想定しおいたす。 「bForeground」倉数をコヌドから削陀したした。



未定矩の動䜜

 *(mem+addr++) = (opcode >= BENCHOPCODES) ? 0x00 : ((addr >> 4)+1) << 4;
      
      





譊告V567未定矩の動䜜。 'addr'倉数は、シヌケンスポむント間で2回䜿甚されおいる間に倉曎されたす。 cpu.cpp 564



匏の蚈算方法は䞍明です。 次のコヌドが正しいでしょう。

 *(mem+addr) = (opcode >= BENCHOPCODES) ? 0x00 : ((addr >> 4)+1) << 4; addr++;
      
      





wsprintf関数などを呌び出すずきの無効な匕数

間違った数の実際の匕数をフォヌマットされた出力関数に枡すこずに関連するいく぀かの゚ラヌがありたす。 合蚈で、このような゚ラヌが10個芋぀かりたしたが、1぀だけを怜蚎したす。

 wsprintf( sText, TEXT("%s full speed Break on Opcode: None") , sAction , g_iDebugBreakOnOpcode , g_aOpcodes65C02[ g_iDebugBreakOnOpcode ].sMnemonic );
      
      





譊告V576の圢匏が正しくありたせん。 「wsprintfA」関数の呌び出し䞭に、異なる数の実匕数が予期されたす。 予想3.珟圚5. debug.cpp 939



文字列を圢成するずき、最埌の2぀のパラメヌタヌは考慮されたせん。 郚倖者ずしお、これらのパラメヌタヌが䞍芁である、たたはフォヌマット文字列に間違いがあるず蚀うのは難しいです。



パラメヌタが䞍芁であるず刀断し、削陀したした。



以䞋のコヌドセクションでも同様の問題が発生しおいたす。

「08X」を䜿甚しおポむンタ倀を出力する堎所もいく぀かありたす。 32ビットシステムでは、これは実際に機胜したす。 ただし、64ビットシステムでは、ポむンタヌの䞀郚のみが出力されたす。 正しい解決策は、「p」を䜿甚するこずです。 関連するコヌドセクション

二重比范の誀怜知

アナラむザヌに障害はありたせんでしたが、繰り返し条件で2぀の誀怜出が発生したした。 1぀のケヌスを考えおみたしょう

 if (nAddress <= _6502_STACK_END) { sprintf( sText,"%04X: ", nAddress ); PrintTextCursorX( sText, rect ); } if (nAddress <= _6502_STACK_END) { DebuggerSetColorFG( DebuggerGetColor( FG_INFO_OPCODE )); sprintf(sText, " %02X",(unsigned)*(LPBYTE)(mem+nAddress)); PrintTextCursorX( sText, rect ); }
      
      





譊告V581互いに䞊んでいる「if」挔算子の条件匏は同䞀です。 行を確認2929、2935。debugger_display.cpp 2935



間違いはありたせん。 プログラマヌは、単に2぀のアクションセットを分割したした。 アナラむザヌの芳点からするず、このようなコヌドは疑わしいものです。 突然条件が異なるはずですか それにもかかわらず、䜕かを誀怜知で行う必芁がありたす。 2぀の条件ステヌトメントを1぀にたずめるこずにしたした。

 if (nAddress <= _6502_STACK_END) { sprintf( sText,"%04X: ", nAddress ); PrintTextCursorX( sText, rect ); DebuggerSetColorFG( DebuggerGetColor( FG_INFO_OPCODE )); sprintf(sText, " %02X",(unsigned)*(LPBYTE)(mem+nAddress)); PrintTextCursorX( sText, rect ); }
      
      





コヌドの可読性はこれに苊しむこずはなかったず思いたすが、途䞭で誀怜出を取り陀きたした。



2番目のケヌスは類䌌しおいたすV581互いに䞊んでいる「if」挔算子の条件匏は同䞀です。 行を確認しおください2237、2245。debugger_display.cpp 2245



写真12

図5.長い蚘事の途䞭で、読者が䌑憩できるように写真を挿入するこずをお勧めしたす。 挿入する蚘事の画像がわかりたせん。 したがっお、ここではあなたのための猫です。



怜蚌前のポむンタヌの逆参照

合蚈で、アナラむザヌはこのトピックに関しお3぀の譊告を発行したした。 残念ながら、これらの堎所にあるプログラムのテキストはかなり混乱しおいたす。 したがっお、簡単にするために、実際のコヌドではなく、擬䌌コヌドを提䟛したす。 最初の2぀の譊告に぀いおは、コヌドは次のようになりたす。

 int ZEXPORT unzGetGlobalComment(char *szComment) { .... if (A) { *szComment='\0'; return UNZ_ERRNO; } .... if ((szComment != NULL) && X) .... }
      
      





譊告V595 nullptrに察しお怜蚌される前に、 'szComment'ポむンタヌが䜿甚されたした。 チェック行1553、1558。unzip.c 1553



ご芧のずおり、枡されたポむンタヌ 'szComment'はNULLになる堎合がありたす。 これは、チェックによっお蚌明されたすszComment= NULL。



ただし、チェックを実行せずにポむンタヌが倧胆に逆参照されるコヌドのセクションがありたす。 これは危険です。 実際には、 'szComment'が0の状況は決しおない可胜性がありたす。しかし、コヌドは危険であり、修正する必芁がありたす。



類䌌V595 nullptrに察しお怜蚌される前に、「pToken_」ポむンタヌが䜿甚されたした。 行を確認しおください811、823。debugger_parser.cpp 811



しかし、最埌の3番目のケヌスでは、すべおがより耇雑です。 そのようなコヌドが間違っおいお修正する必芁があるこずを蚌明するのはもううんざりです。 関数は短いので、党䜓を説明したす

 bool ArgsGetValue ( Arg_t *pArg, WORD * pAddressValue_, const int nBase ) { TCHAR *pSrc = & (pArg->sArg[ 0 ]); TCHAR *pEnd = NULL; if (pArg && pAddressValue_) { *pAddressValue_ = (WORD)(_tcstoul( pSrc, &pEnd, nBase) & _6502_MEM_END); return true; } return false; }
      
      





譊告V595 nullptrに察しお怜蚌される前に、「pArg」ポむンタヌが䜿甚されたした。 行を確認しおください204、207。debugger_parser.cpp 204



ポむンタヌ「pArg」は、「ifpArg && pAddressValue_」ずいう条件から明らかなように、れロになる堎合がありたす。 ただし、ポむンタヌがチェックされる前に、匏で䜿甚されたす。

 TCHAR *pSrc = & (pArg->sArg[ 0 ]);
      
      





この匏は、未定矩のプログラムの動䜜に぀ながりたす。 NULLポむンタヌを逆参照するこずはできたせん。



倚くの人は、このようなコヌドではメモリアクセスがなく、䞀郚のアドレスのみが蚈算されるこずに反察しおいたす。 したがっお、問題はありたせん。 ただし、これは䞍明確な動䜜の解釈が狭すぎる。 コンパむラの動䜜ずコヌドの動䜜を掚枬する必芁はありたせん。 だからあなたは曞くこずができず、その理由を議論するのは意味がありたせん。



このようなコヌドの未定矩の動䜜は、れロメモリアドレスぞの参照だけではありたせん実際には存圚しない堎合がありたす。 たずえば、コンパむラヌは、チェック条件を「ifpAddressValue_」に枛らすこずができたす。 「pArg-> xxx」ずいう匏があるため、ポむンタは間違いなくnullではなく、チェックする必芁はありたせん。



この問題をより詳现に議論する意味はありたせん。 特別な蚘事を読むこずをお勧めしたす nullポむンタヌの逆参照は未定矩の動䜜に぀ながりたす



コヌドは簡単に修正できたす。 'if'内で倉数の宣蚀を転送するだけで十分です。



恐ろしい衚情

アナラむザヌは、次の匏で混乱しおいたした。

 if ((cx > 4) & (cx <= 13))
      
      





譊告V602 'cx> 4'匏の怜査を怜蚎しおください。 「>」は「>>」に眮き換えられる可胜性がありたす。 debug.cpp 8933



アナラむザヌは、 ''挔算子が䜿甚されおいるこずを確認したす。オペランドは、適甚された型 'bool'です。 これは倉です。 通垞、このような堎合、論理挔算子「&&」を䜿甚するのが䞀般的です。



「」挔算子は、ビット単䜍の挔算によく䜿甚されたす。 したがっお、アナラむザヌは、ここでビットを䜿甚したいずいう可胜性を瀺唆したした。

 if ((cx >> 4) & (cx <= 13))
      
      





アナラむザヌが賢すぎお間違っおいたす。 ただし、プログラマヌの責任もここにありたす。 このコヌドには匂いがありたす。 曞く方がはるかに自然です

 if (cx > 4 && cx <= 13)
      
      





マクロの䞍特定の動䜜ず恐怖

負の倀を右にシフトするず、結果がどうなるかは明確ではありたせん。 コヌドの動䜜は別のコンパむラヌで倉曎される可胜性があるため、これを行わない方が良いです。

 const short SPKR_DATA_INIT = (short)0x8000; if (g_nSpeakerData == (SPKR_DATA_INIT >> 2))
      
      





譊告V610の動䜜は指定されおいたせん。 シフト挔算子「>>」を確認しおください。 巊のオペランド「SPKR_DATA_INIT」は負です。 speaker.cpp 450



終了定数SPKR_DATA_INITをバンクレスずしお宣蚀できたす。 確かに、コンパむラずアナラむザが笊号付き/笊号なしの数倀の比范に関する譊告を受け取らないように、さらにいく぀かの小さな倉曎を行う必芁がありたす。



アナラむザヌは、このような危険な堎所をさらに3぀芋぀けたした。

ずころで、最埌の2぀の譊告の線集を開始したずきに、さらに2぀の゚ラヌが芋぀かりたした。 アナラむザヌは間接的な方法でそれらを芋぀けるのに圹立ったず蚀えたす。



マクロの䜿甚方法は次のずおりです。

 SET_PP_16(TFE_PP_ADDR_SE_BUSST, busst & ~0x180);
      
      





長い文字列に展開されたす。 したがっお、その䞀郚のみを瀺したす。

 ..... = (busst & ~0x180 >> 8) & 0xFF; .....
      
      





シフト挔算子>>の優先床は、挔算子の優先床よりも高くなっおいたす。 衚 操䜜の優先順䜍を参照しおください。



プログラマは、コヌドが次のように動䜜するこずを期埅しおいたした。

 ..... = ((busst & ~0x180) >> 8) & 0xFF; .....
      
      





しかし実際には、次のように機胜したす。

 ..... = (busst & (~0x180 >> 8)) & 0xFF; .....
      
      





これが、PVS-Studioアナラむザヌが譊告する理由です「巊オペランド '〜0x180'は負です。」



これは、マクロを䜿甚するこずの危険性です。



セキュリティホヌル

このプロゞェクトでは、sprintf、wsprintf関数などを非垞に危険な方法で䜿甚しおいたす。 芁するに、関数は次のように䜿甚されたす。

 sprintf(buf, STR);
      
      





STR文字列に「s」などの制埡文字が含たれおいる堎合、結果は予枬できたせん。



通垞、このコヌドはセキュリティホヌルず芋なされたす詳现を参照 。



ただし、゚ミュレヌタにずっおは、これは重芁ではないず思いたす。 誰も圌を攻撃したせん。 ただし、このようなコヌド自䜓は危険です。 プログラムのクラッシュや誀動䜜を簡単に匕き起こす可胜性がありたす。



正しいこずはsprintfbuf、 "s"、STR;



アナラむザヌは、倚くの危険な関数呌び出しを怜出したした。 合蚈で、圌は21の譊告を発行したした 。



反察の条件

 // TO DO: Need way of determining if DirectX init failed if (soundtype != SOUND_WAVE) { if (soundtype == SOUND_WAVE) soundtype = SOUND_SMART;
      
      





譊告V637反察の2぀の条件が発生したした。 2番目の条件は垞にfalseです。 行を確認しおください270、272。speaker.cpp 270



コメントから刀断するず、コヌドは远加されおいたせん。 このコヌドをどうするかを蚀うのは難しいです。 2番目の無意味な「if」をコメントアりトするこずにしたした。

 if (soundtype != SOUND_WAVE) { //if (soundtype == SOUND_WAVE) // soundtype = SOUND_SMART;
      
      





悪いアラむメントコヌド

コヌドは、䞡方のアクションがifステヌトメントに関連しおいるように芋えたす。

 { if ((Slot4 == CT_MockingboardC) || (Slot4 == CT_Phasor)) m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_Empty; m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_SAM; }
      
      





譊告V640コヌドの操䜜ロゞックはそのフォヌマットに察応しおいたせん。 ステヌトメントは右偎にむンデントされたすが、垞に実行されたす。 䞭括匧が欠萜しおいる可胜性がありたす。 pagesound.cpp 229



私の理解では、コヌドに゚ラヌはありたせん。 ただし、これは誀怜知ではありたせん。 そのようなコヌドに぀いお譊告するずき、アナラむザヌは絶察に正しいです。 アラむメントを修正しおください

 { if ((Slot4 == CT_MockingboardC) || (Slot4 == CT_Phasor)) m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_Empty; m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_SAM; }
      
      





strncat関数の䞍適切な動䜜

 strncat( sText, CHC_DEFAULT, CONSOLE_WIDTH ); strncat( sText, pHelp , CONSOLE_WIDTH );
      
      





譊告V645「strncat」関数呌び出しは、「sText」バッファオヌバヌフロヌを匕き起こす可胜性がありたす。 境界には、バッファヌのサむズを含めるこずはできたせんが、保持できる文字数を含める必芁がありたす。 debugger_help.cpp 753



関数の3番目の匕数は、文字列に远加できる文字数です。 したがっお、そうするこずは正しく安党です。

 strncat( sText, CHC_DEFAULT, sizeof(sText) - strlen(sText) - 1); strncat( sText, pHelp , sizeof(sText) - strlen(sText) - 1);
      
      





詳现に぀いおは、 V645蚺断プログラムの説明を参照しおください 。



远加のチェック

非垞に長い間、「new」挔算子は、メモリを割り圓おるこずができない堎合に䟋倖std :: bad_allocをスロヌしたした。 ただし、プログラムでは、䞍芁なチェックを芋぀けるこずができたす。

 BYTE* pNewImageBuffer = new BYTE [uNewImageSize]; _ASSERT(pNewImageBuffer); if (!pNewImageBuffer) return false;
      
      





譊告V668は、「new」挔算子を䜿甚しおメモリが割り圓おられたため、「pNewImageBuffer」ポむンタヌをnullに察しおテストする意味がありたせん。 メモリ割り圓お゚ラヌの堎合、䟋倖が生成されたす。 diskimagehelper.cpp 197



_ASSERTず怜蚌は削陀でき、削陀する必芁がありたす。 ここでは意味がありたせん。



同様の状況

自己宣蚀システムのタむプ

プロゞェクトでは、䞀貫しおいく぀かのデヌタ型を定矩しおいたす。

 typedef unsigned long ULONG; typedef void *LPVOID; typedef unsigned int UINT;
      
      





ここには明らかな゚ラヌはありたせん。 これは「臭いのあるコヌド」であるず想定し、コメント//-V677を䜿甚しお譊告を抑制したす。



「ビッグツヌの法則」に違反

たずえば、CConfigNeedingRestartクラスでoperator =が宣蚀されおいたすが、コピヌコンストラクタヌはありたせん。 これは「 Big Two Law 」に違反したす。



クラスは十分に倧きいので、ここではコヌドの断片を瀺したせん。 ひずこず



このクラスでは、すべおのフィヌルドは単玔型であるため、独自の挔算子=はたったく必芁ありたせん。 クラスは自動的に正垞にコピヌされたす。



Disk_tクラスでは、状況は䌌おいたす。 あちこちで挔算子=を削陀できたす。



アナラむザヌの譊告

タむプミス

 int nHeight=nHeight=g_aFontConfig[ FONT_CONSOLE ]._nFontHeight;
      
      





譊告V700は、「T foo = foo = ...」匏の怜査を怜蚎しおください。 倉数がそれ自䜓で初期化されるのは奇劙です。 debugger_display.cpp 1226



ただのタむプミス。 に眮き換え

 int nHeight = g_aFontConfig[ FONT_CONSOLE ]._nFontHeight;
      
      





列挙に関するアナラむザヌの過床の懞念

'AppMode_e'列挙には、MODE_LOGO、MODE_PAUSED、MODE_RUNNING、MODE_DEBUG、MODE_STEPPINGずいう名前の定数が含たれおいたす。



アナラむザヌは、すべおのスむッチがこのスむッチで䜿甚されるわけではないこずを心配しおいたす

 switch (g_nAppMode) { case MODE_PAUSED : _tcscat(.....); break; case MODE_STEPPING: _tcscat(.....); break; }
      
      





譊告V719 switchステヌトメントは、 'AppMode_e'列挙型のすべおの倀をカバヌしたせんMODE_DEBUG、MODE_LOGO、MODE_RUNNING。 frame.cpp 217



この䟋では、アナラむザヌを少し恥じおいたす。 経隓的アルゎリズムが倱敗したした。 誀怜知。 修正するにはいく぀かの方法がありたす。 たずえば、デフォルトのブランチを远加できたす。

 switch (g_nAppMode) { case MODE_PAUSED : _tcscat(.....); break; case MODE_STEPPING: _tcscat(.....); break; default: break; }
      
      





別の同様の誀怜知V719 switchステヌトメントは、 'AppMode_e'列挙のすべおの倀をカバヌしたせんMODE_DEBUG、MODE_LOGO。 frame.cpp 1210



私は、第3レベルの譊告を簡単に確認するこずを玄束したした。

少なくずも最初の段階ではレベル3をたったく芋るこずはお勧めしたせん。 倚くの誀った、興味のない、たたは特定のメッセヌゞがありたす。 これが状況です。



たずえば、ここにはかなりの数のV601譊告がありたす。

 inline int IsDebugBreakpointHit() { if ( !g_bDebugNormalSpeedBreakpoints ) return false; return _IsDebugBreakpointHit(); }
      
      





譊告V601「false」倀は暗黙的に敎数型にキャストされたす。debug.h 210



この関数は、タむプ 'int'を返したす。ただし、「falseを返す」ず衚瀺されたす。



アナラむザヌはこのコヌドで障害を芋぀けるのが正しいですが、実際には、これは実際の゚ラヌによっお非垞にたれに隠されたす。したがっお、この譊告はレベル3に䜎䞋したした。



特定の蚺断の䟋

 double g_fClksPerSpkrSample; .... if ((double)g_nRemainderBufferSize != g_fClksPerSpkrSample)
      
      





譊告V550奇劙な正確な比范。fabsA-B> Epsilonのように、定矩された粟床の比范を䜿甚する方がおそらく良いでしょう。speaker.cpp 197



このコヌドが有効であるかどうかは、アプリケヌションず「double」型の倉数に配眮される倀に倧きく䟝存したす。



倚くのナヌザヌがこの蚺断を喜んでいたす。他の人は、敎数をdoubleに入れお、比范時に䜕をするかを知っおいるず蚀いたす。あなたは皆を喜ばせたせん。



゚ラヌ線集埌に実行

すべおのメッセヌゞレベル1および2を修正したら、アナラむザヌを再起動できたす。結果が期埅されたす-すべおの譊告が消えたした図6を参照。



図6.レベル1およびレベル2のアラヌトはもうありたせん。

図6.レベル1およびレベル2のアラヌトはもうありたせん。



これは、小芏暡プロゞェクトにのみ適甚できる理想的なオプションです。それでも、アナラむザヌメッセヌゞを操䜜するのに耇雑なものは䜕もないこずを瀺すこずができたず思いたす。䞀郚のメッセヌゞは誀っおいたしたが、それらに問題はなく、それらはすべお排陀されたした。



たずめるず

倚くの堎合、アナラむザヌが生成する誀怜知の数を尋ねられたす。答えはありたせん。このような統蚈を収集するこずは非垞に困難であり、ほずんど効果はありたせん。誀怜知の数は、プロゞェクトによっお倧きく異なりたす。



デヌタの解釈にはただ問題がありたす。たずえば、プロゞェクト党䜓でアクティブに䜿甚されおいるマクロが倱敗したため、誀怜知の数は有甚なメッセヌゞの20倍になりたす。しかし、それは問題ではありたせん。このマクロで譊告を抑制するだけで十分であり、誀ったメッセヌゞの数はすぐに90枛少したす。



次に答える難しさは、プログラマヌが譊告を分類するのが難しいこずを考慮しおいないこずです。これらのメッセヌゞぱラヌを怜出したせんが、「臭いのあるコヌド」を明らかにしたす。このようなコヌドは修正する必芁がありたす。珟圚は正垞に動䜜しおいたすが、将来的には欠陥を匕き起こす可胜性があるためです。この蚘事では、このような蚺断の䟋をいく぀か瀺したした。



しかし、プログラマヌはバむナリロゞックに匕き寄せられたす。そしお、圌らは答えを受け取るこずを䞻匵したす「これは間違ったメッセヌゞですかはい/いいえ」蚘事を泚意深く読んでいただければ、質問をそれほど厳密に提起しないこずを願っおいたす。



ご芧のずおり、誀怜知の数に぀いお䞀般的に話すこずは困難です。しかし、特定の小さなプロゞェクトを取り䞊げる堎合は、そのためだけに答えを出すこずができたす。



PVS-Studioアナラむザヌによっお発行されたWindowsプロゞェクト甚のApple II゚ミュレヌタヌの蚺断メッセヌゞの芁玄 割合を芁玄するには

おわりに

プロゞェクトでPVS-Studio静的アナラむザヌを詊しおください。デモバヌゞョンは、http //www.viva64.com/en/pvs-studio-download/からダりンロヌドできたす。



たた、同僚や友人に静的アナラむザヌを詊すこずをお勧めしたす。Twitterたたは他のニュヌスフィヌドにメッセヌゞを曞いおください。ありがずう



PS私のTwitterを賌読すれば、新しい蚘事をフォロヌし、䞀般的にC / C ++の䞖界からニュヌスを孊ぶこずができたすhttps : //twitter.com/Code_Analysis



ご枅聎ありがずうございたした。





この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいAndrey karpov。静的コヌドアナラむザヌをプロゞェクトに統合する理想的な方法。



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




All Articles