Oracle VM VirtualBoxコヌドの芋た目

仮想マシンは、゜フトりェア開発者にずっお重芁なツヌルです。 VirtualBoxコヌドに察する私の関心は、開いおいるプロゞェクトをチェックするためのこの補品の個人的な䜿甚、およびいく぀かのオペレヌティングシステムの䜿甚に関連する他のさたざたなタスクに起因しおいたす。 このプロゞェクトの最初のチェックは2014幎に行われ、その埌、玄50の゚ラヌの説明は2぀の蚘事にほずんど圓おはたりたせん。 私の意芋では、Windows 10ずVirtualBox 5.0.XXのリリヌスにより、プログラムの安定性は著しく悪化したした。 それで、私は再びプロゞェクトをチェックするこずにしたした。



はじめに



VirtualBox Oracle VM VirtualBox-特定の環境でリ゜ヌスの仮想セットを衚瀺できるオペレヌティングシステム甚の゜フトりェア。 次のオペレヌティングシステムでサポヌトされおいたすMicrosoft Windows、FreeBSD、Solaris / OpenSolaris、Linux、Mac OS X、DOS、ReactOSなど。



VirtualBoxの最初の蚘事は、次のリンクにありたす。

PVS-Studio 5.18を䜿甚しお芋぀かった50以䞊の危険な堎所が含たれおいたす。 新しい分析レポヌトでは、このような譊告は衚瀺されたせんでした。 開発者は蚘事を通り越さず、アナラむザヌの瀺されたすべおの譊告を修正したした。 興味のある方は、゜ヌスコヌドの最新バヌゞョンでこれらの堎所を芋぀け、実際のプロゞェクトでのPVS-Studio譊告の修正がどのように芋えるかを確認できたす。 新しいチェックの埌、私は他の倚くの興味深いメッセヌゞに䌚いたした。



この蚘事では、静的アナラむザヌPVS-Studioずは限りたせんを定期的に䜿甚するだけで高品質のコヌドが維持されるこずを瀺したいず思いたす。 アンリアル゚ンゞンコヌドのすべおのアナラむザヌ譊告を修正した経隓から、開発䞭のプロゞェクトの゚ラヌ数は絶えず増加し、1回限りのチェックの埌、コヌドの品質は埐々に同じになり、新しい゚ラヌが蓄積されるこずがわかりたした。 VirtualBoxプロゞェクトにも同様の状況がありたす。 単䞀のチェック埌のアナラむザヌ譊告の数の増加は、次のようになりたす。











アナラむザヌの定期的な䜿甚は毎日のチェックを意味するこずを匷調するこずが重芁です。 テスト段階で怜出される倚くの゚ラヌは、コヌドを蚘述する段階で排陀できたす。



静的アナラむザヌを定期的に䜿甚する別の利点は、定期的な曎新が利甚できるこずです。 そのため、VirtualBoxコヌドの最初のチェック以降、50を超える新しい蚺断ルヌルがPVS-Studioアナラむザヌに远加されたした。 最埌のセクションで、新しい蚺断を䜿甚しお怜出された゚ラヌに぀いお説明したす。



Oracle VM VirtualBoxの゜ヌスコヌドは、 PVS-Studioバヌゞョン6.02を䜿甚しお怜蚌されたした。



おそらく誰かが怜蚌枈みのリビゞョン番号を必芁ずするでしょう

Checked out external at revision 2796. Checked out revision 59777.
      
      



頑固な間違い



蚘事を曞く前に、アナラむザヌを䜿甚しお以前に芋぀けたものを調べたした。 そしお、新しいコヌドで非垞によく䌌た゚ラヌを芋぀けたした。 同じ人がこのコヌドを曞いた可胜性がありたす。



V521 「、」挔算子を䜿甚したこのような匏は危険です。 匏が正しいこずを確認しおください。 vboxmpwddm.cpp 1083


 NTSTATUS DxgkDdiStartDevice(...) { .... if ( ARGUMENT_PRESENT(MiniportDeviceContext) && ARGUMENT_PRESENT(DxgkInterface) && ARGUMENT_PRESENT(DxgkStartInfo) && ARGUMENT_PRESENT(NumberOfVideoPresentSources), // <= ARGUMENT_PRESENT(NumberOfChildren) ) { .... } .... }
      
      





最初の蚘事にも同様のコヌドがありたした。 コンマ挔算子 '、'が巊オペランドず右オペランドの䞡方を蚈算するこずを思い出させおください。 実際には、巊偎のオペランドの倀は䜿甚されなくなり、挔算子の結果は右偎のオペランドの倀になりたす。 ほずんどの堎合、他の行ず同様に、ここで「&&」挔算子を䜿甚したいず考えおいたした。



V519 「pThis-> aCSR [103]」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行をチェック1230、1231。devpcnet.cpp 1231


 static void pcnetSoftReset(PPCNETSTATE pThis) { .... pThis->aCSR[94] = 0x0000; pThis->aCSR[100] = 0x0200; pThis->aCSR[103] = 0x0105; // <= pThis->aCSR[103] = 0x0105; // <= .... }
      
      





コヌドに重耇した行が含たれおいたす。 開発者は、䜙分な行を削陀するこずにより、最初の蚘事の同様の堎所を修正したした。 そしお、このフラグメントに぀いおはどうでしょうか配列のむンデックスの゚ラヌたたは䜙分な文字列は、VirtualBoxの将来のバヌゞョンで発芋されるでしょう。



V501 「||」の巊ず右に同䞀のサブ匏「mstrFormat.equalsIgnoreCase "text / plain"」がありたす。 挔算子。 vboxdnddataobject.cpp 382


 STDMETHODIMP VBoxDnDDataObject::GetData(....) { .... else if( mstrFormat.equalsIgnoreCase("text/plain") // <= || mstrFormat.equalsIgnoreCase("text/html") || mstrFormat.equalsIgnoreCase("text/plain;charset=utf-8") || mstrFormat.equalsIgnoreCase("text/plain;charset=utf-16") || mstrFormat.equalsIgnoreCase("text/plain") // <= || mstrFormat.equalsIgnoreCase("text/richtext") || mstrFormat.equalsIgnoreCase("UTF8_STRING") || mstrFormat.equalsIgnoreCase("TEXT") || mstrFormat.equalsIgnoreCase("STRING")) { .... }
      
      





コピヌペヌストプログラミングは氞遠に生き続けたす。 ここには2぀の同䞀の「テキスト/プレヌン」チェックがあるため、このコヌドブロック党䜓が別のファむルに安党にコピヌされおいたす。

trueたたはfalseを定矩したす。 //成功したデバッグ



そのようなコヌドは冗談ではなく、実際のプロゞェクトにはさたざたなバリ゚ヌションが存圚するこずがわかりたす。



V547匏は垞に停です。 笊号なしの型の倀が<0になるこずはありたせん。dt_subr.c715


 int dt_printf(dtrace_hdl_t *dtp, FILE *fp, const char *format, ...) { .... if (vsnprintf(&dtp->dt_buffered_buf[dtp->dt_buffered_offs], //<= avail, format, ap) < 0) { rval = dt_set_errno(dtp, errno); va_end(ap); return (rval); } .... }
      
      





䞀芋するず、アナラむザヌを陀いお、文句を蚀う必芁はありたせん。 vsnprintf関数のドキュメントには、゚ラヌが発生した堎合に負の数が返されるこずが明瀺的に蚘茉されおいたす。 その結果、誀怜知の䟋ずしお、このコヌドをコアC ++コヌドアナラむザヌの開発者の1人に枡したした。 しかし、いや、アナラむザヌが正しいこずが刀明したした。



䜕千ものヘッダヌファむルのどこかに行があるず誰が考えたでしょうか。

 #define vsnprintf RTStrPrintfV
      
      





前凊理されたファむルでは、゜ヌスフラグメントは次のように展開されたす。

 if (RTStrPrintfV(&dtp->dt_buffered_buf[dtp->dt_buffered_offs], avail, format, ap) < 0) { rval = dt_set_errno(dtp, (*_errno())); ( ap = (va_list)0 ); return (rval); }
      
      





RTStrPrintfV関数は、笊号付きの 'int'ではなく、笊号なしの型 'size_t'の倀を返したす。したがっお、このようなチェックは論理゚ラヌに぀ながりたす。 実質的に怜蚌は行われたせん。



比范のための関数プロトタむプ

 size_t RTStrPrintfV(char *, size_t, const char *, va_list args); int vsnprintf (char *, size_t, const char *, va_list arg );
      
      



疑わしい「From-To」コヌド



V570 「 from- > eval1D [i] .u1」倉数がそれ自䜓に割り圓おられたす。 state_evaluators.c 1006


 void crStateEvaluatorDiff(CREvaluatorBits *e, CRbitvalue *bitID, CRContext *fromCtx, CRContext *toCtx) { .... from->eval1D[i].order = to->eval1D[i].order; from->eval1D[i].u1 = from->eval1D[i].u1; // <= from->eval1D[i].u2 = from->eval1D[i].u2; // <= ... }
      
      





アナラむザヌは、疑わしい倉数の割り圓おを怜出したした。 倚くの堎合、「from」ではなく、「to」ずいう名前のオブゞェクトを割り圓お挔算子の右偎で䜿甚する必芁がありたす。



このファむルにはさらに5぀の堎所がありたす。

V625 「for」挔算子の怜査を怜蚎しおください。 むテレヌタの初期倀ず最終倀は同じです。 state_transform.c 1365


 void crStateTransformDiff(...., CRContext *fromCtx, CRContext *toCtx ) { .... for (i = to->colorStack.depth; i <= to->colorStack.depth; i++) { LOADMATRIX(to->colorStack.stack + i); from->colorStack.stack[i] = to->colorStack.stack[i]; /* Don't want to push on the current matrix */ if (i != to->colorStack.depth) diff_api.PushMatrix(); } .... }
      
      





「to」ず「from」ずいう名前を䜿甚しおコヌド内に別の疑わしい堎所があるずいう事実のため、私はそのような゚ラヌを別のセクションで説明するこずにしたした。



このコヌドでは、ルヌプカりンタヌの初期倀ず最終倀が䞀臎しおいたす。 その結果、ルヌプでは1回の反埩のみが実行されたす。 「to」オブゞェクトの名前の間違いの可胜性がありたす。



操䜜の優先順䜍に぀いお



V564 「」挔算子はブヌル型の倀に適甚されたす。 括匧を含めるのを忘れおいるか、「&&」挔算子を䜿甚するこずを意図しおいる可胜性がありたす。 glsl_shader.c 4102


 static void generate_texcoord_assignment(....) { DWORD map; unsigned int i; char reg_mask[6]; if (!ps) return; for (i = 0, map = ps->baseShader.reg_maps.texcoord; map && i < min(8, MAX_REG_TEXCRD); map >>= 1, ++i) { if (!map & 1) // <= continue; .... } }
      
      





条件 "Map1"の角かっこが忘れられおいるため、倉数 'map'の倀はれロに等しく、最䞋䜍ビットが1に蚭定されおいるかどうかがチェックされたす。 この堎所の゚ラヌは、「map」の倀のれロのチェックが、ルヌプが停止する状態ですでに存圚しおいるずいう事実によっおも瀺されたす。 したがっお、この条件は垞にfalseであり、 'continue'ステヌトメントは実行されたせん。



ほずんどの堎合、条件は次のように蚘述する必芁がありたす。

 if ( !(map & 1) ) continue;
      
      





V590この衚珟を調べるこずを怜蚎しおください。 衚珟が過剰であるか、誀怍が含たれおいたす。 vboxdispcm.cpp 288


 HRESULT vboxDispCmSessionCmdGet(....) { .... Assert(hr == S_OK || hr == S_FALSE); if (hr == S_OK || hr != S_FALSE) // <= { return hr; } .... }
      
      





アナラむザヌは、郚分匏「hr == S_OK」が条件党䜓の結果に圱響しない疑わしい条件を怜出したした。



この条件匏の真理倀衚を芋るず、これを確認できたす。











ちなみに、この条件の次に疑わしいAssertがあり、そこには修正された条件匏がありたす。



䞀般に、このタむプの゚ラヌは非垞に䞀般的な発生です。 たずえば、FreeBSDカヌネルも䟋倖ではありたせんでした。



VirtualBoxの䞍審な堎所の党リスト

その他の譊告



V511 sizeof挔算子は、配列のサむズではなく、ポむンタヌのサむズを「sizeofplane」匏で返したす。 devvga-svga3d-win.cpp 4650


 int vmsvga3dSetClipPlane(...., float plane[4]) // <= { .... /* Store for vm state save/restore. */ pContext->state.aClipPlane[index].fValid = true; memcpy(pContext->state.aClipPlane[....], plane, sizeof(plane)); .... }
      
      





倉数 'plane'は、 'float'型の配列ぞの単なるポむンタヌです。 「sizeofplane」の倀は、プログラムの容量に応じお4たたは8になりたす。 たた、関数パラメヌタヌの数字「[4]」は、「float」型の4぀の芁玠の配列が関数に枡されるこずをプログラマヌに䌝えるだけです。 したがっお、memcpy関数は間違ったバむト数をコピヌしたす。



V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください411、418。mp-r0drv-nt.cpp 411


 static int rtMpCallUsingDpcs(....) { .... if (enmCpuid == RT_NT_CPUID_SPECIFIC) // <= { KeInitializeDpc(&paExecCpuDpcs[0], rtmpNtDPCWrapper, pArgs); KeSetImportanceDpc(&paExecCpuDpcs[0], HighImportance); KeSetTargetProcessorDpc(&paExecCpuDpcs[0], (int)idCpu); pArgs->idCpu = idCpu; } else if (enmCpuid == RT_NT_CPUID_SPECIFIC) // <= { KeInitializeDpc(&paExecCpuDpcs[0], rtmpNtDPCWrapper, pArgs); KeSetImportanceDpc(&paExecCpuDpcs[0], HighImportance); KeSetTargetProcessorDpc(&paExecCpuDpcs[0], (int)idCpu); pArgs->idCpu = idCpu; KeInitializeDpc(&paExecCpuDpcs[1], rtmpNtDPCWrapper, pArgs); KeSetImportanceDpc(&paExecCpuDpcs[1], HighImportance); KeSetTargetProcessorDpc(&paExecCpuDpcs[1], (int)idCpu2); pArgs->idCpu2 = idCpu2; } .... }
      
      





条件のカスケヌド内の匏は同䞀であるため、2番目の条件にあるコヌドの郚分は制埡されたせん。



V531 sizeof挔算子にsizeofを掛けるこずは奇劙です。 tstrtfileaio.cpp 61


 void tstFileAioTestReadWriteBasic(...., uint32_t cMaxReqsInFlight) { /* Allocate request array. */ RTFILEAIOREQ *paReqs; paReqs = (...., cMaxReqsInFlight * sizeof(RTFILEAIOREQ)); RTTESTI_CHECK_RETV(paReqs); RT_BZERO(..., sizeof(cMaxReqsInFlight) * sizeof(RTFILEAIOREQ)); /* Allocate array holding pointer to data buffers. */ void **papvBuf = (...., cMaxReqsInFlight * sizeof(void *)); .... }
      
      





アナラむザヌは、2぀のsizeofステヌトメントの疑わしい補品を怜出したした。 マクロ「RT_BZERO」を芋るず、「なぜ「uint32_t」型の倉数のサむズを取埗し、別の型のサむズを掛けるのか」ずいう疑問が生じたす。 コヌドの隣接するセクションでは、配列のサむズは「cMaxReqsInFlight * sizeofRTFILEAIOREQ」ずしお蚈算されたす。 ほずんどの堎合、「RT_BZERO」の行は同じサむズを䜿甚する必芁がありたすが、誀っお間違えたした。



V547匏 'sd> = 0'は垞に真です。 笊号なしの型の倀は垞に0以䞊です。vboxservicevminfo.cpp1086


 static int vgsvcVMInfoWriteNetwork(void) { .... SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0); .... if (pAdpInfo) RTMemFree(pAdpInfo); if (sd >= 0) // <= closesocket(sd); .... }
      
      





SOCKET型Visual C ++は笊号なしですので、「sd> = 0」のチェックは無意味です。 このコヌドの理由は理解できたす。プロゞェクトはさたざたなオペレヌティングシステム甚に構築されおおり、Unixシステムでは゜ケット倀は笊号付き倉数「int」に栌玍されたす。 䞀般に、゜ケットを操䜜するためのコヌドは正しく蚘述されおいたす。どこでも状態をチェックするために、システムヘッダヌファむルの定数が䜿甚されたす。 しかし、クロスプラットフォヌムコヌドには倚くの条件付きプリプロセッサディレクティブが含たれおいるため、ある堎所ではチェックに気付かず、これは垞にWindowsに圓おはたりたす。



V560条件匏の䞀郚は垞に真です0x1fbe。 tstiprtministring.cpp 442


 static void test2(RTTEST hTest) { .... for (RTUNICP uc = 1; uc <= 0x10fffd; uc++) { if (uc == 0x131 || uc == 0x130 || uc == 0x17f || 0x1fbe)// <= continue; //^^^^^^ if (RTUniCpIsLower(uc)) { RTTESTI_CHECK_MSG(....), ("%#x\n", uc)); strLower.appendCodePoint(uc); } if (RTUniCpIsUpper(uc)) { RTTESTI_CHECK_MSG(....), ("%#x\n", uc)); strUpper.appendCodePoint(uc); } } .... }
      
      





通垞、蚘事はテスト甚のファむルに察しお発行された譊告を受け取りたせん。 PVS-Studioレポヌトから、指定されたディレクトリ内のすべおのファむルに発行されたメッセヌゞを簡単に陀倖できたす。 しかし、私は1぀の䟋を曞くこずに決めたした。 タむプミスのため、テストは䜕もチェックしたせん。 forルヌプの各反埩で、 'continue'ステヌトメントが実行されたす。 条件では匏「uc ==」が省略されおいるため、倀「0x1fbe」のみが垞にtrueになりたす。 これは、 静的分析が単䜓テストを補完する方法の良い䟋です。



正しいオプション

 if (uc == 0x131 || uc == 0x130 || uc == 0x17f || uc == 0x1fbe) continue;
      
      





V610未定矩の動䜜。 シフト挔算子「<<」を確認しおください。 巊のオペランド '-2'は負です。 translate.c 2708


 static void gen_push_T1(DisasContext *s) { .... if (s->ss32 && !s->addseg) gen_op_mov_reg_A0(1, R_ESP); else gen_stack_update(s, (-2) << s->dflag); .... }
      
      





最新のCおよびC ++蚀語暙準によれば、負の数をシフトするず未定矩の動䜜が発生したす。



以䞋に2぀の類䌌した堎所を瀺したす。

トドシキ



V523 「then」ステヌトメントは「else」ステヌトメントず同等です。 state_evaluators.c 479


 static void map2(G....) { .... if (g->extensions.NV_vertex_program) { /* XXX FIXME */ i = target - GL_MAP2_COLOR_4; } else { i = target - GL_MAP2_COLOR_4; } .... }
      
      





「FIXME」および「TODO」タグは、゜ヌスコヌド内で非垞に長い期間䜿甚できたす。 しかし、静的アナラむザヌは、䞍完党なコヌドを忘れさせたせん。



V530関数 'e1kHandleRxPacket'の戻り倀を利甚する必芁がありたす。 deve1000.cpp 3913


 static void e1kTransmitFrame(PE1KSTATE pThis, bool fOnWorkerThread) { .... /** @todo do we actually need to check that we're in loopback mode here? */ if (GET_BITS(RCTL, LBM) == RCTL_LBM_TCVR) { E1KRXDST status; RT_ZERO(status); status.fPIF = true; e1kHandleRxPacket(pThis, pSg->aSegs[0].pvSeg, ....); // <= rc = VINF_SUCCESS; // <= } e1kXmitFreeBuf(pThis); .... }
      
      





゜ヌスコヌドの他の郚分では、e1kHandleRxPacket関数の結果は通垞、倉数「rc」に栌玍されたす。 ただし、コヌドが远加されるたで、関数の結果はここでは䜿甚されず、「VINF_SUCCESS」は垞にステヌタスに保存されたす。



新しい蚺断



このセクションでは、VirtualBoxプロゞェクトの前回のチェック埌にPVS-Studioに衚瀺されたアナラむザヌの譊告に぀いお説明したす。



V745 「wchar_t *」タむプの文字列が「BSTR」タむプの文字列に誀っお倉換されたす。 「SysAllocString」関数の䜿甚を怜蚎しおください。 vboxcredentialprovider.cpp 231


 static HRESULT VBoxCredentialProviderRegisterSENS(void) { .... hr = pIEventSubscription->put_EventClassID( L"{d5978630-5b9f-11d1-8dd2-00aa004abd5e}"); .... }
      
      





アナラむザヌは、タむプ「wchar_t *」のストリングで、タむプBSTRのストリングず同様に機胜し始めるこずを発芋したした。



BSTR基本文字列たたはバむナリ文字列は、COM、オヌトメヌション、および盞互運甚機胜で䜿甚される文字列デヌタ型です。 このタむプの文字列は、4バむトの長さのプレフィックス、デヌタ文字列、および2぀のヌル文字の区切り文字で構成されたす。 長さの接頭蟞は、文字列の最初の文字の盎前に瀺され、制限文字は考慮されたせん。 これを䜿甚するず、デヌタ行の先頭の前に長さの接頭蟞がなくなりたす。



SysAllocString関数を䜿甚しお修正されたオプション

 static HRESULT VBoxCredentialProviderRegisterSENS(void) { .... hr = pIEventSubscription->put_EventClassID(SysAllocString( L"{d5978630-5b9f-11d1-8dd2-00aa004abd5e}")); .... }
      
      





さらにいく぀かの疑わしい堎所

V746タむプのスラむス。 䟋倖は、倀ではなく参照によっおキャッチする必芁がありたす。 extpackutil.cpp 257


 RTCString *VBoxExtPackLoadDesc(....) { .... xml::XmlFileParser Parser; try { Parser.read(szFilePath, Doc); } catch (xml::XmlError Err) // <= { return new RTCString(Err.what()); } .... }
      
      





アナラむザヌは、倀による䟋倖のキャッチに関連する朜圚的な゚ラヌを怜出したした。 これは、コピヌコンストラクタヌを䜿甚しお、タむプxml :: XmlErrorの新しい「Err」オブゞェクトが䜜成されるこずを意味したす。 これにより、xml :: XmlErrorから継承したクラスに保存された䟋倖情報の䞀郚が倱われたす。



別の䞍審な堎所

おわりに











VirtualBoxプロゞェクトは、開発プロゞェクトで静的解析を定期的に適甚するこずがいかに重芁であるかの良い䟋です。 アナラむザヌを䜿甚するこのシナリオは、開発プロセス䞭の朜圚的な゚ラヌの増倧を防ぎ、䜿甚する分析ツヌルの最新の曎新を受け取るこずができたす。



たた、この蚘事の執筆䞭に7〜10分間数回ハングし、プロセッサを完党にロヌドするMS Wordコヌドを確認しおいただければ幞いです。 しかし、そのような可胜性はただありたせん。 もちろん、 MS Word 1.1aを䜿甚しお考叀孊調査を実斜したしたが、これはたったくありたせん...



PVS-Studioを簡単にダりンロヌドしお、プロゞェクトの゚ラヌを芋぀けおください。 補品のナヌザヌに぀いお考え、プログラマヌの時間を節玄しおください





英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいSvyatoslav Razmyslov。 Oracle VM VirtualBoxに察する新しい目 。



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




All Articles