﻿# Предновогоднее шоу: Топ 10 ошибок в C и С\+\+ проектах в 2023 году

Вот уже выпал снег, на дворе декабрь, а значит и Новый Год где\-то рядом\. В преддверии праздников мы решили показать вам наиболее интересные ошибки, которые мы смогли найти в коде популярных Open Source проектов\. Наши авторы написали много познавательных статей, разобрали множество ошибок в коде, и теперь мы подведём итоги\.

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

<details>
   <summary>Перед дальнейшим чтением мы рекомендуем</summary>

Комфортно расположитесь перед камином в тёплом, мягком кресле после тяжёлого рабочего дня \(если нет камина, можно обойтись мягким креслом\)\. \+ 10 к уюту\.

У вас в руках кружка горячего шоколада или чая\. Вы пьёте свой напиток осторожно, чтобы не обжечься и дольше наслаждаться его вкусом\. \+15 к настроению\.

Берёте планшет или ноутбук и можете приступить к прочтению\. Вы отдыхаете: \+ 9 к интеллекту и \+5 к навыкам, связанным с программированием\.


</details>


## Вступление

Вечер, снег блестит под светом уличных фонарей, приятно щиплет лицо от мороза, вокруг тишина, а с неба, кружась, падают снежинки\. Скоро новый год и всё замерло в предвкушении чуда\. Думаю, каждому знакомо это ощущение надвигающегося праздника\. Я с теплотой вспоминаю, как меня в детстве зимой перед праздниками водили в цирк\. Это было великолепно\! Везде горят разноцветные огни, и тебя наполняет праздничное настроение\! Весёлая музыка, смешные клоуны, запах попкорна\. Загадочный усатый ведущий в чёрном цилиндре, с тростью старинной, задающий атмосферу таинственности\. И вот он объявляет номер, и на арене происходит маленькое приключение, или случаются чудеса, и весь зал замирает на время выступления\. Затем аплодисменты, овации – всё смешивается в едином порыве\. Сейчас мы уже выросли, но можем сами создавать свои чудеса для наших близких, друзей и знакомых, которых хотим порадовать\.

А посему сегодня вас ждёт не просто статья о том, как где\-то в коде допустили ошибку, а целое волшебство кодинга\. 10 масштабных и зрелищных разборов ошибок – всё только для вас\! Итак, \*надевает маску [Шпрехшталмейстера](https://ru.wikipedia.org/wiki/%D0%A8%D0%BF%D1%80%D0%B5%D1%85%D1%88%D1%82%D0%B0%D0%BB%D0%BC%D0%B5%D0%B9%D1%81%D1%82%D0%B5%D1%80#:~:text=%D0%A8%D0%BF%D1%80%D0%B5%D1%85%D1%88%D1%82%D0%B0%D0%BB%D0%BC%D0%B5%CC%81%D0%B9%D1%81%D1%82%D0%B5%D1%80%20(%D0%BD%D0%B5%D0%BC.,%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BD%D0%B8%D0%BA%20%D1%86%D0%B8%D1%80%D0%BA%D0%B0%2C%20%D0%B2%D0%B5%D0%B4%D1%83%D1%89%D0%B8%D0%B9%20%D1%86%D0%B8%D1%80%D0%BA%D0%BE%D0%B2%D0%BE%D0%B5%20%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5.) \(Ш:\)\*, посмотрим, что у нас сегодня в программе:

_Ш: "Наше предновогоднее шоу готово искренне удивить и восхитить вас своей новой программой\! Незабываемое зрелище, множество жанров: от утечек памяти до разыменовывания указателей без страховки\! Смешные опечатки\!_

_Потрясающие выступления профессиональных программистов на открытых проектах – это шоу мирового уровня\!"_

### Начинаем Шоу\!

_Ш: "Шоу для программистов\! Так, так, так\.\.\. Раз уж на то пошло, наше волшебство начнём\.\.\. пожалуй, с цифры 9, а закончим 0\! Воистину магические цифры, и зачем нам больше?"_

### Девятый "номер"\. Сомнительный цикл

_Ш: "Дамы и Господа\! Леди и Джентльмены\! Программисты всех мастей\! Время для магии и волшебства\! Этот код заставит вас усомниться в самой концепции существования эффективного код\-ревью\."_

Статья: "[Проверка компилятора GCC 13 с помощью PVS\-Studio](https://pvs-studio.ru/ru/blog/posts/cpp/1067/)"\.

```cpp
static bool
combine_reaching_defs (ext_cand *cand,
                       const_rtx set_pat,
                       ext_state *state)
{
  ....
  while (   REG_P (SET_SRC (*dest_sub_rtx))
         && (REGNO (SET_SRC (*dest_sub_rtx)) == REGNO (SET_DEST (set))))
  {
    ....
    if (....)
      break;

    if (....)
      break;
    
    ....
    break;
  }
}
```

Предупреждение анализатора PVS\-Studio: [V612](https://pvs-studio.ru/ru/docs/warnings/v612/) An unconditional 'break' within a loop\. ree\.cc 985

Анализатор PVS\-Studio обнаружил, что этот цикл безусловно прерывается на первой итерации\. При дальнейшем рассмотрении мы также обнаруживаем множество других _break_ под условиями\. Возможно, это способ избежать написания оператора _goto_\. Однако выглядит он странно, учитывая его постоянное открытое использование в целом по проекту\.

### Восьмой "номер"\. Самая опасная функция в мире C и C\+\+

_Ш: "Уважаемая публика\! Сам король сегодня посетил нас\! Но вместе с ним к нам пришло нечто очень опасное, запретное в мире программирования\.\.\. Но не бойтесь\! с этим можно бороться\. Надо только знать, как\!"_

Статья: "[Microsoft PowerToys: Король GitHub среди C\# проектов с C\+\+ ошибками](https://pvs-studio.ru/ru/blog/posts/cpp/1078/)"\.

```cpp
void SetNumLockToPreviousState(....)
{
  int key_count = 2;
  LPINPUT keyEventList = new INPUT[size_t(key_count)]();
  memset(keyEventList, 0, sizeof(keyEventList));
  ....
}
```

На этот код PVS\-Studio выдаёт сразу три предупреждения:

* [V579](https://pvs-studio.ru/ru/docs/warnings/v579/) The memset function receives the pointer and its size as arguments\. It is possibly a mistake\. Inspect the third argument\. KeyboardEventHandlers\.cpp 16
* [V568](https://pvs-studio.ru/ru/docs/warnings/v568/) It's odd that 'sizeof\(\)' operator evaluates the size of a pointer to a class, but not the size of the 'keyEventList' class object\. KeyboardEventHandlers\.cpp 16
* [V1086](https://pvs-studio.ru/ru/docs/warnings/v1086/) A call of the 'memset' function will lead to underflow of the buffer 'keyEventList'\. KeyboardEventHandlers\.cpp 16

На тему подобных ошибок у нас даже есть отдельная статья: "[Самая опасная функция в мире С/С\+\+](https://pvs-studio.ru/ru/blog/posts/cpp/0360/)"\.

В данном случае разработчики хотели занулить массив _keyEventList_\. Обратим внимание на 3 параметр – количество байт, которые хотели заполнить нулями\. Вот только _sizeof\(keyEventList\)_ вычисляет не размер массива, а размер указателя\. Он зависит от целевой платформы, но чаще всего это 4 или 8 байт\. При этом размер структуры явно больше 4 или 8 байт:

```cpp
typedef struct tagINPUT 
{
  DWORD   type;

  union
  {
    MOUSEINPUT      mi;
    KEYBDINPUT      ki;
    HARDWAREINPUT   hi;
  } DUMMYUNIONNAME;
} INPUT, *PINPUT, FAR* LPINPUT;
```

### Седьмой "номер"\. Дважды добавленный символ

_Ш: "Загадки, загадки, загадки\.\.\. Сейчас мы с вами поучаствуем в битве\! Посмотрите на код\! Сможете ли вы за короткое время найти здесь самый маленький, но очень важный символ? Часто этот символ означает конец всего\! Но иногда он является началом чего\-то нового и прекрасного\. Ну, или обращения напрямую к полям объекта\.\.\."_

Статья: "[PVS\-Studio vs CodeLite: битва за идеальный код](https://pvs-studio.ru/ru/blog/posts/cpp/1065/)"\.

```cpp
std::unordered_set<wxChar> delimiters =
  { ':', '@', '.', '!', ' ', '\t', '.', '\\', 
    '+', '*', '-', '<', '>', '[', ']', '(', 
    ')', '{', '}',  '=', '%', '#', '^', '&', 
    '\'', '"', '/', '|',  ',', '~', ';', '`' };
```

Предупреждение анализатора: [V766](https://pvs-studio.ru/ru/docs/warnings/v766/) An item with the same key ''\.'' has already been added\. wxCodeCompletionBoxManager\.cpp:19

Из\-за такого количества одинарных кавычек глаз вполне может не заметить, что здесь повторно добавляется символ _'\.'_\. Возможно, что здесь забыли добавить какой\-то другой символ\. Либо это просто случайный дубликат, и его можно убрать\.

### Шестой "номер"\. Ошибочное переопределение

_Ш: "Вы хотите выиграть? Или не проиграть? Или это иллюзия выбора? Этот номер порадует вас как ответами на эти вопросы, так и иллюзией переопределения виртуальной функции\!"_

Статья: "[PVS\-Studio vs CodeLite: битва за идеальный код](https://pvs-studio.ru/ru/blog/posts/cpp/1065/)"\.

В базовом классе функция выглядит так:

```cpp
class WXDLLIMPEXP_CORE wxGenericProgressDialog : public wxDialog
{
public:
  ....
  virtual bool Update(int value,
                      const wxString& newmsg = wxEmptyString,
                      bool *skip = NULL);
  ....
};
```

А вот как в наследнике:

```cpp
class clProgressDlg : public wxProgressDialog
{
public:
  ....
  bool Update(int value, const wxString& msg);
  ....
};
```

Предупреждение анализатора: [V762](https://pvs-studio.ru/ru/docs/warnings/v762/) It is possible a virtual function was overridden incorrectly\. See third argument of function 'Update' in derived class 'clProgressDlg' and base class 'wxGenericProgressDialog'\. progress\_dialog\.h:47, progdlgg\.h:44

Исходя из совпадения первых двух параметров функций, можно сделать вывод, что действительно хотели переопределить виртуальную функцию\. Однако параметры по умолчанию также являются частью сигнатуры\. Поэтому функция _clProgressDlg::Update_ на самом деле не переопределяет, а скрывает виртуальную функцию _wxGenericProgressDialog::Update_\.

Корректное объявление виртуальной функции должно быть такое:

```cpp
class clProgressDlg : public wxProgressDialog
{
public:
  ....
  bool Update(int value, const wxString& msg, bool *skip);
  ....
};
```

Чтобы избежать таких ошибок, начиная с C\+\+11, можно и даже нужно использовать спецификатор _override_:

```cpp
class clProgressDlg : public wxProgressDialog
{
public:
  ....
  bool Update(int value, const wxString& msg, bool *skip) override;
  ....
};
```

Теперь компилятор выдаст ошибку, если виртуальная функция ничего не переопределяет из базового класса\. Вот и ответ\.

### Пятый "номер"\. Мультивселенная безумия

_Ш: "Короли, иллюзии, магия и загадки\. И правда — вселенная сегодня безумна, но сейчас вы увидите что\-то по\-настоящему такое, что повергает в шок\! Слабонервным просьба отвернуться\!"_

Статья: "[Ква\! Как писали код во времена Quake](https://pvs-studio.ru/ru/blog/posts/cpp/1066/)"\.

```cpp
// PVS-Studio: void Mod_LoadTexinfo (lump_t *l)
// PVS-Studio: client/model.c

mtexinfo_t *out;
// ... 
for (j=0 ; j<8 ; j++)
{
  ut->vecs[0][j] = LittleFloat (in->vecs[0][j]);
}
```

[V557](https://pvs-studio.ru/ru/docs/warnings/v557/) Array overrun is possible\. The value of 'j' index could reach 7\.

Для понимания добавлено объявление для _mtexinfo\_t \*out_:

```cpp
// PVS-Studio: client/model.c

typedef struct
{
  float       vecs[2][4]; // PVS-Studio: see what is wrong here?
  float       mipadjust;
  texture_t   *texture;
  int         flags;
} mtexinfo_t;
// PVS-Studio: client/common.h

float (*LittleFloat) (float l);
```

У нас есть двумерный массив, который используют как одномерный\. Мало кто увидит проблему\. Программист может подумать, что в памяти он лежит последовательно как минимум _C99, 6\.2\.5 Types p20_ — и будет прав\.

_"An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type\. The element type shall be complete whenever the array type is specified\."_

Однако в языке С существует специальный тип — [указатель на массив T](https://c-faq.com/aryptr/aryvsadr.html)\. Во время обращения к массиву внутри цикла, _j_ обязательно превысит значение 3 и оператор \[\] вернёт переменную типа _float\[4\]_\. Как следствие произойдёт чтение элемента массива вне его пределов, что является классическим примером неопределённого поведения \(UB\)\.

### Четвёртый "номер"\. Только истина

_Ш: "Подходите ближе, дамы и господа\! Вас ждёт удивительный номер\! Представьте, что условия — это всего лишь условности, а истина может обмануть ваши ожидания\.\.\."_

Статья: "[Герои Кода и Магии: анализ игрового движка VCMI](https://pvs-studio.ru/ru/blog/posts/cpp/1058/)"\.

```cpp
void deallocate_segment(....) 
{
  ....
  if (seg_index >= first_block) 
  {
    segment_element_allocator_traits::deallocate(....);
  }
  else 
  if (seg_index == 0) 
  {
    elements_to_deallocate = first_block > 0
                               ? this->segment_size(first_block) 
                               : this->segment_size(0);
    ....
  }
}
```

Предупреждение анализатора: [V547](https://pvs-studio.ru/ru/docs/warnings/v547/) Expression 'first\_block \> 0' is always true\. concurrent\_vector\.h 669

В данном коде, если первая проверка не выполняется, то _seg\_index < first\_block,_ а если _seg\_index \=\= 0_, то это означает, что _first\_block \> 0_\. Затем идёт ещё одна проверка _first\_block \> 0_, которую анализатор справедливо помечает как всегда истинную\. Следовательно, выражение _this\-\>segment\_size\(0\)_ никогда не выполнится\.

Хорошо, что у нас есть технологии для статического анализа, выявляющие подобные ошибки\! Всё это возможно благодаря использованию в анализаторе технологии символьного выполнения \(_symbolic execution\)_\. Магия, не правда ли? И наверняка так и воспринимают это обычные люди\. А вот представьте:

Существует некая высокотехнологичная раса\. При колонизации очередной планеты технологии безвозвратно утеряны во время войны за власть\.\.\. Прошло несколько сотен лет, и технологичная раса перенеслась во времена мудрых и расчётливых королей, доблестных рыцарей и загадочных магических руин\. А работоспособность технологий воплощается при помощи различных ритуалов и артефактов\. И, не понимая, как это работает на самом деле, эти существа взывают к магии\. Кстати, как раз об этом и повествует сюжет Героев Меча и Магии\.

А если вам интересно узнать об упомянутой выше технологи символьного выполнения, предлагаю вашему вниманию данную статью "[Технологии статического анализа кода PVS\-Studio](https://pvs-studio.ru/ru/blog/posts/0908/)"\.

### Третий "номер"\. Очепятка неопределённого поведения

_Ш: "Ох уж эти шутники\-программисты\. Один раз не дай им выспаться, как они начинают играть кодом\! Сейчас на нашей арене перед вами выступят забавные опечатки и их коварные последствия\!"_

Статья: "[FreeCAD и C\+\+ код с неопределённым поведением для медитации](https://pvs-studio.ru/ru/blog/posts/cpp/1072/)"\.

```cpp
QGVPage* QGIView::getQGVPage(TechDraw::DrawView* dView)
{
  ViewProviderPage* vpp = getViewProviderPage(dView);
  if (!vpp) {
    return vpp->getQGVPage();
  }
  return nullptr;
}
```

Предупреждение анализатора: [V522](https://pvs-studio.ru/ru/docs/warnings/v522/) \[CWE\-476, CERT\-EXP34\-C\] Dereferencing of the null pointer 'vpp' might take place\. QGIView\.cpp 592

Автор кода опечатался при написании условия:

* Если указатель ненулевой, то функция ничего не делает и возвращает _nullptr_\.
* Если указатель нулевой, то произойдёт его разыменование\. Возникнет [неопределённое поведение](https://pvs-studio.ru/ru/blog/posts/cpp/0306/)\.

Что интересно, по соседству присутствует функция\-близнец, и в ней условие правильное:

```cpp
QGVPage* QGIView::getQGVPage(TechDraw::DrawView* dView)
{
  ViewProviderPage* vpp = getViewProviderPage(dView);
  if (!vpp) {
    return vpp->getQGVPage();
  }
  return nullptr;
}

QGSPage* QGIView::getQGSPage(TechDraw::DrawView* dView)
{
  ViewProviderPage* vpp = getViewProviderPage(dView);
  if (vpp) {
    return vpp->getQGSPage();
  }
  return nullptr;
}
```

Непонятно, как так получилось\. С большой вероятностью вторая функция была написана с помощью _copy\-paste_\. Странно то, что во второй функции ошибку поправили, а в первой она осталась, и, возможно, ошибка присутствовала в обеих функциях\. Затем кто\-то заметил, что функция _getQGSPage_ работает неправильно, и исправил её\.

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

### Второй "номер"\! Доверяй, но проверяй

_Ш: "И снова загадка\! И снова код, покрытый тайнами и иллюзиями правильности своего выполнения\! Неотвратимость судьбы или чья\-то нехорошая шутка?  Попробуйте же отгадать, что здесь не так?"_

Статья: "[Игоры\! Как пишут код для SDL \(\+ интервью с создателем\)](https://pvs-studio.ru/ru/blog/posts/cpp/1081/)"\.

Файл: SDL/src/stdlib/SDL\_iconv\.c \([GitHub permalink](https://github.com/libsdl-org/SDL/blob/bac7eeaaae00b929808ed8efe1a76e45a4e2c54b/src/stdlib/SDL_iconv.c#L789)\)

```cpp
char *SDL_iconv_string(const char *tocode, const char *fromcode,
                       const char *inbuf, size_t inbytesleft)
{
  SDL_iconv_t cd;
  ....

  cd = SDL_iconv_open(tocode, fromcode);
  if (cd == (SDL_iconv_t)-1) {
    /* See if we can recover here (fixes iconv on Solaris 11) */
    if (tocode == NULL || !*tocode) {
      tocode = "UTF-8";
    }
    if (fromcode == NULL || !*fromcode) {
      fromcode = "UTF-8";
    }
    cd = SDL_iconv_open(tocode, fromcode);
  }
  ....
}
```

[V595](https://pvs-studio.ru/ru/docs/warnings/v595/) The 'tocode' pointer was utilized before it was verified against nullptr\. Check lines: 37, 789, 792\. SDL/src/stdlib/SDL\_iconv\.c:792:1

Для понимания проблемы надо окунуться в реализацию функции [SDL\_iconv\_open](https://github.com/libsdl-org/SDL/blob/bac7eeaaae00b929808ed8efe1a76e45a4e2c54b/src/stdlib/SDL_iconv.c#L35):

```cpp
SDL_iconv_t SDL_iconv_open(const char *tocode, const char *fromcode)
{
  return (SDL_iconv_t)((uintptr_t)iconv_open(tocode, fromcode));
}
```

На вход функции _SDL\_iconv\_string_ вполне могут быть переданы нулевые указатели в аргументах _tocode_ и _fromcode_\. В случае этого они перед проверкой с ветерком полетят и в саму _iconv\_open_\.

Если мы посмотрим в _man_ страницу данной функции проекта [GNU](https://www.gnu.org/software/libiconv/documentation/libiconv-1.13/iconv_open.3.html), то не увидим никакого упоминания о том, что _NULL_ толкать в неё нельзя\. Но нас не проведёшь\! Поэтому надо взглянуть в [исходники](https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/iconv/iconv_open.c#L32) библиотеки С:

```cpp
iconv_t
iconv_open (const char *tocode, const char *fromcode)
{
  /* Normalize the name.  We remove all characters beside alpha-numeric,
     '_', '-', '/', '.', and ':'.  */
  size_t tocode_len = strlen (tocode) + 3;
  ....
}
```

Ну\.\.\. обращения по указателю, конечно, не происходит, но вполне себе присутствует вызов _strlen_ без проверки на _NULL_\. Все мы знаем, что _strlen_ делает в этом случае, и мало кому это по душе\!

Однако автор не остановился на одной имплементации — он проверил также [BSD](https://github.com/freebsd/freebsd-src/blob/366ef17bb6ce008f79a283ff5dc5e87ab13dbdb3/sys/libkern/iconv.c#L248) и [Musl](https://github.com/kraj/musl/blob/2e6edc1b699851fcbe9f7ab0fbd724624adde88a/src/locale/iconv.c#L145)\. И все они ведут себя равным образом\.

### Первый "номер"\! Много потоков, много проблем

_Ш: "А теперь ловкач\-процессор покажет вам, как жонглировать потоками\! И в этом ему будет помогать малыш\-компилятор\! Посмотрите на его точные акробатические движения с потоками, сможет ли кто\-то из зала так же?"_

Статья: "[Проверяем YTsaurus\. Доступность, надёжность, open source](https://pvs-studio.ru/ru/blog/posts/cpp/1077/)"\.

```cpp
TAsyncSignalsHandler *SIGNALS_HANDLER = nullptr;

void SetAsyncSignalHandler(int signum, TAutoPtr<TEventHandler> handler)
{
  static TAdaptiveLock lock;

  if (Y_UNLIKELY(SIGNALS_HANDLER == nullptr))       // N1
  {
    TGuard dnd(lock);

    if (SIGNALS_HANDLER == nullptr)                 // N2
    {
      // NEVERS GETS DESTROYED
      SIGNALS_HANDLER = new TAsyncSignalsHandler(); // N3
    }
  }

  SIGNALS_HANDLER->Install(signum, handler);        // N4
}
```

Предупреждение анализатора: [V547](https://pvs-studio.ru/ru/docs/warnings/v547/) Expression 'SIGNALS\_HANDLER \=\= nullptr' is always true\. async\_signals\_handler\.cpp:200

Несмотря на то, что здесь сработало диагностическое правило [V547](https://pvs-studio.ru/ru/docs/warnings/v547/), был найден интересный экземпляр паттерна [Double\-checked locking](https://en.wikipedia.org/wiki/Double-checked_locking)\.

Что же может пойти не так? Для начала имеем небольшую вероятность того, что компилятор может закэшировать значение переменной _SIGNALS\_HANDLER_ в регистре и не будет заново читать из неё новое значение\. По крайней мере, если будет использоваться нетипичный способ блокировки\. Лучшим решением будет объявить переменную как _volatile_ или использовать _std::atomic_\.

Дальше больше\. Давайте попробуем разобраться, что здесь не очень хорошо\. Будем считать, что с этим кодом оперируют поток _A_ и поток _B_\.

Поток _A_ первым добирается до точки _N1_\. Указатель нулевой, так что поток управления заходит в первый _if_ и захватывает объект блокировки\.

Поток _A_ доходит до точки _N2_\. Указатель также нулевой, поэтому поток управления заходит во второй _if_\.

Дальше в точке _N3_ может произойти небольшая магия\. Инициализацию указателя можно представить примерно в следующем виде:

```cpp
auto tmp = (TAsyncSignalsHandler *) malloc(sizeof(TAsyncSignalsHandler));
new (tmp) TAsyncSignalsHandler();
SIGNALS_HANDLER = tmp;
```

Хитрый процессор может переупорядочить инструкции, и в итоге _SIGNALS\_HANDLER_ будет проинициализирован до того, как будет вызван _placement new_\.

И тут в бой вступает поток _B_ — как раз в тот момент, когда поток _A_ ещё не успел вызвать _placement new\._ Поток управления приходит в точку _N1_, видит инициализированный указатель и перемещается в точку _N4_\.

Дальше в потоке _B_ происходит вызов функции\-члена _Install_ на объекте, у которого ещё не стартовало время жизни\. Неопределённое поведение\.\.\.

Как поправить этот паттерн? Надо сделать так, чтобы чтение и запись в _SIGNALS\_HANDLER_ происходили атомарно:

```cpp
std::atomic<TAsyncSignalsHandler *> SIGNALS_HANDLER { nullptr };

void SetAsyncSignalHandler(int signum, TAutoPtr<TEventHandler> handler)
{
  static TAdaptiveLock lock;

  auto tmp = SIGNALS_HANDLER.load(std::memory_order_acquire); // <=
    
  if (Y_UNLIKELY(tmp == nullptr))
  {
    TGuard dnd(lock);

    tmp = SIGNALS_HANDLER.load(std::memory_order_relaxed);    // <=
    if (tmp == nullptr)
    {
      // NEVERS GETS DESTROYED
      tmp = new TAsyncSignalsHandler();
      SIGNALS_HANDLER.store(tmp, std::memory_order_release);  // <=
    }
  }

  tmp->Install(signum, handler);
}
```

### Нулевой "номер"\!\!\! Капитан Блад и его сокровища\!

_Ш: "А теперь то, чего вы все так долго ждали\! Безжалостные Пираты\! Сундук с сокровищами, оставленный в назидание нам, а также тем, кто придёт после\.\.\. Бережно хранимый в нашем набитом другими редкими сокровищами трюме\. Сегодня мы откроем его специально для вас\! Йо\-хо\-хо и бутылка рома\!_"

Статья: "[Приключения капитана Блада: потонет ли Арабелла?](https://pvs-studio.ru/ru/blog/posts/cpp/1033/)"\.

Приведу проблемную функцию полностью:

```cpp
void appDebuger::CopyDump(const char * src, DWORD from, const char * dst)
{
  FILE * file = fopen(src, "rb");
  if (file)
  {
    fseek(file, 0, SEEK_END);
    DWORD size = ftell(file);
    fseek(file, from, SEEK_SET);
    if(size > from)
    {
      FILE * to = fopen(dst, "a+b");
      if(to)
      {
        char buf[128];
        while (from < size)
        {          
          DWORD s = size - from;
          if (s > sizeof(buf)) s = sizeof(buf);
          memset(buf, ' ', sizeof(buf));
          fread(buf, s, 1, file);
          fwrite(buf, s, 1, file);            // <=
          from += s;
        }
        fclose(to);
      }
    }
    fclose(file);
  }
}
```

[V1075](https://pvs-studio.ru/ru/docs/warnings/v1075/) The function expects the file to be opened for writing, but it was opened for reading\. Debugger\.cpp 172

Мы не думали, что диагностика когда\-нибудь сработает, так как ошибка довольно редкая, однако первое трофейное срабатывание произошло сразу после выхода в релизе PVS\-Studio 7\.15\. Данное диагностическое правило ругается на запись в файлы, открытые для чтения, и наоборот\.

Итак, функция должна прочитать данные из одного файла по пути _src_, начиная с позиции _from_, и записать их в другой файл по пути _dst_\. За исходный файл отвечает переменная _file_, за результирующий файл — переменная _to_\.

К сожалению, в коде происходит чтение из переменной _file_ и запись в переменную _file_\. Поправить код можно таким образом:

```cpp
fwrite(buf, s, 1, to);
```

### Конец представления\!

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

А самое главное, не забывайте, мы с вами все можем творить чудеса хоть каждый день\. Главное упорство и вера в своё дело, чем бы мы с вами не занимались\.

Отдельно огромная благодарность авторам упомянутых статей, ведь без них не было бы и этой\.

Традиционно предлагаю [попробовать](https://pvs-studio.ru/ru/pvs-studio/download/) наш анализатор PVS\-Studio\. Для Open Source проектов у нас [предоставляется](https://pvs-studio.ru/ru/order/open-source-license/) бесплатная лицензия\.

Берегите себя и всего доброго\! Приятных новогодних праздников\!