Легко і просто перевіряємо Firefox за допомогою PVS-Studio Standalone

    PVS-Studio and Firefox
Три роки тому ми вже перевіряли Mozilla Firefox за допомогою аналізатора PVS-Studio. Тоді це було незручно і важко. Для Firefox відсутня проектний файл для Visual Studio. Збірка здійснюється за допомогою make-файлів. Тому просто взяти і перевірити проект не можна. Було потрібно інтегрувати PVS-Studio в систему збирання, що виявилося важким завданням. У результаті, як мені пам'ятається, була проаналізована тільки частину проекту. Але все змінилося, коли з'явився PVS-Studio Standalone. Тепер можна відстежити всі запуски компіляторів і легко перевірити проект.
 
  
 Mozilla Firefox
Думаю, розповідати про Firefox немає необхідності. Однак, формат статті вимагає все-таки написати трохи про перевіреному проекті. Полінуюся і скористаюся описом з Wikipedia:
 
Mozilla Firefox — вільний браузер на движку Gecko, розробкою та розповсюдженням якого займається Mozilla Corporation. Третій за популярністю браузер у світі й перший серед вільного ПЗ — у серпні 2013 його ринкова частка склала 19,26%. У браузері присутній інтерфейс з багатьма вкладками, перевірка орфографії, пошук у міру набору, «живі закладки», менеджер закачувань, поле для звернення до пошукових систем. Нові функції можна додавати за допомогою розширень.
 
Ми вже намагалися перевірити Firefox. Частково це нам навіть вдалося. За результатами перевірки була написана стаття " Як зменшити ймовірність помилки на етапі написання коду. Замітка N4 ". Складність полягала в тому, що потрібно вставити виклик command-line версії PVS-Studio в make-файли. Зробити це у великому незнайомому проекті буває важко. Саме тому ми надалі не робили спроб перевірити ще раз Firefox. Ситуація змінилася з появою PVS-Studio Standalone.
  
 PVS-Studio Standalone
PVS-Studio Standalone може бути використаний в 3 режимах:
     
  1. Зручна робота з файлом звіту (*. plog), який містить інформацію про знайдені помилки на комп'ютері, де не встановлено Visual Studio.
  2.  
  3. Перевірка яким-небудь чином препроцессірованних *. i файлів.
  4.  
  5. Відстеження запуску компілятора і збір всієї необхідної інформації для подальшої перевірки файлів. Нам зараз цікавий саме цей режим роботи.
  6.  
Тепер не обов'язково інтегрувати command-line версію PVS-Studio в make-файли. Можна перевірити Firefox набагато простіше. Ми саме так і зробили. Послідовність дій:
     
  1. Запускаємо програму PVS-Studio Standalone;
  2.  
  3. Виконуємо команду «Compiler Monitoring»;
  4.  
  5. Компілюємо проект Firefox;
  6.  
  7. Зупиняємо процедуру стеження («Stop Monitoring»);
  8.  
  9. Запускається перевірка файлів;
  10.  
  11. Вивчаємо попередження, видані аналізатором коду.
  12.  
Більш докладно, як використовувати цей режим, можна прочитати тут .
 
 Примітка
 
Крім PVS-Studio ми пропонуємо спрощений варіант аналізатора під назвою CppCat . У разі c Firefox, він не зможе виконати аналіз проекту. У ньому немає Standalone версії, а також немає command-line версії для інтеграції в make-файли. Однак, CppCat набагато простіше у вивченні і добре підходить індивідуальним розробникам і невеликим командам. Див також: " Порівняння можливостей статичних аналізаторів коду PVS-Studio і CppCat ".
 
 Результати перевірки Mozilla Firefox
Проект Firefox дуже якісний. У добавок, як я розумію, при його розробці вже застосовуються інструменти статичного аналіз коду: Coverity і Klocwork. Принаймні, я зустрічав згадки цих аналізаторів в деяких файлах.
 
Тому знайти хоч щось — це вже досягнення. Давайте подивимося, які попередження аналізатора PVS-Studio здалися мені цікавими.
 
 Друкарська помилка N1
 
NS_IMETHODIMP
nsNativeThemeWin::WidgetStateChanged(....)
{
  ....
  if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
      aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
      aWidgetType == NS_THEME_WINDOW_FRAME_LEFT ||
      aWidgetType == NS_THEME_WINDOW_FRAME_RIGHT ||
      aWidgetType == NS_THEME_WINDOW_FRAME_BOTTOM ||
      aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE ||
      aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||   <<<===
      aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||   <<<===
      aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
    *aShouldRepaint = true;
    return NS_OK;
  ....
}

Попередження PVS-Studio: V501 There are identical sub-expressions 'aWidgetType == 237' to the left and to the right of the '| |' operator. nsnativethemewin.cpp 2475
 
Мінлива 'aWidgetType' два рази порівнюється з константою NS_THEME_WINDOW_BUTTON_MINIMIZE. Це помилка. Другий раз змінну потрібно порівняти з константою NS_THEME_WINDOW_BUTTON_MAXIMIZE.
 
 Друкарська помилка N2
 
bool nsHTMLCSSUtils::IsCSSEditableProperty(....)
{
  ....
  if (aAttribute && aAttribute->EqualsLiteral("align") &&
      (nsEditProperty::ul == tagName          <<<<====
       || nsEditProperty::ol == tagName
       || nsEditProperty::dl == tagName
       || nsEditProperty::li == tagName
       || nsEditProperty::dd == tagName
       || nsEditProperty::dt == tagName
       || nsEditProperty::address == tagName
       || nsEditProperty::pre == tagName
       || nsEditProperty::ul == tagName)) {   <<<<====
    return true;
  }
  ....
}

Попередження PVS-Studio: V501 There are identical sub-expressions 'nsEditProperty :: ul == tagName' to the left and to the right of the '| |' operator. nshtmlcssutils.cpp 432
 
Мінлива 'tagName' два рази порівнюється з nsEditProperty :: ul. Можливо, одна перевірка зайва. Або забули порівняти з чимось ще.
 
 Друкарська помилка N3
 
void Reverb::process(....)
{
  ....
  bool isCopySafe =
    destinationChannelL &&
    destinationChannelR &&
    size_t(destinationBus->mDuration) >= framesToProcess &&
    size_t(destinationBus->mDuration) >= framesToProcess;
  ....
}

Попередження PVS-Studio: V501 There are identical sub-expressions 'size_t (destinationBus-> mDuration)> = framesToProcess' to the left and to the right of the '&&' operator. reverb.cpp 192
 
Мінлива 'framesToProcess' два рази порівнюється з 'size_t (destinationBus-> mDuration)'.
 
 Друкарська помилка N4
 
float
PannerNode::ComputeDopplerShift()
{
  ....
  double scaledSpeedOfSound = listener->DopplerFactor() /
                              listener->DopplerFactor();
  ....
}

Попередження PVS-Studio: V501 There are identical sub-expressions 'listener-> DopplerFactor ()' to the left and to the right of the '/' operator. pannernode.cpp 529
 
Дуже підозріле вираз, який варто перевірити.
 
 Друкарська помилка N5
 
bool DataChannelConnection::SendDeferredMessages()
{
  ....
  if ((result = usrsctp_sendv(mSocket, data, ...., 0) < 0)) {
  ....
}

Попередження PVS-Studio: V593 Consider reviewing the expression of the 'A = B < C 'kind. The expression is calculated as following: 'A = (B <C)'. datachannel.cpp 1105
 
Не там поставлена ​​дужка. Спростимо вираз, щоб помилка була краще помітна:
 
if ((result = foo() < 0))

Це вираз обчислюється так. Результат, який повернула функція, порівнюється з 0. Потім true або false записується в змінну 'result'. Помилка в тому, що не там закривається одна з дужок. Насправді, програміст хотів написати вираз:
 
if ((result = foo()) < 0)

Тепер значення, яке повернула функція, записується в змінну 'result'. І тільки потім це значення порівнюється з нулем.
 
 Друкарська помилка N6
 
void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold)
{
  ....
  topRects = destRect;
  bottomRects = bottomRectsEnd;
  destRect = topRects;
  ....
}

Попередження PVS-Studio: V587 An odd sequence of assignments of this kind: A = B; B = A;. Check lines: 358, 360. Nsregion.cpp 360
 
Код підозрілий. Напевно, тут є якась помилка.
 
 Некоректна перевірка N1
 
enum nsBorderStyle {
  eBorderStyle_none = 0,
  ....
};
....
NS_IMETHODIMP
nsWindow::SetNonClientMargins(nsIntMargin &margins)
{
  if (!mIsTopWidgetWindow ||
      mBorderStyle & eBorderStyle_none ||
      mHideChrome)
    return NS_ERROR_INVALID_ARG;
  ....
}

Попередження PVS-Studio: V616 The 'eBorderStyle_none' named constant with the value of 0 is used in the bitwise operation. nswindow.cpp 2278
 
Вираз «mBorderStyle & eBorderStyle_none »не має сенсу. Відсутність стилів (eBorderStyle_none) кодується значенням 0. За все видимості, код умова слід записати так:
 
if (!mIsTopWidgetWindow ||
    mBorderStyle != eBorderStyle_none ||
    mHideChrome)

 Некоректна перевірка N2
 
NS_IMETHODIMP nsWindowsRegKey::ReadStringValue(....)
{
  ....
  DWORD type;
  ....
  if (type != REG_SZ && type == REG_EXPAND_SZ &&
      type == REG_MULTI_SZ)
    return NS_ERROR_FAILURE;
  ....
}

Попередження PVS-Studio: V547 Expression is always false. Probably the '| |' operator should be used here. nswindowsregkey.cpp 292
 
Мінлива 'type' не може бути одночасно дорівнює двом різним значенням. Спростимо код, щоб легше зрозуміти, що не подобається аналізатору:
 
if (... && type == 2 && type == 7)

Ця умова завжди брехливо.
 
Швидше за все, код повинен бути таким:
 
if (type != REG_SZ && type != REG_EXPAND_SZ &&
    type != REG_MULTI_SZ)

 Некоректна перевірка N3
 
const SafepointIndex *
IonScript::getSafepointIndex(uint32_t disp) const
{
  ....
  size_t minEntry = 0;
  ....
  size_t guess = ....;
  ....
  while (--guess >= minEntry) {
    guessDisp = table[guess].displacement();
    JS_ASSERT(guessDisp >= disp);
    if (guessDisp == disp)
      return &table[guess];
  }
  ....
}

Попередження PVS-Studio: V547 Expression '- guess> = minEntry' is always true. Unsigned type value is always> = 0. Ion.cpp 1112
 
Цикл зупиниться тільки тоді, коли буде знайдений потрібний елемент. Якщо такого елемента немає, умова зупинки циклу ніколи не виконається, і відбудеться вихід за межі масиву.
 
Причина в тому, що змінна 'guess' має беззнаковий тип. Це означає, що умова (- guess> = 0) завжди істинно.
 
 Неуважність N1
 
void WinUtils::LogW(const wchar_t *fmt, ...)
{
  ....
  char* utf8 = new char[len+1];
  memset(utf8, 0, sizeof(utf8));
  ....
}

Попередження PVS-Studio: V579 The memset function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. winutils.cpp 146
 
Вираз 'sizeof (utf8)' повертає розмір покажчика, а не розмір виділеного буфера пам'яті. Правильний варіант коду:
 
memset(utf8, 0, sizeof(*utf8) * (len+1));

 Неуважність N2
 
Як завжди зустрічається код, де покажчик на початку використовується, а тільки потім перевіряється на рівність нулю. Наведу в статті тільки один такий випадок. Решта помилок автори Firefox зможуть знайти, запустивши аналізатор.
 
void
nsHttpTransaction::RestartVerifier::Set(
  int64_t contentLength, nsHttpResponseHead *head)
{
  if (mSetup)
    return;

  if (head->Status() != 200)    <<<<====
    return;

  mContentLength = contentLength;

  if (head) {                   <<<<====
  ....
}

Попередження PVS-Studio: V595 The 'head' pointer was utilized before it was verified against nullptr. Check lines: 1915, 1920. Nshttptransaction.cpp 1915
 
На початку покажчик 'head' разименовивается у вираженні «head-> Status ()». А потім він перевіряється на рівність нулю.
 
 Неуважність N3
 
NPError NPP_New(....)
{
  ....
  InstanceData* instanceData = new InstanceData;
  ....
  NPError err = pluginInstanceInit(instanceData);
  if (err != NPERR_NO_ERROR) {
    NPN_ReleaseObject(scriptableObject);
    free(instanceData);
    return err;
  }
  ....
}

Попередження PVS-Studio: V611 The memory was allocated using 'new' operator but was released using the 'free' function. Consider inspecting operation logics behind the 'instanceData' variable. nptest.cpp 1029
 
Пам'ять виділяється за допомогою оператора 'new', а звільняється викликом функції 'free'. Результат — невизначене поведінку програми. Втім, це не страшно, так як код відноситься до тестів.
 
 Неуважність N4
 
Ще один фрагмент коду, що відноситься до тестів. Мінлива 'device' може залишитися неініціалізованої:
 
static ID3D10Device1* getD3D10Device()
{
  ID3D10Device1 *device;
  ....
  if (createDXGIFactory1)
  {
    ....
    hr = createD3DDevice(...., &device);
    ....
  }
  return device;
}

Попередження PVS-Studio: V614 Potentially uninitialized pointer 'device' used. nptest_windows.cpp 164
 
 Більш ретельна перевірка
Метою статті не ставилося описати всі помилки, які може виявити PVS-Studio. Напевно, я щось пропустив. Про дещо що не став писати свідомо. Наприклад, аналізатор видавав багато попереджень V610 , що відносяться до зсувними операціями, які призводять до невизначеного поведінки. Ці попередження однотипні, і писати про них не цікаво.
 
Мета статті показати можливості статичного аналізу, і привернути увагу людей до нашого інструменту. Більш ретельний аналіз Firefox можуть здійснити самі розробники. Їм будемо набагато легше зрозуміти, є щось помилкою чи ні.
 
Примітка для розробників Firefox. Проект вельми великий, тому аналізатор PVS-Studio видає багато помилкових спрацьовувань. Однак, більшість з них відносяться до специфічних макросам. Можна дуже швидко скоротити кількість помилкових попереджень у кілька разів, розставивши відповідні коментарі в коді. Як придушити попередження, які стосуються певним макросам, описано в документації (див. розділ "Придушення помилкових попереджень "). Якщо розробники Firefox зацікавляться придбанням ліцензії на PVS-Studio, ми зі свого боку також готові взяти участь у скороченні помилкових спрацьовувань.
 
 Висновок
Підозрілих ділянок коду знайшлося мало. Причина в тому, що помилки вже були знайдені за допомогою інших методів тестування та інших статичних аналізаторів. Статичні аналізатори коду найбільш корисні при регулярному використанні, так як дозволяють виявити помилку ще на етапі написання коду. Детальніше це розглядається в статті " Лев Толстой і статичний аналіз коду ".
 
Бажаю всім успіхів у програмуванні і трохи менше помилок.
 
 Додаткові посилання
 
     
  1. Аналізатор PVS-Studio . Знайди масу дурних помилок в процесі написання коду. Заощадь час команди. У вас не роблять дурних помилок? Ха-ха !
  2.  
  3. Аналізатор CppCat для індивідуальних розробників і невеликих проектів. Ціна $ 250 за ліцензію. Знижки при продовженні ліцензії або при купівлі декількох ліцензій.
  4.  
  5. Запрошуємо вас підписатися на наш твіттер: @ Code_Analysis . Регулярно публікуємо посилання на цікаві статті з програмування та про перевірку нових проектів.
  6.  
 
 цю статтю англійською
Якщо хочете поділитися цією статтею з англомовною аудиторією, то прошу використовувати посилання на переклад: Andrey Karpov. Firefox Easily Analyzed by PVS-Studio Standalone .
 
 Прочитали статтю і є питання? Часто до наших статей ставлять одні і ті ж питання. Відповіді на них ми зібрали тут: Відповіді на питання читачів статей про PVS-Studio і CppCat, версія 2014 . Будь ласка, ознайомтеся зі списком.
 
    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.