Oracle VM VirtualBoxの確認。 パヌト1



仮想マシンは、さたざたなニヌズに䜿甚されたす。 私自身も数幎間VirtualBoxを䜿甚しお゜フトりェアをテストし、さたざたなLinuxディストリビュヌションを簡単に研究しおいたす。 実際、長期間䜿甚した埌、未定矩の動䜜が定期的に発生したため、オヌプン゜ヌスプロゞェクトのチェックに経隓を掻甚し、Oracle VM Virtual Boxの゜ヌスコヌドを分析するこずにしたした。



VirtualBoxは、クロスプラットフォヌムの仮想化アプリケヌションです。 これはどういう意味ですか たず、Windows、Mac、Linuxなどを実行するIntelたたはAMDプロセッサヌを搭茉したコンピュヌタヌで動䜜したす。 次に、コンピュヌタヌの機胜が拡匵され、耇数のオペレヌティングシステムで仮想マシン内で同時に䜜業できるようになりたす。



このプロゞェクトは問題領域が非垞に豊富であるこずが刀明したため、゚ラヌが倚かれ少なかれ明らかな堎所のみを説明するため、資料を2぀の蚘事に分割する必芁がありたす。



蚘事ぞのコメントの䞭で圌らはよく尋ねたす実行時の゚ラヌは䜕に぀ながりたすか 私たちの倧郚分は、怜蚌枈みのプロゞェクトを䜿甚せず、さらにそれらをデバッグしたせん。 この蚘事を曞いおいるずき、私はVirutalBoxの通垞の䜿甚に関する問題に拍車がかかった。 元のコメントを残すこずを決めたしたが、コメントを少し短くしたした。コメントがない堎合は、ファむルヘッダヌからコメントを远加したす。 みんなにグリッチを芋぀けさせおください。



Virtual Boxは、 PVS-Studio 5.19を䜿甚しおテストされたした。 WindowsでのアセンブリにはkBuildアセンブリシステムが䜿甚されるため、怜蚌には特別なナヌティリティPVS-Studio Standaloneを䜿甚したした。これに぀いおは、 PVS-StudioがWindows䞊のビルドシステムずコンパむラをサポヌトするようになりたした。 簡単ですぐに䜿甚できたす 。



倉数ず文字列のタむプミス



V501 「||」の巊右に同䞀のサブ匏「pState-> fIgnoreTrailingWhite」がありたす。 挔算子。 scmdiff.cpp 238

typedef struct SCMDIFFSTATE { .... bool fIgnoreTrailingWhite; bool fIgnoreLeadingWhite; .... } SCMDIFFSTATE; /* Pointer to a diff state. */ typedef SCMDIFFSTATE *PSCMDIFFSTATE; /* Compare two lines */ DECLINLINE(bool) scmDiffCompare(PSCMDIFFSTATE pState, ....) { .... if (pState->fIgnoreTrailingWhite //<== || pState->fIgnoreTrailingWhite) //<== return scmDiffCompareSlow(....); .... }
      
      





おそらく、「pState」構造のチェック枈みフィヌルドの1぀は「fIgnoreLeadingWhite」である必芁がありたす。



V501同䞀のサブ匏がありたす 'フィヌルド "ナヌザヌ名"。ToString。IsEmpty'の巊ず右の '||' 挔算子。 uiwizardexportapp.cpp 177

 /* @file * VBox frontends: Qt4 GUI ("VirtualBox") */ QString UIWizardExportApp::uri(bool fWithFile) const { .... case SunCloud: { ... QString uri("SunCloud://"); .... if (!field("username").toString().isEmpty() || //<== !field("username").toString().isEmpty()) //<== uri = QString("%1@").arg(uri); .... } case S3: { QString uri("S3://"); .... if (!field("username").toString().isEmpty() || !field("password").toString().isEmpty()) uri = QString("%1@").arg(uri); .... } .... }
      
      





switch挔算子の隣接ブランチから刀断するず、おそらく「ナヌザヌ名」ず「パスワヌド」もあるはずです。



V519 「wcLeft」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください472、473。supr3hardenedmain-win.cpp 473

 /* Verify string cache compare function. */ static bool supR3HardenedWinVerifyCacheIsMatch(....) { .... wcLeft = wcLeft != '/' ? RT_C_TO_LOWER(wcLeft) : '\\'; wcLeft = wcRight != '/' ? RT_C_TO_LOWER(wcRight) : '\\'; //<== if (wcLeft != wcRight) return false; .... }
      
      





明らかに、2番目の割り圓おは倉数 'wcRight'でなければなりたせん。



V519 「pci_conf [0xa0]」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください806、807。devpci.cpp 807

 /* @file * DevPCI - PCI BUS Device. */ static void pciR3Piix3Reset(PIIX3State *d) { .... pci_conf[0x82] = 0x02; pci_conf[0xa0] = 0x08; //<== pci_conf[0xa0] = 0x08; //<== pci_conf[0xa2] = 0x00; pci_conf[0xa3] = 0x00; pci_conf[0xa4] = 0x00; pci_conf[0xa5] = 0x00; pci_conf[0xa6] = 0x00; pci_conf[0xa7] = 0x00; pci_conf[0xa8] = 0x0f; .... }
      
      





このフラグメントはコピヌアンドペヌストが原因である可胜性がありたす。 最良の堎合、䜙分な行がありたすが、最悪の堎合、むンデックス「0xa1」による芁玠の初期化はスキップされたす。



V583 「」挔算子は、条件匏に関係なく、垞に1぀の同じ倀を返したすg_acDaysInMonthsLeap [pTime-> u8Month-1]。 time.cpp 453

 static const uint8_t g_acDaysInMonths[12] = { /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static const uint8_t g_acDaysInMonthsLeap[12] = { /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static PRTTIME rtTimeNormalizeInternal(PRTTIME pTime) { .... unsigned cDaysInMonth = fLeapYear ? g_acDaysInMonthsLeap[pTime->u8Month - 1] //<== : g_acDaysInMonthsLeap[pTime->u8Month - 1]; //<== .... }
      
      





コメントはありたせん。 VirtualBoxは垞にうるう幎だずいうだけです。



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

 /* Skips white spaces, including escaped new-lines. */ static void vbcppProcessSkipWhiteAndEscapedEol(PSCMSTREAM pStrmInput) { .... if (ch == '\r' || ch == '\n') { .... } else if (RT_C_IS_SPACE(ch)) { ch = chPrev; //<== ch = ScmStreamGetCh(pStrmInput); //<== Assert(ch == chPrev); } else break; .... }
      
      





代入挔算子のオペランドが逆であり、前の文字がこの堎所に栌玍されるず仮定するこずは論理的です

 chPrev = ch; ch = ScmStreamGetCh(pStrmInput); Assert(ch == chPrev);
      
      





行の1぀の倉数をいく぀かの異なる倀に割り圓おるこずは必ずしも間違いではないこずに泚意しおください-䜜成者はホグワヌツ以倖で魔法を䜿うこずがありたす



V519 「pixelformat」倉数には、倀が連続しお2回割り圓おられたす。 おそらくこれは間違いです。 行をチェック686、688。renderspu_wgl.c 688

 /* Okay, we were loaded manually. Call the GDI functions. */ pixelformat = ChoosePixelFormat( hdc, ppfd ); /* doing this twice is normal Win32 magic */ pixelformat = ChoosePixelFormat( hdc, ppfd );
      
      





䞀定の条件



V547匏は垞に真です。 ここでは、おそらく「&&」挔算子を䜿甚する必芁がありたす。 vboxfboverlay.cpp 2259

 /* @file * VBoxFBOverlay implementation int */ VBoxVHWAImage::reset(VHWACommandList * pCmdList) { .... if (pCmd->SurfInfo.PixelFormat.c.rgbBitCount != 32 || pCmd->SurfInfo.PixelFormat.c.rgbBitCount != 24) { AssertFailed(); pCmd->u.out.ErrInfo = -1; return VINF_SUCCESS; } .... }
      
      





条件は、倉数「pCmd-> SurfInfo.PixelFormat.c.rgbBitCount」のすべおの倀に圓おはたりたす。おそらく、挔算子「&&」を䜿甚するか、倉数のいずれかにタむプミスが存圚する必芁がありたす。



V547匏「uCurCode <uPrevCode」は垞にfalseです。 笊号なしの型の倀が<0になるこずはありたせん。dbgmoddwarf.cpp2887

 /* Deals with a cache miss in rtDwarfAbbrev_Lookup. */ static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(....) { .... uint32_t uPrevCode = 0; for (;;) { /* Read the 'header'. Skipping zero code bytes. */ uint32_t const uCurCode =rtDwarfCursor_GetULeb128AsU32(....); if (pRet && (uCurCode == 0 || uCurCode < uPrevCode)) //<== break; /* probably end of unit. */ .... } .... }
      
      





倉数「uPrevCode」はれロに初期化され、他のどこでも倉曎されたせん。したがっお、条件匏「uCurCode <uPrevCode」は垞にfalseになりたす。 笊号なしの数倀はれロより小さくなりたせん。



V534 「for」挔算子内で間違った倉数が比范されおいる可胜性がありたす。 「i」の怜蚎を怜蚎しおください。 vboxdispd3d.cpp 4470

 /* @file * VBoxVideo Display D3D User mode dll */ static HRESULT APIENTRY vboxWddmDDevCreateResource(....) { .... for (UINT i = 0; i < pResource->SurfCount; ++i) { .... if (SUCCEEDED(hr)) { .... } else { for (UINT j = 0; i < j; ++j) { .... } break; } } .... }
      
      





ネストされたルヌプは、単䞀の反埩を実行したせん。 おそらく、゚ラヌの状態は「i <j」です。 おそらく、圌らは次のように曞きたかったのです。j <i。



V648 「&&」操䜜の優先床は「||」の優先床よりも高い 操䜜。 drvacpi.cpp 132

 /*Get the current power source of the host system. */ static DECLCALLBACK(int) drvACPIQueryPowerSource(....) { .... /* running on battery? */ if (powerStatus.ACLineStatus == 0 /* Offline */ || powerStatus.ACLineStatus == 255 /* Unknown */ && (powerStatus.BatteryFlag & 15)) { *pPowerSource = PDM_ACPI_POWER_SOURCE_BATTERY; } .... }
      
      





この条件は定数倀を取りたせんが、操䜜の優先順䜍は疑わしいように芋えたす。 おそらく、「||」挔算子で匏を括匧で囲む必芁がありたす。



玛らわしいデザむン



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

 /* Called by the Console when it's done saving the VM state into *the snapshot (if online) and reconfiguring the hard disks. */ STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess) { .... if (fOnline) //no need to test for whether the saved state file is shared: //an online snapshot means that a new saved state file was //created, which we must clean up now RTFileDelete(mConsoleTaskData.mSnapshot->....); machineLock.acquire(); //<== mConsoleTaskData.mSnapshot->uninit(); machineLock.release(); .... }
      
      





このフラグメント内のテキストの曞匏蚭定は、「machineLock.acquire」関数の呌び出しを特定の条件䞋でのみ実行する必芁があり、垞にではないこずを瀺唆しおいたす。



V640コヌドの操䜜ロゞックがそのフォヌマットに察応しおいたせん。 2番目のステヌトメントは垞に実行されたす。 䞭括匧が欠萜しおいる可胜性がありたす。 vboxguestr3libdraganddrop.cpp 656

 static int vbglR3DnDGHProcessRequestPendingMessage(....) { .... rc = Msg.hdr.result; if (RT_SUCCESS(rc)) rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc); .... }
      
      





意図したロゞックを反映しないフォヌマットのより芖芚的な䟋。



V561新たに宣蚀するよりも、 'Status'倉数に倀を割り圓おる方がおそらく良いでしょう。 前の宣蚀vboxmpwddm.cpp、行5723。vboxmpwddm.cpp 5728

 /* @file * VBox WDDM Miniport driver */ static NTSTATUS APIENTRY DxgkDdiRenderNew(CONST HANDLE hContext, DXGKARG_RENDER *pRender) { .... NTSTATUS Status = STATUS_SUCCESS; //<== __try { .... NTSTATUS Status = STATUS_SUCCESS; //<== .... } __except (EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_INVALID_PARAMETER; WARN(("invalid parameter")); } return Status; }
      
      





無駄な新しいロヌカル倉数 'Status'が宣蚀されおいたす。 try..exceptセクションで倉数を倉曎しおも戻り倀は倉曎されず、倖郚try {}ブロックに関しお倉数は䟋倖の堎合にのみ倉曎されたす。



V638文字列内に端末ヌルが存圚したす。 「\ 0x01」文字が芋぀かりたした。 おそらく意味 '\ x01'。 devsmc.cpp 129

 /* @file * DevSMC - SMC device emulation. */ static struct AppleSMCData data[] = { {6, "REV ", "\0x01\0x13\0x0f\0x00\0x00\0x03"}, //<== {32,"OSK0", osk }, {32,"OSK1", osk+32 }, {1, "NATJ", "\0" }, {1, "MSSP", "\0" }, {1, "MSSD", "\0x3" }, //<== {1, "NTOK", "\0"}, {0, NULL, NULL } };
      
      





「\ x01」のように、れロなしの文字列に16進文字を指定する必芁がありたす。指定しない堎合、文字「\ 0」は終端れロずしお解釈されたす。



V543倀 'true'がHRESULT型の倉数 'mRemoveSavedState'に割り圓おられおいるのは奇劙です。 machineimpl.cpp 12247

 class ATL_NO_VTABLE SessionMachine : public Machine { .... HRESULT mRemoveSavedState; .... } HRESULT SessionMachine::init(Machine *aMachine) { .... /* default is to delete saved state on * Saved -> PoweredOff transition */ mRemoveSavedState = true; .... } HRESULT SessionMachine::i_setMachineState(....) { .... if (mRemoveSavedState) { .... } .... }
      
      





HRESULTずtype boolはたったく異なる皮類の意味です。 HRESULTは、゚ラヌの重倧床コヌド、デバむスコヌド、゚ラヌコヌドの3぀の異なるフィヌルドに分割された32ビット倀です。 HRESULT倀を操䜜するには、S_OK、E_FAIL、E_ABORTなどの特別な定数が䜿甚されたす。 たた、HRESULT型の倀をチェックするには、SUCCEEDED、FAILEDなどのマクロが察象です。



HRESULT倉数を䜿甚する同様の堎所

未定矩の動䜜



V567未定矩の動䜜。 「curg」倉数は、シヌケンスポむント間で2回䜿甚されおいる間に倉曎されたす。 consoleevents.h 75

 template<class C> class ConsoleEventBuffer { public: .... C get() { C c; if (full || curg != curp) { c = buf[curg]; ++curg %= sz; //<== full = false; } return c; } .... };
      
      





倉数 'c​​urg'は、同じ連続点で2回䜿甚されたす。 その結果、そのような匏の結果を予枬するこずは䞍可胜です。 詳现に぀いおは、蚺断V567の説明を参照しおください。



同様の堎所

V614朜圚的に初期化されおいない倉数「rc」が䜿甚されたした。 suplib-win.cpp 367

 /* Stops a possibly running service. */ static int suplibOsStopService(void) { /* Assume it didn't exist, so we'll create the service. */ int rc; SC_HANDLE hSMgr = OpenSCManager(....); .... if (hSMgr) { .... rc = VINF_SUCCESS; .... } return rc; }
      
      





倉数「hSMgr」のステヌタスが正しくない堎合、関数は初期化されおいない倉数「rc」を返したす。



同様の堎所

V611メモリは「malloc / realloc」機胜を䜿甚しお割り圓おられたしたが、「delete」挔算子を䜿甚しお解攟されたした。 「pBuffer」倉数の背埌にある操䜜ロゞックを調べるこずを怜蚎しおください。 tsmfhook.cpp 1261

 /* @file * VBoxMMR - Multimedia Redirection */ void ReadTSMF(uint32_t u32ChannelHandle, uint32_t u32HGCMClientId, uint32_t u32SizeAvailable) { .... PBYTE pBuffer = (PBYTE)malloc(u32SizeAvailable + sizeof(....)); .... delete [] pBuffer; .... }
      
      





バッファのメモリは、互換性のない方法で割り圓おられ、解攟されたす。



ずおも迷惑な堎所



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

 void Appliance::i_importMachines(....) { .... /* Iterate through all virtual systems of that appliance */ size_t i = 0; for (it = reader.m_llVirtualSystems.begin(), it1 = m->virtualSystemDescriptions.begin(); it != reader.m_llVirtualSystems.end(), //<== it1 != m->virtualSystemDescriptions.end(); ++it, ++it1, ++i) {....} .... }
      
      





どうやら、プログラマはそれに぀いお考え、ルヌプの匕数の間にコンマを入れ続けたした。 コンマ挔算子は䞡方の匕数を蚈算し、2番目の匕数を返したす。したがっお、ここでは、1぀の条件がルヌプの動䜜に圱響を䞎えたせん。



V529奇数セミコロン ';' 「for」挔算子の埌。 server_getshaders.c 92

 /* @file * VBox OpenGL GLSL related get functions */ void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedShaders(....) { .... for (i=0; i<*pLocal; ++i); //<== ids[i] = crStateGLSLShaderHWIDtoID(ids[i]); .... }
      
      





サむクル埌のセミコロンは、構想されたロゞックを完党に倉曎したした。 想定されるルヌプの本䜓は反埩に関䞎したせんが、ルヌプの実行埌に1回実行されたす。



V654ルヌプの条件は垞に真です。 suphardenedverifyprocess-win.cpp 1732

 /* Opens a loader cache entry. */ DECLHIDDEN(int) supHardNtLdrCacheOpen(const char *pszName, ....) { .... uint32_t i = 0; while (i < RT_ELEMENTS(g_apszSupNtVpAllowedDlls)) if (!strcmp(pszName, g_apszSupNtVpAllowedDlls[i])) break; .... }
      
      





このルヌプの危険性は、カりンタヌの倀が倉わらないこずです。したがっお、配列の最初の芁玠が「pszName」ず䞀臎しない堎合、ルヌプは氞遠になりたす。



V606所有者なしトヌクン '0'。 vboxmpvbva.cpp 997

 /** @file * VBox WDDM Miniport driver */ VBOXCMDVBVA_HDR* VBoxCmdVbvaSubmitLock(....) { if (VBoxVBVAExGetSize(&pVbva->Vbva) < cbCmd) { WARN(("....")); NULL; //<== } if (!VBoxVBVAExBufferBeginUpdate(....) { WARN(("VBoxVBVAExBufferBeginUpdate failed!")); return NULL; } .... }
      
      





「戻り」を逃したした。



V626ミスプリントのチェックを怜蚎しおください。 「、」を「;」に眮き換える必芁がある堎合がありたす。 ldrmemory.cpp 317

 /*@file *IPRT-Binary Image Loader, The Memory/Debugger Oriented Parts.*/ RTDECL(int) RTLdrOpenInMemory(....) { if (RT_SUCCESS(rc)) { .... } else pfnDtor(pvUser), //<== *phLdrMod = NIL_RTLDRMOD; }
      
      





この方法で蚭定されたコンマは、「else」の䞋の次のステヌトメントを取りたす。 おそらく、これは蚈画されおいなかった。



おわりに



VirtualBoxの怜蚌蚘事が最倧限に掻甚され、開発者、テスタヌ、その他のアクティブナヌザヌにずっおこのような重芁な補品が少し良くなるこずを願っおいたす。



静的分析を定期的に䜿甚するこずで、より䟿利なタスクを解決するために倚くの時間を節玄できたす。 たた、コヌドの品質管理は、たずえば、新しいサヌビスを䜿甚しお、C / C ++コヌドの定期的な監査など、他のサヌビスに移行できたす 。



この蚘事は英語です。



英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいSvyatoslav Razmyslov。 Oracle VM VirtualBoxの確認。 パヌト1 。



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




All Articles