• Save
Сравнение статического анализа общего назначения из Visual Studio 2010 и PVS-Studio на примере обнаруженных ошибок в пяти открытых проектах
Upcoming SlideShare
Loading in...5
×
 

Сравнение статического анализа общего назначения из Visual Studio 2010 и PVS-Studio на примере обнаруженных ошибок в пяти открытых проектах

on

  • 497 views

В статье показаны ошибки, выявленные с помощью статического анализатора кода, встроенного в Visual Studio 2010. ...

В статье показаны ошибки, выявленные с помощью статического анализатора кода, встроенного в Visual Studio 2010. Исследование проводилось на пяти open source проектах. Эти же проекты были проверены с помощью PVS-Studio. Приведены результаты сравнения этих двух инструментов.

Statistics

Views

Total Views
497
Views on SlideShare
497
Embed Views
0

Actions

Likes
0
Downloads
0
Comments
0

0 Embeds 0

No embeds

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Сравнение статического анализа общего назначения из Visual Studio 2010 и PVS-Studio на примере обнаруженных ошибок в пяти открытых проектах Сравнение статического анализа общего назначения из Visual Studio 2010 и PVS-Studio на примере обнаруженных ошибок в пяти открытых проектах Document Transcript

  • Сравнение статического анализаобщего назначения из Visual Studio2010 и PVS-Studio на примереобнаруженных ошибок в пятиоткрытых проектахАвторы: Евгений РыжковДата: 20.04.2011АннотацияВ статье показаны ошибки, выявленные с помощью статического анализатора кода, встроенного вVisual Studio 2010. Исследование проводилось на пяти open source проектах. Эти же проекты былипроверены с помощью PVS-Studio. Приведены результаты сравнения этих двух инструментов.ВведениеВ статье "Трудности сравнения анализаторов кода или не забывайте об удобстве использования"[1] говорится о том, что сравнить между собой два инструмента не так просто как кажется, потомучто помимо собственно технических характеристик анализаторов очень большое значение имееттакой параметр как удобство использования.Но все-таки от сравнения по обнаруживаемым ошибкам никуда не деться. Естественно, простопосчитать их количество – смысла нет. Поэтому мы решили провести практический экспериментпо обнаружению ошибок в реальных проектах.Мы проверили пять случайных open source проектов сначала c помощью статическогоанализатора, встроенного в Visual Studio 2010 Premium. Просмотрели весь список сообщений ивыбрали явные ошибки. Затем также поступили с PVS-Studio.Вот список проектов, участвовавших в исследовании: • eMule Plus; • Pixie; • VirtualDub; • WinMerge; • XUIFramework.Итак, поехали!
  • eMule PlusВсего сообщений от статического анализатора Visual Studio – 237. Из них реальных ошибок – 4.Всего сообщений от PVS-Studio – 68. Из них реальных ошибок – 3.Visual Studio: warning C6054: String szwThemeFile might not bezero-terminated. c:emuleplusdialogmintraybtn.hpp 445WCHAR szwThemeFile[MAX_PATH];WCHAR szwThemeColor[256];if (m_themeHelper.GetCurrentThemeName(szwThemeFile, ARRSIZE(szwThemeFile), szwThemeColor, ARRSIZE(szwThemeColor), NULL, 0) != S_OK) return NULL;WCHAR *p;if ((p = wcsrchr(szwThemeFile, L)) == NULL)Действительно строка может не заканчиваться на 0, что приведет к потенциальным проблемам.Однако в данном случае эта ошибка не проявит себя, скорее всего.Visual Studio: warning C6269: Possibly incorrect order of operations:dereference ignored. c:emulepluscustomautocomplete.cpp 277PVS-Studio: V532 Consider inspecting the statement of *pointer++pattern. Probably meant: (*pointer)++. customautocomplete.cpp 277if (pceltFetched != NULL) *pceltFetched++;Здесь программист "не умеет" пользоваться выражением (*ptr)++. Хотя, казалось бы, такаяконструкция выглядит достаточно безобидно, тем не менее, подобная ошибка оченьраспространена.Visual Studio: warning C6298: Argument 6: using a read-only stringas a writable string argument. This will attempt to write into static
  • read-only memory and cause random crashes.c:emuleplusfirewallopener.cpp 183HRESULT hr = pNSC->AddPortMapping( riPortRule.m_strRuleName.AllocSysString(), riPortRule.m_byProtocol, riPortRule.m_nPortNumber, riPortRule.m_nPortNumber, 0, L"127.0.0.1", ICSTT_IPADDRESS, &pNSPM);Хотя это и не ошибка, но сообщение анализатора справедливое. Вообще это проблема всехинструментов статического анализа. Они выдают совершенно корректные сообщения, но далеконе всегда это действительно ошибки. Означает ли это, что инструменты и подобные сообщениябесполезны? Нет, не означает, поскольку исправление даже подобных замечаний в целомповышает качество кода.Visual Studio: warning C6314: Incorrect order of operations: bitwise-or has higher precedence than the conditional-expression operator.Add parentheses to clarify intent. c:emuleplussearchlistctrl.cpp 659PVS-Studio: V502 Perhaps the ?: operator works in a different waythan it was expected. The ?: operator has a lower priority than the| operator. searchlistctrl.cpp 659menuSearchFile.AppendMenu( MF_STRING | ((iSelectionMark != -1) && (dwSelectedCount > 0) && g_App.m_pServerConnect->IsConnected() && ((pCurServer = g_App.m_pServerConnect->GetCurrentServer())!= NULL)&& (pCurServer->GetTCPFlags() & SRV_TCPFLG_RELATEDSEARCH)) ? MF_ENABLED : MF_GRAYED, MP_SEARCHRELATED, GetResString(IDS_SEARCHRELATED));Здесь (из-за сложности конструкции) получился неправильный приоритет операторов. Уж сколькораз твердили миру... Ну вот кто мешал писать это все не в одну строку (как в оригинальнойпрограмме), а разбить на несколько отдельных выражений? Нет же, программисты часто хотят"записать красиво". View slide
  • PVS-Studio: V519 The m_clrSample object is assigned values twicesuccessively. Perhaps this is a mistake. fontpreviewcombo.cpp 61CFontPreviewCombo::CFontPreviewCombo(){ ... m_clrSample = GetSysColor(COLOR_WINDOWTEXT); m_clrSample = RGB(60,0,0); ...}Возможно, попробовали как будет смотреться цвет RGB(60,0,0) и забыли убрать.PixieВсего сообщений от статического анализатора Visual Studio – 18. Из них реальных ошибок – 0.Всего сообщений от PVS-Studio – 65. Из них реальных ошибок – 5.PVS-Studio: V519 The numRays object is assigned values twicesuccessively. Perhaps this is a mistake. bundles.cpp 579void CGatherBundle::post() { numRays = last; numRays = 0; last = 0; depth++;}Ой, как подозрительно, что сначала numRays инициализируется одним значением, а затемдругим. Ошибка? Или нет? Знает только автор кода. Но настораживает!PVS-Studio: V501 There are identical sub-expressions to the left andto the right of the | operator: PARAMETER_DPDU | PARAMETER_DPDU View slide
  • quadrics.cpp 880if (up & (PARAMETER_DPDU | PARAMETER_DPDU)) {Явно здесь должно быть что-то еще. Кстати общее замечание по исправлению ошибок,обнаруженных статическим анализатором. Если в каких-то случаях исправление очевидно и егоможет сделать любой, то в каких-то лишь автор кода может разобраться с тем, что же он хотелнаписать. Это к вопросу о том, а нельзя ли сделать инструмент, который предлагает исправлениеошибок "как в Ворде".PVS-Studio: V501 There are identical sub-expressions to the left andto the right of the | operator: SLC_VECTOR | SLC_VECTORexpression.cpp 2604lock(N, getConversion(SLC_VECTOR | SLC_VECTOR,parameters[2]));Дважды упомянутый SLC_VECTOR в подобном контексте – конечно же, признак ошибки.PVS-Studio: V505 The alloca function is used inside the loop.This can quickly overflow stack. polygons.cpp 1120inline void triangulatePolygon(...) { ... for (i=1;i<nloops;i++) { ... do { ... do { ... CTriVertex *snVertex = (CTriVertex *) alloca(2*sizeof(CTriVertex)); ... } while(dVertex != loops[0]);
  • ... } while(sVertex != loops[i]); ... } ...}Из-за большой вложенности вызов alloca может привести к переполнению стека.VirtualDubВсего сообщений от статического анализатора Visual Studio – 24. Из них реальных ошибок – 0.Всего сообщений от PVS-Studio – 41. Из них реальных ошибок – 2.PVS-Studio: V547 Expression c < 0 is always false.Unsigned type value is never < 0. lexer.cpp 279typedef unsigned short wint_t;wint_t lexgetescape() { wint_t c = lexgetc(); if (c < 0) fatal("Newline found in escape sequence"); ...}Код никогда не будет вызван, поскольку выражение всегда false.PVS-Studio: V557 Array overrun is possible. The 9 index is pointingbeyond array bound. f_convolute.cpp 73struct ConvoluteFilterData { long m[9];
  • long bias; void *dyna_func; DWORD dyna_size; DWORD dyna_old_protect; BOOL fClip;};static unsigned long __fastcall do_conv( unsigned long *data, const ConvoluteFilterData *cfd, long sflags, long pit){ long rt0=cfd->m[9], gt0=cfd->m[9], bt0=cfd->m[9]; ...}Банальный выход за границы массива.WinMergeВсего сообщений от статического анализатора Visual Studio – 343. Из них реальных ошибок – 3.Всего сообщений от PVS-Studio – 69. Из них реальных ошибок – 12.Visual Studio: warning C6313: Incorrect operator: zero-valued flagcannot be tested with bitwise-and. Use an equality test to check forzero-valued flags. c:winmergesrcbcmenu.cpp 1489else if (nFlags&MF_STRING){ ASSERT(!(nFlags&MF_OWNERDRAW)); ModifyMenu(pos,nFlags,nID,mdata->GetString());}Неудачно записано условие. Хотели, конечно записать другое, но уж "что получилось".
  • Visual Studio: warning C6287: Redundant code: the left and rightsub-expressions are identical.c:winmergesrceditlibccrystaleditview.cpp 1560PVS-Studio: V501 There are identical sub-expressions to the left andto the right of the || operator: c == } || c == }ccrystaleditview.cpp 1560boolisclosebrace (TCHAR c){ return c == _T (}) || c == _T (}) || c == _T (]) || c == _T (>);}Не все типы скобочек проверяются. Почему? "Copy-paste-technology", как это нередко бывает,приводит к ошибкам.Visual Studio: warning C6287: Redundant code: the left and rightsub-expressions are identical. c:winmergesrcmergedoc.cpp 1165PVS-Studio: V501 There are identical sub-expressions to the left andto the right of the || operator. mergedoc.cpp 1165if ((m_nBufferType[nBuffer] == BUFFER_UNNAMED) || (m_nBufferType[nBuffer] == BUFFER_UNNAMED)) nSaveErrorCode = SAVE_NO_FILENAME;Опять неудачное условие. И опять, похоже, из-за копи-паста.PVS-Studio: V551 The code under this case label is unreachable.The value range of signed char type: [-128, 127].
  • ccrystaltextview.cpp 1646TCHAR ch = strText[i];switch (ch){ case 0xB7: case 0xBB: strHTML += ch; strHTML += _T("<wbr>"); bLastCharSpace = FALSE; nNonbreakChars = 0; break;А вот здесь пример кода, который никогда не будет использован. Вроде бы и case написан, даникогда он не получит управление. Потому что диапазон значений слишком узкий. TCHAR вданном случае представляет собой тип char.PVS-Studio: V524 It is odd that the body of IsValidTextPosX functionis fully equivalent to the body of IsValidTextPos function(ccrystaltextview.cpp, line 3700). ccrystaltextview.cpp 3707bool CCrystalTextView::IsValidTextPos (const CPoint &point){ return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 && point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);}bool CCrystalTextView::IsValidTextPosX (const CPoint &point){
  • return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 && point.y >= 0 && point.y < GetLineCount () && point.x >= 0 && point.x <= GetLineLength (point.y);}bool CCrystalTextView::IsValidTextPosY (const CPoint &point){ return GetLineCount () > 0 && m_nTopLine >= 0 && m_nOffsetChar >= 0 && point.y >= 0 && point.y < GetLineCount ();}Крайне похожие функции... Снова и снова копи-пастили и забыли поправить результат. ФункцияIsValidTextPosX() делает лишнюю проверку.PVS-Studio: V563 It is possible that this else branch must apply tothe previous if statement. bcmenu.cpp 1852if(IsLunaMenuStyle()) if(!xp_space_accelerators)return;else if(!original_space_accelerators)return;Кто, глядя на код, точно скажет, к какому if относится else? А это ли хотел сделать программист? Авы уверены?PVS-Studio: V556 The values of different enum types are compared:switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. diffwrapper.cpp 956enum output_style {}...
  • enum DiffOutputType m_outputStyle;switch (m_options.m_outputStyle){ case OUTPUT_CONTEXT:Немножечко попутали тип enum в switch. Но это ведь не страшно? Или страшно?PVS-Studio: V530 The return value of function empty is required tobe utilized. diractions.cpp 1307void CDirView::GetItemFileNames(int sel, String& strLeft, String& strRight) const{ UINT_PTR diffpos = GetItemKey(sel); if (diffpos == (UINT_PTR)SPECIAL_ITEM_POS) { strLeft.empty(); strRight.empty();Тот случай, когда empty() не делает empty. Пример крайне неудачного названия метода.PVS-Studio: V524 It is odd that the body of OnUpdateLastdifffunction is fully equivalent to the body of OnUpdateFirstdifffunction (DirView.cpp, line 2189). dirview.cpp 2220void CDirView::OnUpdateLastdiff(CCmdUI* pCmdUI){ int firstDiff = GetFirstDifferentItem(); if (firstDiff > -1) pCmdUI->Enable(TRUE); else
  • pCmdUI->Enable(FALSE);}void CDirView::OnUpdateFirstdiff(CCmdUI* pCmdUI){ int firstDiff = GetFirstDifferentItem(); if (firstDiff > -1) pCmdUI->Enable(TRUE); else pCmdUI->Enable(FALSE);}Еще две крайне похожие функции...PVS-Studio: V501 There are identical sub-expressionspView1->GetTextBufferEol (line) to the left and to the right ofthe != operator. mergedoclinediffs.cpp 216if (pView1->GetTextBufferEol(line) != pView1->GetTextBufferEol(line)){Или то, или то... Или не то? Наверное, здесь должно быть pView2.PVS-Studio: V530 The return value of function empty is required tobe utilized. varprop.cpp 133void VariantValue::Clear(){ m_vtype = VT_NULL; m_bvalue = false; m_ivalue = 0;
  • m_fvalue = 0; m_svalue.empty(); m_tvalue = 0;}И опять empty() вовсе не очищает строку.PVS-Studio: V510 The Format function is not expected to receiveclass-type variable as N actual argument". PropShel 105String GetSysError(int nerr);...CString msg;msg.Format( _T("Failed to open registry key HKCU/%s:nt%d : %s"), f_RegDir, retVal, GetSysError(retVal) );При возникновении различных аварийных ситуаций WinMerge попробует сообщить об ошибках,но в некоторых случаях у него это плохо получится. На первый взгляд все хорошо. Вот только тип"String" есть не что иное как "std::wstring". А, следовательно, в лучшем случае мы распечатаемабракадабру, а в худшем произойдет ошибка доступа к памяти (Access Violation). Корректный коддолжен содержать вызов c_str().PVS-Studio: V534 It is likely that a wrong variable is being comparedinside the for operator. Consider reviewing i." BinTrans.cpp 357// Get length of translated array of bytes from text.int Text2BinTranslator::iLengthOfTransToBin( char* src, int srclen ){ ... for (k=i; i<srclen; k++) {
  • if (src[k]==>) break; } ...}Анализатор нашел подозрительный цикл. Этот код предрасположен к Access Violation. Циклдолжен продолжаться пока не найдется символ > или не закончится строка длиной в srclenсимволов. Вот только случайно для сравнения использована переменная i, а не k. Если символ> найден не будет, то все вероятно закончится печально.XUIFrameworkВсего сообщений от статического анализатора Visual Studio – 93. Из них реальных ошибок – 2.Всего сообщений от PVS-Studio – 30. Из них реальных ошибок – 5.Visual Studio: warning C6269: Possibly incorrect order of operations:dereference ignoredc:xui-gui frameworkwidgetscstatichtmlppdrawmanager.cpp 298PVS-Studio: V532 Consider inspecting the statement of *pointer++pattern. Probably meant: (*pointer)++. ppdrawmanager.cpp 298for (DWORD pixel = 0; pixel < dwWidth * dwHeight; pixel++, *pBits++)Опять кто-то не умеет пользоваться *ptr++. Как говорилось выше, ошибка распространенная.Visual Studio: warning C6283: pBuffer is allocated with array new[],but deleted with scalar delete.c:xui-gui frameworkwidgetscxstaticcxstatic.cpp 544BYTE* pBuffer = new BYTE [ nBufferLen ];...delete pBuffer;
  • Путают люди delete и delete[]. Это приводит к проблемам. Иногда проблемы проявляются, иногданет. Но так делать не надо.PVS-Studio: V519 The m_xSt object is assigned values twicesuccessively. Perhaps this is a mistake. resizedlg.cpp 244m_xSt = CST_RESIZE;m_xSt = CST_RESIZE;Судя по коду, там должно быть m_ySt. Но от копи-паста ведь снова и снова не удержаться, да?V531 It is odd that a sizeof() operator is multiplied by sizeof().pphtmldrawer.cpp 258DWORD dwLen = ::LoadString(hInstDll, dwID, szTemp, (sizeof(szTemp) * sizeof(TCHAR)));Должно быть sizeof(szTemp) / sizeof(TCHAR) .PVS-Studio: V556 The values of different enum types are compared:enuHAlign == Center. cxstatic.cpp 151if (enuHAlign == Center)Должно быть enuHAlign == Midle. И в коде рядом есть еще if (enuVAlign == Middle), хотя должнобыть Center. Попутали enum, короче.PVS-Studio: V501 There are identical sub-expressions to the left andto the right of the || operator. resizedlg.cpp 157HDWP CItemCtrl::OnSize(...){ ...
  • if (m_styTop == CST_ZOOM || m_styTop == CST_ZOOM || m_styBottom == CST_DELTA_ZOOM || m_styBottom == CST_DELTA_ZOOM) ...}Вероятно, код должен выглядеть так:HDWP CItemCtrl::OnSize(...){ ... if (m_styTop == CST_ZOOM || m_styTop == CST_DELTA_ZOOM || m_styBottom == CST_ZOOM || m_styBottom == CST_DELTA_ZOOM) ...}Результаты сравненияМы не делаем никаких конкретных выводов. В каких-то проектах лучше себя показал одининструмент, в каких-то – другой.Библиографический список 1. Андрей Карпов, Евгений Рыжков. Трудности сравнения анализаторов кода или не забывайте об удобстве использования. http://www.viva64.com/ru/a/0071/.