﻿# Легко и просто проверяем Firefox с помощью PVS\-Studio Standalone

Три года назад мы уже проверяли Mozilla Firefox с помощью анализатора PVS\-Studio\. Тогда это было неудобно и затруднительно\. Для Firefox отсутствует проектный файл для Visual Studio\. Сборка осуществляется с помощью make\-файлов\. Поэтому просто взять и проверить проект нельзя\. Требовалось интегрировать PVS\-Studio в систему сборки, что оказалось трудной задачей\. В результате, как мне помнится, была проанализирована только часть проекта\. Но всё поменялось, когда появился PVS\-Studio Standalone\. Теперь можно отследить все запуски компиляторов и легко проверить проект\.

![0262_Firefox_ru/image1.png](https://import.viva64.com/docx/blog/0262_Firefox_ru/image1.png)

## Mozilla Firefox

Думаю, рассказывать про Firefox нет необходимости\. Однако, формат статьи требует всё\-таки написать немного о проверенном проекте\. Поленюсь и воспользуюсь [описанием](https://ru.wikipedia.org/wiki/Mozilla_Firefox) из Wikipedia:

Mozilla Firefox \- свободный браузер на движке Gecko, разработкой и распространением которого занимается Mozilla Corporation\. Третий по популярности браузер в мире и первый среди свободного ПО \- в августе 2013 года его рыночная доля составила 19,26%\. В браузере присутствует интерфейс со многими вкладками, проверка орфографии, поиск по мере набора, "живые закладки", менеджер закачек, поле для обращения к поисковым системам\. Новые функции можно добавлять при помощи расширений\.

Мы уже пытались проверить Firefox\. Частично это нам даже удалось\. По результатам проверки была написана статья "[Как уменьшить вероятность ошибки на этапе написания кода\. Заметка N4](https://pvs-studio.ru/ru/blog/posts/cpp/a0078/)"\. Сложность заключалась в том, что нужно вставить вызов command\-line версии PVS\-Studio в make\-файлы\. Сделать это в большом незнакомом проекте бывает затруднительно\. Именно поэтому мы в дальнейшем не делали попыток перепроверить Firefox\. Ситуация изменилась с появлением PVS\-Studio Standalone\.

## PVS\-Studio Standalone

PVS\-Studio Standalone может быть использован в 2 режимах:

1. Удобная работа с файлом отчета \(\*\.plog\),  который содержит информацию о найденных ошибках на компьютере, где не установлен Visual Studio\.
1. Отслеживание запуска компилятора и сбор всей необходимой информации для дальнейшей проверки файлов\. Нам сейчас интересен именно этот режим работы\.

Теперь не обязательно интегрировать command\-line версию PVS\-Studio в make\-файлы\. Можно проверить Firefox намного проще\. Мы именно так и сделали\. Последовательность действий:

1. Запускаем программу PVS\-Studio Standalone;
1. Выполняем команду "Compiler Monitoring";
1. Компилируем проект Firefox;
1. Останавливаем процедуру слежения \("Stop Monitoring"\);
1. Запускается проверка файлов;
1. Изучаем предупреждения, выданные анализатором кода\.

Более подробно, как использовать этот режим, можно прочитать [здесь](https://pvs-studio.ru/ru/blog/posts/0243/)\.

## Результаты проверки Mozilla Firefox

Проект Firefox очень качественный\. В добавок, как я понимаю, при его разработке уже применяются инструменты статического анализ кода: Coverity и Klocwork\. По крайней мере, я встречал упоминания этих анализаторов в некоторых файлах\.

Поэтому найти хоть что\-то \- это уже достижение\. Давайте посмотрим, какие предупреждения анализатора PVS\-Studio показались мне интересными\.

**Опечатка N1**

```cpp
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**

```cpp
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**

```cpp
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**

```cpp
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**

```cpp
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

Не там поставлена скобка\. Упростим выражение, чтобы ошибка была лучше заметна:

```cpp
if ((result = foo() < 0))
```

Это выражение вычисляется так\. Результат, который вернула функция, сравнивается с 0\. Затем true или false записывается в переменную 'result'\. Ошибка в том, что не там закрывается одна из скобок\. На самом деле, программист хотел написать выражение:

```cpp
if ((result = foo()) < 0)
```

Теперь значение, которое вернула функция, записывается в переменную 'result'\. И только потом это значение сравнивается с нулём\.

**Опечатка N6**

```cpp
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**

```cpp
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\. По все видимости, код условие следует записать так:

```cpp
if (!mIsTopWidgetWindow ||
    mBorderStyle != eBorderStyle_none ||
    mHideChrome)
```

**Некорректная проверка N2**

```cpp
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' не может быть одновременно равна двум разным значениям\. Упростим код, чтобы легче понять, что не нравится анализатору:

```cpp
if (... && type == 2 && type == 7)
```

Это условие всегда ложно\. 

Скорее всего, код должен быть таким:

```cpp
if (type != REG_SZ && type != REG_EXPAND_SZ &&
    type != REG_MULTI_SZ)
```

**Некорректная проверка N3**

```cpp
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**

```cpp
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\)' возвращает размер указателя, а не размер выделенного буфера памяти\. Правильный вариант кода:

```cpp
memset(utf8, 0, sizeof(*utf8) * (len+1));
```

**Невнимательность N2**

Как всегда встречается код, где указатель в начале используется, а только затем проверяется на равенство нулю\. Приведу в статье только один такой случай\. Остальные ошибки авторы Firefox смогут найти, запустив анализатор\.

```cpp
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**

```cpp
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' может остаться неинициализированной:

```cpp
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](https://pvs-studio.ru/ru/docs/warnings/v610/), относящихся к сдвиговым операциям, которые приводят к неопределённому поведению\. Эти предупреждения однотипны, и писать про них не интересно\.

Цель статьи показать возможности статического анализа, и привлечь внимание людей к нашему инструменту\. Более тщательный анализ Firefox могут осуществить сами разработчики\. Им будем намного легче понять, является что\-то ошибкой или нет\.

Примечание для разработчиков Firefox\. Проект весьма большой, поэтому анализатор PVS\-Studio выдаёт много ложных срабатываний\. Однако, большинство из них относятся к специфическим макросам\. Можно очень быстро сократить количество ложных предупреждений в несколько раз, расставив соответствующие комментарии в коде\. Как подавить предупреждения, относящиеся к определённым макросам, описано в документации \(см\. раздел "[Подавление ложных предупреждений](https://pvs-studio.ru/ru/docs/manual/0017/)"\)\. Если разработчики Firefox заинтересуются приобретением лицензии на PVS\-Studio, мы со своей стороны также готовы принять участие в сокращении ложных срабатываний\.

## Заключение

Подозрительных участков кода нашлось мало\. Причина в том, что ошибки уже были найдены с помощью других методов тестирования и других статических анализаторов\. Статические анализаторы кода наиболее полезны при регулярном использовании, так как позволяют выявить ошибку ещё на этапе написания кода\. Подробнее это рассматривается в статье "[Лев Толстой и статический анализ кода](https://pvs-studio.ru/ru/blog/posts/0105/)"\.

Желаю всем успехов в программировании и поменьше ошибок\.

## Дополнительные ссылки

1. Анализатор [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/)\. Найди массу глупых ошибок в процессе написания кода\. Сэкономь время команды\. У вас не делают глупых ошибок? [Ха\-ха](https://pvs-studio.ru/ru/blog/posts/0116/)\!
1. Приглашаем вас подписаться на наш твиттер: [@Code\_Analysis](https://twitter.com/Code_Analysis)\. Регулярно публикуем ссылки на интересные статьи по программированию и о проверке новых проектов\.