Windows検証用のIntel IPPサンプル-続き

PVS-Studio対IPPサンプル。続ける

進歩は止まりません。 私のお気に入りのPVS-Studio静的コードアナライザーも開発中です。 そして最近、私たちがすでにテストしたプロジェクトを完全に再チェックできることに気付きました。 それについての新しい記事を書くことは、どういうわけか奇妙であり、彼らが面白くありそうにない。 ただし、そのような記事の1つはまだ行う必要があると思います。 静的分析の真の利点は、通常の使用でのみ得られ、ケースバイケースのチェックでは得られないという声明を支持する別の議論になります。 それでは、インテルIPPサンプルプロジェクトで何が新しいものを見つけたのかを見てみましょう。



前の記事「Intel IPPサンプルfor Windows-エラー処理」[ 1 ]は、2011年1月27日に発行されました。 約9か月が経過しました。 この間、記事で説明されている多くのエラーはIPPサンプルで修正されました。 いくつかのライブラリの更新がリリースされました。 ただし、インテルの開発者はPVS-Studioに興味がなかったため、テストは1回限りでした。 そして、9か月の開発後にアナライザーが検出する新しい興味深いエラーをよく見ることができます。



私たちは怠idleな話にふけることはありません。私たちはプログラマであり、コードの例にすぐに移ります。 分析はPVS-Studio 4.37によって実行されました(前の注記はバージョン4.10を参照しています)。 もちろん、すべての欠陥が与えられるわけではありませんが、興味深いだけであまり繰り返さないものです。 すべての落とし穴を見たい人は誰でもPVS-Studioを使用してレポートを調べることができます 。 しかし、私たちはそのためにここにはいません。



古典的な未定義の動作



template<typename T, Ipp32s size> void HadamardFwdFast(..., Ipp16s* pDst) { Ipp32s *pTemp; ... for(j=0;j<4;j++) { a[0] = pTemp[0*4] + pTemp[1*4]; a[1] = pTemp[0*4] - pTemp[1*4]; a[2] = pTemp[2*4] + pTemp[3*4]; a[3] = pTemp[2*4] - pTemp[3*4]; pTemp = pTemp++; pDst[0*4] = (Ipp16s)(a[0] + a[2]); pDst[1*4] = (Ipp16s)(a[1] + a[3]); pDst[2*4] = (Ipp16s)(a[0] - a[2]); pDst[3*4] = (Ipp16s)(a[1] - a[3]); pDst = pDst++; } ... }
      
      





PVS-Studio診断メッセージ:



V567未定義の動作。 'pTemp'変数は、シーケンスポイント間で2回使用されている間に変更されます。 me umc_me_cost_func.h 168



V567未定義の動作。 「pDst」変数は、シーケンスポイント間で2回使用されている間に変更されます。 me umc_me_cost_func.h 174



不定の振る舞いを実証するために記事で与えられている単なる規範的な例[ 2 ]。 変数pTempとpDstが同じシーケンスポイント内で2回変化するため、変数が増加するかどうかを言うことは不可能です。 結果は、コンパイラと最適化の設定によって異なります。



別の同様のコードスニペットがあります。

 void VC1BRC_I::CompleteFrame(ePType picType) { ... m_Quant.LimIQuant = m_Quant.LimIQuant--; ... m_Quant.IQuant = m_Quant.IQuant--; ... }
      
      







未定義の動作とプレフィックスの増分



 bool MoveOnNextFrame() { if (m_nFrames>0) { m_pFrame[m_curIndex] = 0; m_curIndex = (++m_curIndex)%m_maxN; m_nFrames--; return true; } return false; }
      
      





PVS-Studio診断メッセージ:



V567未定義の動作。 'm_curIndex'変数は、シーケンスポイント間で2回使用されている間に変更されます。 vc1_enc umc_vc1_enc_planes.h 630



あいまいな動作の別の例を次に示します。 ここではプレフィックスの増分が使用されますが、本質的には何も変更されません。 とにかく、1つのシーケンスポイントがあり、変数m_curIndexは2回変化します。 理論的には、コンパイラには次の擬似コードを作成する権利があります。



A = m_curIndex + 1;



B = A%m_maxN;



m_curIndex = B;



m_curIndex = A;



実際には、これは起こりそうになく、変数はすぐに増加しますが、どのような方法でもそれに依存することはできません。



オブジェクト名のタイプミス



 IPLFUN(void, iplMpyRCPack2D, (IplImage* srcA, IplImage* srcB, IplImage* dst)) { ... if( (srcA->depth == IPL_DEPTH_8U ) || (srcB->depth == IPL_DEPTH_8U ) || (srcB->depth == IPL_DEPTH_16U) || (srcB->depth == IPL_DEPTH_16U) || (srcA->depth == IPL_DEPTH_1U ) || (srcB->depth == IPL_DEPTH_1U ) ) ... }
      
      





PVS-Studio診断メッセージ:



V501「||」の左と右に同一のサブ式「(srcB-> depth == 16)」があります 演算子。 IPL iplmpy2d.c 457



コードをよく見ると、忍び寄るタイプミスに気づくでしょう。 チェックが失われました: "(srcA-> depth == IPL_DEPTH_16U)"。



不完全なバッファクリーンアップ



 UMC::Status VC1EncoderADV::SetMEParams_I_Field(UMC::MeParams* MEParams) { UMC::Status umcSts UMC::UMC_OK; memset(MEParams,0,sizeof(MEParams)); ... }
      
      





PVS-Studio診断メッセージ:



V512「memset」関数を呼び出すと、バッファー「MEParams」のアンダーフローが発生します。 vc1_enc umc_vc1_enc_adv.cpp 1767



「sizeof(MEParams)」は構造体ではなくポインタのサイズを返すため、バッファの一部のみがクリアされます。 正しいサイズを計算するには、ポインターを逆参照する必要があります: "sizeof(* MEParams)"。



コピー貼り付けエラー



 Status VC1VideoDecoder::ResizeBuffer() { ... if(m_pContext && m_pContext->m_seqLayerHeader && m_pContext->m_seqLayerHeader->heightMB && m_pContext->m_seqLayerHeader->heightMB) ... }
      
      





PVS-Studio診断メッセージ:



V501「&&」演算子の左右には、同一のサブ式「m_pContext-> m_seqLayerHeader-> heightMB」があります。 vc1_dec umc_vc1_video_decoder.cpp 1351



おそらく、コピーされた行を単純化するために、それを修正するのを忘れました。 私には、これは次のようになっているはずでした:

 if(m_pContext && m_pContext->m_seqLayerHeader && m_pContext->m_seqLayerHeader->heightMB && m_pContext->m_seqLayerHeader->widthMB)
      
      







配列の境界を越える



 Ipp32f pa_nb_long[NUM_CHANNELS][2][MAX_PPT_LONG]; MP3Status mp3enc_psychoacousticInit(...) { ... for (ch = 0; ch < NUM_CHANNELS; ch++) for (i = 0; i < MAX_PPT_LONG; i++) { for (j = 0; j < 3; j++) state->pa_nb_long[ch][j][i] = (Ipp32f)1.0e30; } ... }
      
      





PVS-Studio診断メッセージ:



V557配列のオーバーランが可能です。 「j」インデックスの値は2に達する可能性があります。mp3_enc mp3enc_psychoacoustic_fp.c 361



このコードはセグメンテーションエラーにつながります。 変数 'j'は値2をとることができますが、有効なインデックスは0と1のみです。

 for (j = 0; j < 2; j++)
      
      





配列を超える可能性が高い他のループが見つかりました。

 typedef Ipp32f samplefbout[2][18][32]; samplefbout fbout_data[NUM_CHANNELS]; static void mp3enc_scale_factor_calc_l2(MP3Enc *state) { ... for (ch = 0; ch < stereo + state->com.mc_channel; ch++) { for (t = 0; t < 3; t++) { for (sb = 0; sb < sblimit_real; sb++){ for (j = 0; j < 12; j++) fbout[j] = state->fbout_data[ch][0][t * 12 + j][sb]; ... }
      
      





PVS-Studio診断メッセージ:



V557配列のオーバーランが可能です。 't * 12 + j'インデックスの値は35に達します。mp3_enc mp3enc_quantization_12_fp.c 275



t == 2、j == 11の状況が発生する可能性がある場合、配列は境界を越えます。 このコードはどのように見えるはずでしたか、答えるのは難しいと思います。



配列 'samplefbout'の使用はまったくスムーズに進みません。 別のコード:

 typedef Ipp32f samplefbout[2][18][32]; samplefbout fbout_data[NUM_CHANNELS]; static void mp3enc_join_LR_l2(MP3Enc *state) { Ipp32s sb, j; Ipp32s sblimit_real = state->com.sblimit_real; for (sb = 0; sb < sblimit_real; sb++) for (j = 0; j < 36; j++) state->fbout_data[2][0][j][sb] = 0.5f * (state->fbout_data[0][0][j][sb] + state->fbout_data[1][0][j][sb]); }
      
      





PVS-Studio診断メッセージ:



V557配列のオーバーランが可能です。 「j」インデックスの値は35に達する可能性があります。mp3_enc mp3enc_quantization_12_fp.c 639



V557配列のオーバーランが可能です。 「j」インデックスの値は35に達する可能性があります。mp3_enc mp3enc_quantization_12_fp.c 640



2つのループに1つの変数を使用する



 JERRCODE CJPEGDecoder::DecodeScanBaselineNI(void) { ... for(c = 0; c < m_scan_ncomps; c++) { block = m_block_buffer + (DCTSIZE2*m_nblock*(j+(i*m_numxMCU))); // skip any relevant components for(c = 0; c < m_ccomp[m_curr_comp_no].m_comp_no; c++) { block += (DCTSIZE2*m_ccomp[c].m_nblocks); } ... } ... }
      
      





PVS-Studio診断メッセージ:



V535変数 'c​​'は、このループと外側のループに使用されています。 jpegcodec jpegdec.cpp 4652



このコードの問題は、変数「c」が外側のループとネストされたループで同時に使用されることです。 ループの境界値によっては、このコードはすべてのデータを処理したり、無限ループになったりすることはありません。



未解決のエラー



最初の記事で説明したエラーの多くは修正されています。 しかし、IPPサンプルの一部の開発者は、注意を払っていないか、これらは間違いではないと感じています。 たとえば、このフラグメントの同様の状況:

 vm_file* vm_file_fopen(const vm_char* fname, const vm_char* mode) { ... mds[3] = FILE_ATTRIBUTE_NORMAL | (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING; ... }
      
      





PVS-Studio診断メッセージ:



V502おそらく、「?:」演算子は予想とは異なる方法で動作します。 「?:」演算子の優先順位は「|」よりも低い 演算子。 vm vm_file_win.c 393



奇妙なコード



言うのが難しいコードのセクションがたくさんあります;本当の間違いはこれまたは単なる冗長コードです。 いくつか例を挙げます。

 int ec_fb_GetSubbandNum(void* stat) { _fbECState* state = (_fbECState*)stat; return (state->freq - state->freq); }
      
      





PVS-Studio診断メッセージ:



V501「-」演算子の左右に同じ副次式があります。state-> freq-state-> freq speech ec_fb.c 253



非常に奇妙な機能。 ここで、使用されていない変数と奇妙に闘うか、「return」ステートメントが別の式の結果を返す必要があります。

 AACStatus alsdecGetFrame(...) { ... if (state->msbFirst == 0) { for (i = 0; i < num; i++) { *tmpPtr = *srcPrt; tmpPtr += state->numChannels; srcPrt++; } } else { for (i = 0; i < num; i++) { *tmpPtr = *srcPrt; tmpPtr += state->numChannels; srcPrt++; } } ... }
      
      





PVS-Studio診断メッセージ:



V523「then」ステートメントは「else」ステートメントと同等です。 aac_dec als_dec_api.c 923



さて、私は何と言えますか? 疑わしい! なぜ異なる条件下で2つの同一のサイクルが必要なのですか?

 void rrGetNextBunch_Spiral(...) { int x,y; ... if(x < 0) if(x < 0) goto _begine; ... if(y < 0) if(y < 0) goto _begine; ... }
      
      





PVS-Studio診断メッセージ:



V571定期的なチェック。 「if(x <0)」条件は、1025行目ですでに検証されています。3d-viewerrrdemosupport.cpp 1026



V571定期的なチェック。 「if(y <0)」条件は、1028行目ですでに検証されています。3d-viewerrrdemosupport.cpp 1029



奇妙な重複チェック。

 Status H264ENC_MAKE_NAME(H264CoreEncoder_UpdateRefPicMarking) (void* state) { ... // set frame_num to zero for this picture, for correct // FrameNumWrap core_enc->m_pCurrentFrame->m_FrameNum = 0; core_enc->m_pCurrentFrame->m_FrameNum = 0; ... }
      
      





PVS-Studio診断メッセージ:



V519「core_enc-> m_pCurrentFrame-> m_FrameNum」変数には、値が連続して2回割り当てられます。 おそらくこれは間違いです。 行を確認してください:1804、1805。h264_enc umc_h264_video_encoder_tmpl.cpp.h 1805



誤って文字列をコピーしましたか? または、別の変数をリセットしますか? 明確ではありません。



おわりに



静的解析をプロジェクト開発プロセスに統合してみてください。 最初は余分な労力の無駄のようです。 ただし、作成したばかりのコードフラグメントのテストを開始する前であっても、エラーメッセージを受信することは非常に珍しくて楽しいと感じるでしょう。 そして、アナライザの次のバージョンのリリース後に何かがあるかどうかを興味を持って観察します。



書誌リスト



  1. アンドレイ・カルポフ。 Windows用Intel IPPサンプル-バグの修正。 http://www.viva64.com/go.php?url=741
  2. エレナ・サガラエワ。 シーケンスポイント http://www.viva64.com/go.php?url=665



All Articles