﻿# Находим баги в LLVM 8 с помощью анализатора PVS\-Studio

Прошло более двух лет с момента последней проверки кода проекта LLVM с помощью нашего анализатора PVS\-Studio\. Давайте убедимся, что анализатор PVS\-Studio по\-прежнему является лидирующим инструментом по выявлению ошибок и потенциальных уязвимостей\. Для этого проверим и найдём новые ошибки в релизе LLVM 8\.0\.0\.

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

## Статья, которая должна быть написана

Если честно, мне не хотелось писать эту статью\. Неинтересно писать про проект, который мы уже неоднократно проверяли \([1](https://pvs-studio.ru/ru/blog/posts/0108/), [2](https://pvs-studio.ru/ru/blog/posts/cpp/0155/), [3](https://pvs-studio.ru/ru/blog/posts/cpp/0446/)\)\. Лучше написать про что\-то новое, но у меня нет выбора\.

Каждый раз, когда выходит новая версия LLVM или обновляется [Clang Static Analyzer](https://clang-analyzer.llvm.org/), у нас в почте появляются вопросы следующего типа:

_Смотрите, новая версия Clang Static Analyzer научилась находить новые ошибки\! Мне кажется, актуальность использовать PVS\-Studio уменьшается\. Clang находит больше ошибок, чем раньше и догоняет по возможностям PVS\-Studio\. Что вы про это думаете?_

На это мне всегда хочется ответить что\-то в духе:

**Мы тоже не сидим без дела\!** Мы существенно улучшили возможности анализатора PVS\-Studio\. Так что не волнуйтесь, мы продолжаем лидировать, как и раньше\.

К сожалению, это плохой ответ\. В нём нет proof\-ов\. И именно поэтому сейчас я пишу эту статью\. Итак, проект LLVM в очередной раз проверен и в нём найдены разнообразнейшие ошибки\. Те, которые мне показались интересными, я сейчас продемонстрирую\. Эти ошибки не может найти Clang Static Analyzer \(или это крайне неудобно делать с его помощью\)\. А мы можем\. Причём я нашел и выписал все эти ошибки за один вечер\.

А вот написание статьи затянулось на несколько недель\. Никак не мог себя заставить всё это оформить в виде текста :\)\.

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

## Новые и старые диагностики

Как уже было отмечено, около двух лет назад проект LLVM был в очередной раз проверен, а найденные ошибки исправлены\. Теперь в этой статье будет представлена новая порция ошибок\. Почему были найдены новые ошибки? На это есть 3 причины:

1. Проект LLVM развивается, в нём изменяется старый код, и появляется новый\. Естественно в изменённом и написанном коде есть новые ошибки\. Это хорошо демонстрирует, что статический анализ должен применяться регулярно, а не от случая к случаю\. Наши статьи хорошо показывают возможности анализатора PVS\-Studio, но это не имеет ничего общего с повышением качества кода и снижением стоимости исправления ошибок\. Используйте статический анализатор кода регулярно\!
1. Мы дорабатываем и усовершенствуем уже существующие диагностики\. Поэтому анализатор может выявить ошибки, которые не замечал при предыдущих проверках\.
1. В PVS\-Studio появились новые диагностики, которых не было 2 года назад\. Я решил выделить их в отдельный раздел, чтобы наглядно показать развитие PVS\-Studio\.

## Дефекты, выявленные диагностиками, существовавшими 2 года назад

**Фрагмент N1: Copy\-Paste**

```cpp
static bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) {
  if (Name == "addcarryx.u32" || // Added in 8.0
    ....
    Name == "avx512.mask.cvtps2pd.128" || // Added in 7.0
    Name == "avx512.mask.cvtps2pd.256" || // Added in 7.0
    Name == "avx512.cvtusi2sd" || // Added in 7.0
    Name.startswith("avx512.mask.permvar.") || // Added in 7.0     // <=
    Name.startswith("avx512.mask.permvar.") || // Added in 7.0     // <=
    Name == "sse2.pmulu.dq" || // Added in 7.0
    Name == "sse41.pmuldq" || // Added in 7.0
    Name == "avx2.pmulu.dq" || // Added in 7.0
  ....
}
```

Предупреждение PVS\-Studio: [V501](https://pvs-studio.ru/ru/docs/warnings/v501/) \[CWE\-570\] There are identical sub\-expressions 'Name\.startswith\("avx512\.mask\.permvar\."\)' to the left and to the right of the '\|\|' operator\. AutoUpgrade\.cpp 73

Дважды проверяется, что имя начинается с подстроки "avx512\.mask\.permvar\."\. Во второй проверке явно хотели написать что\-то ещё, но забыли исправить скопированный текст\.

**Фрагмент N2: Опечатка**

```cpp
enum CXNameRefFlags {
  CXNameRange_WantQualifier = 0x1,
  CXNameRange_WantTemplateArgs = 0x2,
  CXNameRange_WantSinglePiece = 0x4
};

void AnnotateTokensWorker::HandlePostPonedChildCursor(
    CXCursor Cursor, unsigned StartTokenIndex) {
  const auto flags = CXNameRange_WantQualifier | CXNameRange_WantQualifier;
  ....
}
```

Предупреждение PVS\-Studio: V501 There are identical sub\-expressions 'CXNameRange\_WantQualifier' to the left and to the right of the '\|' operator\. CIndex\.cpp 7245

Из\-за опечатки дважды используется одна и та же именованная константа _CXNameRange\_WantQualifier_\.

**Фрагмент N3: Путаница с приоритетами операторов**

```cpp
int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
  ....
  if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
    return 0;
  ....
}
```

Предупреждение PVS\-Studio: [V502](https://pvs-studio.ru/ru/docs/warnings/v502/) \[CWE\-783\] Perhaps the '?:' operator works in a different way than it was expected\. The '?:' operator has a lower priority than the '\=\=' operator\. PPCTargetTransformInfo\.cpp 404

На мой взгляд, это очень красивая ошибка\. Да, я знаю, что у меня странные представления о красоте :\)\.

Сейчас, согласно [приоритетам операторов](https://pvs-studio.ru/ru/blog/terms/0064/), выражение вычисляется следующим образом:

```cpp
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0
```

С практической точки зрения такое условие не имеет смысла, так как его можно сократить до:

```cpp
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())
```

Это явная ошибка\. Скорее всего, 0/1 хотели сравнить с переменной _Index_\. Чтобы исправить код необходимо добавить скобки вокруг тернарного оператора:

```cpp
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))
```

Кстати, тернарный оператор очень опасный и провоцирует логические ошибки\. Будьте очень аккуратны с ним и не жадничайте ставить круглые скобки\. Подробнее эту тему я рассматривал [здесь](https://pvs-studio.ru/ru/blog/posts/cpp/0391/), в главе "Бойтесь оператора ?: и заключайте его в круглые скобки"\.

**Фрагмент N4, N5: Нулевой указатель**

```cpp
Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
  ....
  TypedInit *LHS = dyn_cast<TypedInit>(Result);
  ....
  LHS = dyn_cast<TypedInit>(
    UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get())
      ->Fold(CurRec));
  if (!LHS) {
    Error(PasteLoc, Twine("can't cast '") + LHS->getAsString() +
                    "' to string");
    return nullptr;
  }
  ....
}
```

Предупреждение PVS\-Studio: [V522](https://pvs-studio.ru/ru/docs/warnings/v522/) \[CWE\-476\] Dereferencing of the null pointer 'LHS' might take place\. TGParser\.cpp 2152

Если указатель _LHS_ окажется нулевым, то должно быть выдано предупреждение\. Однако вместо этого произойдёт разыменование этого самого нулевого указателя: _LHS\-\>getAsString\(\)_\.

Это весьма типовая ситуация, когда ошибка прячется в обработчике ошибок, так как их никто не тестирует\. Статические анализаторы проверяют весь достижимый код, независимо от того, как часто он используется\. Это очень хороший пример, как статический анализ дополняет другие методики тестирования и защиты от ошибок\.

Аналогичная ошибка обработки указателя _RHS_ допущена в коде чуть ниже: V522 \[CWE\-476\] Dereferencing of the null pointer 'RHS' might take place\. TGParser\.cpp 2186

**Фрагмент N6: Использование указателя после перемещения**

```cpp
static Expected<bool>
ExtractBlocks(....)
{
  ....
  std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap);
  ....
  BD.setNewProgram(std::move(ProgClone));                                // <=
  MiscompiledFunctions.clear();

  for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
    Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);  // <=
    assert(NewF && "Function not found??");
    MiscompiledFunctions.push_back(NewF);
  }
  ....
}
```

Предупреждение PVS\-Studio: V522 \[CWE\-476\] Dereferencing of the null pointer 'ProgClone' might take place\. Miscompilation\.cpp 601

В начале умный указатель _ProgClone_ перестаёт владеть объектом:

```cpp
BD.setNewProgram(std::move(ProgClone));
```

Фактически, теперь _ProgClone_ \- это нулевой указатель\. Поэтому чуть ниже должно произойти разыменование нулевого указателя:

```cpp
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
```

Но, на самом деле, этого не произойдёт\! Обратите внимание, что цикл на самом деле не выполняется\.

В начале контейнер _MiscompiledFunctions_ очищается:

```cpp
MiscompiledFunctions.clear();
```

Далее размер этого контейнера используется в условии цикла:

```cpp
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
```

Легко видеть, что цикл не запускается\. Думаю, это тоже ошибка, и код должен быть написан иным образом\.

Кажется, мы встретили ту самую знаменитую чётность ошибок\! Одна ошибка маскирует другую :\)\.

**Фрагмент N7: Использование указателя после перемещения**

```cpp
static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
                                    std::unique_ptr<Module> Safe) {
  outs() << "  Optimizing functions being tested: ";
  std::unique_ptr<Module> Optimized =
      BD.runPassesOn(Test.get(), BD.getPassesToRun());
  if (!Optimized) {
    errs() << " Error running this sequence of passes"
           << " on the input program!\n";
    BD.setNewProgram(std::move(Test));                       // <=
    BD.EmitProgressBitcode(*Test, "pass-error", false);      // <=
    if (Error E = BD.debugOptimizerCrash())
      return std::move(E);
    return false;
  }
  ....
}
```

Предупреждение PVS\-Studio: V522 \[CWE\-476\] Dereferencing of the null pointer 'Test' might take place\. Miscompilation\.cpp 709

Вновь та же самая ситуация\. В начале содержимое объекта перемещается, а затем он используется как ни в чём не бывало\. Я всё чаще встречаю эту ситуацию в коде программ, после того, как в С\+\+ появилась семантика перемещения\. За это я и люблю язык C\+\+\! Появляются всё новые и новые способы отстрелить себе ногу\. Анализатору PVS\-Studio всегда будет работа :\)\.

**Фрагмент N8: Нулевой указатель**

```cpp
void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
  uint32_t TypeId = Symbol.getTypeId();
  auto Type = Symbol.getSession().getSymbolById(TypeId);
  if (Type)
    Printer << "<unknown-type>";
  else
    Type->dump(*this);
}
```

Предупреждение PVS\-Studio: V522 \[CWE\-476\] Dereferencing of the null pointer 'Type' might take place\. PrettyFunctionDumper\.cpp 233

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

Правильно:

```cpp
if (Type)
  Type->dump(*this);
else
  Printer << "<unknown-type>";
```

**Фрагмент N9: Нулевой указатель**

```cpp
void SearchableTableEmitter::collectTableEntries(
    GenericTable &Table, const std::vector<Record *> &Items) {
  ....
  RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
  if (!Ty)                                                              // <=
    PrintFatalError(Twine("Field '") + Field.Name + "' of table '" +
                    Table.Name + "' has incompatible type: " +
                    Ty->getAsString() + " vs. " +                       // <=
                    TI->getType()->getAsString());
   ....
}
```

Предупреждение PVS\-Studio: V522 \[CWE\-476\] Dereferencing of the null pointer 'Ty' might take place\. SearchableTableEmitter\.cpp 614

Думаю, и так всё понятно и не требует пояснений\.

**Фрагмент N10: Опечатка**

```cpp
bool FormatTokenLexer::tryMergeCSharpNullConditionals() {
  ....
  auto &Identifier = *(Tokens.end() - 2);
  auto &Question = *(Tokens.end() - 1);
  ....
  Identifier->ColumnWidth += Question->ColumnWidth;
  Identifier->Type = Identifier->Type;                    // <=
  Tokens.erase(Tokens.end() - 1);
  return true;
}
```

Предупреждение PVS\-Studio: [V570](https://pvs-studio.ru/ru/docs/warnings/v570/) The 'Identifier\-\>Type' variable is assigned to itself\. FormatTokenLexer\.cpp 249

Нет смысла присваивать переменную саму себе\. Скорее всего хотели написать:

```cpp
Identifier->Type = Question->Type;
```

**Фрагмент N11: Подозрительный break**

```cpp
void SystemZOperand::print(raw_ostream &OS) const {
  switch (Kind) {
    break;
  case KindToken:
    OS << "Token:" << getToken();
    break;
  case KindReg:
    OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
    break;
  ....
}
```

Предупреждение PVS\-Studio: [V622](https://pvs-studio.ru/ru/docs/warnings/v622/) \[CWE\-478\] Consider inspecting the 'switch' statement\. It's possible that the first 'case' operator is missing\. SystemZAsmParser\.cpp 652

В начале присутствует очень подозрительный оператор _break_\. Не забыли ли здесь написать что\-то ещё?

**Фрагмент N12: Проверка указателя после разыменования**

```cpp
InlineCost AMDGPUInliner::getInlineCost(CallSite CS) {
  Function *Callee = CS.getCalledFunction();
  Function *Caller = CS.getCaller();
  TargetTransformInfo &TTI = TTIWP->getTTI(*Callee);

  if (!Callee || Callee->isDeclaration())
    return llvm::InlineCost::getNever("undefined callee");
  ....
}
```

Предупреждение PVS\-Studio: [V595](https://pvs-studio.ru/ru/docs/warnings/v595/) \[CWE\-476\] The 'Callee' pointer was utilized before it was verified against nullptr\. Check lines: 172, 174\. AMDGPUInline\.cpp 172

Указатель _Callee_ в начале разыменовывается в момент вызова функции _getTTI_\.

А затем оказывается, что этот указатель следует проверять на равенство _nullptr_:

```cpp
if (!Callee || Callee->isDeclaration())
```

Но уже поздно\.\.\.

**Фрагмент N13 \- N\.\.\.: Проверка указателя после разыменования**

Ситуация, рассмотренная в предыдущем фрагменте кода не уникальна\. Она встречается здесь:

```cpp
static Value *optimizeDoubleFP(CallInst *CI, IRBuilder<> &B,
                               bool isBinary, bool isPrecise = false) {
  ....
  Function *CalleeFn = CI->getCalledFunction();
  StringRef CalleeNm = CalleeFn->getName();                 // <=
  AttributeList CalleeAt = CalleeFn->getAttributes();
  if (CalleeFn && !CalleeFn->isIntrinsic()) {               // <=
  ....
}
```

Предупреждение PVS\-Studio: V595 \[CWE\-476\] The 'CalleeFn' pointer was utilized before it was verified against nullptr\. Check lines: 1079, 1081\. SimplifyLibCalls\.cpp 1079

И здесь:

```cpp
void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
                            const Decl *Tmpl, Decl *New,
                            LateInstantiatedAttrVec *LateAttrs,
                            LocalInstantiationScope *OuterMostScope) {
  ....
  NamedDecl *ND = dyn_cast<NamedDecl>(New);
  CXXRecordDecl *ThisContext =
    dyn_cast_or_null<CXXRecordDecl>(ND->getDeclContext());         // <=
  CXXThisScopeRAII ThisScope(*this, ThisContext, Qualifiers(),
                             ND && ND->isCXXInstanceMember());     // <=
  ....
}
```

Предупреждение PVS\-Studio: V595 \[CWE\-476\] The 'ND' pointer was utilized before it was verified against nullptr\. Check lines: 532, 534\. SemaTemplateInstantiateDecl\.cpp 532

И здесь:

* V595 \[CWE\-476\] The 'U' pointer was utilized before it was verified against nullptr\. Check lines: 404, 407\. DWARFFormValue\.cpp 404
* V595 \[CWE\-476\] The 'ND' pointer was utilized before it was verified against nullptr\. Check lines: 2149, 2151\. SemaTemplateInstantiate\.cpp 2149

А дальше мне стало неинтересно изучать предупреждения с номером V595\. Так что я не знаю, есть ли ещё подобные ошибки, помимо перечисленных здесь\. Скорее всего есть\.

**Фрагмент N17, N18: Подозрительный сдвиг**

```cpp
static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
                                           uint64_t &Encoding) {
  ....
  unsigned Size = RegSize;
  ....
  uint64_t NImms = ~(Size-1) << 1;
  ....
}
```

Предупреждение PVS\-Studio: [V629](https://pvs-studio.ru/ru/docs/warnings/v629/) \[CWE\-190\] Consider inspecting the '\~\(Size \- 1\) << 1' expression\. Bit shifting of the 32\-bit value with a subsequent expansion to the 64\-bit type\. AArch64AddressingModes\.h 260

Возможно, это не ошибка, и код работает именно так, как и задумано\. Но это явно очень подозрительное место, и его нужно проверить\.

Допустим, переменная _Size_ равна 16, и тогда автор кода планировал получить в переменной _NImms_ значение:

1111111111111111111111111111111111111111111111111111111111100000

Однако, на самом деле, получится значение:

0000000000000000000000000000000011111111111111111111111111100000

Дело в том, что все вычисления происходят с использованием 32\-битного типа unsigned\. И только затем, этот 32\-битный беззнаковый тип будет неявно расширен до _uint64\_t_\. При этом старшие биты окажутся нулевыми\.

Исправить ситуацию можно так:

```cpp
uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
```

Аналогичная ситуация: V629 \[CWE\-190\] Consider inspecting the 'Immr << 6' expression\. Bit shifting of the 32\-bit value with a subsequent expansion to the 64\-bit type\. AArch64AddressingModes\.h 269

**Фрагмент N19: Пропущено ключевое слово _else_?**

```cpp
void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
  ....
  if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
    // VOP2b (v_add_u32, v_sub_u32 ...) dpp use "vcc" token.
    // Skip it.
    continue;
  } if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) {    // <=
    Op.addRegWithFPInputModsOperands(Inst, 2);
  } else if (Op.isDPPCtrl()) {
    Op.addImmOperands(Inst, 1);
  } else if (Op.isImm()) {
    // Handle optional arguments
    OptionalIdx[Op.getImmTy()] = I;
  } else {
    llvm_unreachable("Invalid operand type");
  }
  ....
}
```

Предупреждение PVS\-Studio: [V646](https://pvs-studio.ru/ru/docs/warnings/v646/) \[CWE\-670\] Consider inspecting the application's logic\. It's possible that 'else' keyword is missing\. AMDGPUAsmParser\.cpp 5655

Ошибки здесь нет\. Так как then\-блок первого _if_ оканчивается на _continue_, то не важно, есть ключевое слово _else_ или нет\. В любом случае код будет работать одинаково\. Тем не менее, пропущенный _else_ делает код более непонятным и опасным\. Если в дальнейшем _continue_ исчезнет, то код начнёт работать совсем по\-другому\. На мой взгляд, лучше добавить _else_\.

**Фрагмент N20: Четыре однотипных опечатки**

```cpp
LLVM_DUMP_METHOD void Symbol::dump(raw_ostream &OS) const {
  std::string Result;
  if (isUndefined())
    Result += "(undef) ";
  if (isWeakDefined())
    Result += "(weak-def) ";
  if (isWeakReferenced())
    Result += "(weak-ref) ";
  if (isThreadLocalValue())
    Result += "(tlv) ";
  switch (Kind) {
  case SymbolKind::GlobalSymbol:
    Result + Name.str();                        // <=
    break;
  case SymbolKind::ObjectiveCClass:
    Result + "(ObjC Class) " + Name.str();      // <=
    break;
  case SymbolKind::ObjectiveCClassEHType:
    Result + "(ObjC Class EH) " + Name.str();   // <=
    break;
  case SymbolKind::ObjectiveCInstanceVariable:
    Result + "(ObjC IVar) " + Name.str();       // <=
    break;
  }
  OS << Result;
}
```

Предупреждения PVS\-Studio:

* V655 \[CWE\-480\] The strings were concatenated but are not utilized\. Consider inspecting the 'Result \+ Name\.str\(\)' expression\. Symbol\.cpp 32
* V655 \[CWE\-480\] The strings were concatenated but are not utilized\. Consider inspecting the 'Result \+ "\(ObjC Class\) " \+ Name\.str\(\)' expression\. Symbol\.cpp 35
* V655 \[CWE\-480\] The strings were concatenated but are not utilized\. Consider inspecting the 'Result \+ "\(ObjC Class EH\) " \+ Name\.str\(\)' expression\. Symbol\.cpp 38
* V655 \[CWE\-480\] The strings were concatenated but are not utilized\. Consider inspecting the 'Result \+ "\(ObjC IVar\) " \+ Name\.str\(\)' expression\. Symbol\.cpp 41

Случайно вместо оператора \+\= используется оператор \+\. В результате получаются конструкции, лишённые смысла\.

**Фрагмент N21: Неопределённое поведение**

```cpp
static void getReqFeatures(std::map<StringRef, int> &FeaturesMap,
                           const std::vector<Record *> &ReqFeatures) {
  for (auto &R : ReqFeatures) {
    StringRef AsmCondString = R->getValueAsString("AssemblerCondString");

    SmallVector<StringRef, 4> Ops;
    SplitString(AsmCondString, Ops, ",");
    assert(!Ops.empty() && "AssemblerCondString cannot be empty");

    for (auto &Op : Ops) {
      assert(!Op.empty() && "Empty operator");
      if (FeaturesMap.find(Op) == FeaturesMap.end())
        FeaturesMap[Op] = FeaturesMap.size();
    }
  }
}
```

Попробуйте самостоятельно найти опасный код\. А это картинка для отвлечения внимания, чтобы сразу не посмотреть на ответ:

![0629_LLVM_8_ru/image2.png](https://import.viva64.com/docx/blog/0629_LLVM_8_ru/image2.png)

Предупреждение PVS\-Studio: [V708](https://pvs-studio.ru/ru/docs/warnings/v708/) \[CWE\-758\] Dangerous construction is used: 'FeaturesMap\[Op\] \= FeaturesMap\.size\(\)', where 'FeaturesMap' is of 'map' class\. This may lead to undefined behavior\. RISCVCompressInstEmitter\.cpp 490

Проблемная строчка:

```cpp
FeaturesMap[Op] = FeaturesMap.size();
```

Если элемент _Op_ не найден, то создаётся новый элемент в карте и туда записывается количество элементов в этой карте\. Вот только неизвестно, будет вызвана функция _size_ до или после добавления нового элемента\.

**Фрагмент N22\-N24: Повторные присваивания**

```cpp
Error MachOObjectFile::checkSymbolTable() const {
  ....
  } else {
    MachO::nlist STE = getSymbolTableEntry(SymDRI);
    NType = STE.n_type;                              // <=
    NType = STE.n_type;                              // <=
    NSect = STE.n_sect;
    NDesc = STE.n_desc;
    NStrx = STE.n_strx;
    NValue = STE.n_value;
  }
  ....
}
```

Предупреждение PVS\-Studio: [V519](https://pvs-studio.ru/ru/docs/warnings/v519/) \[CWE\-563\] The 'NType' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 1663, 1664\. MachOObjectFile\.cpp 1664

Думаю, настоящей ошибки здесь нет\. Просто лишнее повторяющееся присваивание\. Но всё равно ляп\.

Аналогично:

* V519 \[CWE\-563\] The 'B\.NDesc' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 1488, 1489\. llvm\-nm\.cpp 1489
* V519 \[CWE\-563\] The variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 59, 61\. coff2yaml\.cpp 61

**Фрагмент N25\-N27: Ещё повторные присваивания**

Теперь рассмотрим немного другой вариант повторного присваивания\.

```cpp
bool Vectorizer::vectorizeLoadChain(
    ArrayRef<Instruction *> Chain,
    SmallPtrSet<Instruction *, 16> *InstructionsProcessed) {
  ....
  unsigned Alignment = getAlignment(L0);
  ....
  unsigned NewAlign = getOrEnforceKnownAlignment(L0->getPointerOperand(),
                                                 StackAdjustedAlignment,
                                                 DL, L0, nullptr, &DT);
  if (NewAlign != 0)
    Alignment = NewAlign;
  Alignment = NewAlign;
  ....
}
```

Предупреждение PVS\-Studio: V519 \[CWE\-563\] The 'Alignment' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 1158, 1160\. LoadStoreVectorizer\.cpp 1160

Это очень странный код, который по всей видимости содержит логическую ошибку\. В начале, переменной _Alignment_ присваивается значение в зависимости от условия\. А затем вновь происходит присваивание, но теперь уже без всякой проверки\.

Аналогичные ситуации можно увидеть здесь:

* V519 \[CWE\-563\] The 'Effects' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 152, 165\. WebAssemblyRegStackify\.cpp 165
* V519 \[CWE\-563\] The 'ExpectNoDerefChunk' variable is assigned values twice successively\. Perhaps this is a mistake\. Check lines: 4970, 4973\. SemaType\.cpp 4973

**Фрагмент N28: Всегда истинное условие**

```cpp
static int readPrefixes(struct InternalInstruction* insn) {
  ....
  uint8_t byte = 0;
  uint8_t nextByte;
  ....
  if (byte == 0xf3 && (nextByte == 0x88 || nextByte == 0x89 ||
                       nextByte == 0xc6 || nextByte == 0xc7)) {
    insn->xAcquireRelease = true;
    if (nextByte != 0x90) // PAUSE instruction support             // <=
      break;
  }
  ....
}
```

Предупреждение PVS\-Studio: [V547](https://pvs-studio.ru/ru/docs/warnings/v547/) \[CWE\-571\] Expression 'nextByte \!\= 0x90' is always true\. X86DisassemblerDecoder\.cpp 379

Проверка не имеет смысла\. Переменная _nextByte_ всегда не равна значению _0x90_, что вытекает из предыдущей проверки\. Это какая\-то логическая ошибка\.

**Фрагмент N29 \- N\.\.\.: Всегда истинные/ложные условия**

Анализатор выдаёт много предупреждений о том, что всё условие \([V547](https://pvs-studio.ru/ru/docs/warnings/v547/)\) или его часть \([V560](https://pvs-studio.ru/ru/docs/warnings/v560/)\) всегда истинно или ложно\. Часто это не настоящие ошибки, а просто неаккуратный код, результат развертывания макросов и тому подобное\. Тем не менее, есть смысл посмотреть все эти предупреждения, так как время от времени встречаются настоящие логические ошибки\. Например, подозрителен вот этот участок кода:

```cpp
static DecodeStatus DecodeGPRPairRegisterClass(MCInst &Inst, unsigned RegNo,
                                   uint64_t Address, const void *Decoder) {
  DecodeStatus S = MCDisassembler::Success;

  if (RegNo > 13)
    return MCDisassembler::Fail;

  if ((RegNo & 1) || RegNo == 0xe)
     S = MCDisassembler::SoftFail;
  ....
}
```

Предупреждение PVS\-Studio: [V560](https://pvs-studio.ru/ru/docs/warnings/v560/) \[CWE\-570\] A part of conditional expression is always false: RegNo \=\= 0xe\. ARMDisassembler\.cpp 939

Константа 0xE это значение 14 в десятичной системе\. Проверка _RegNo \=\= 0xe_ не имеет смысла, так как если _RegNo \> 13_, то функция завершит своё выполнение\.

Было множество других предупреждений с идентификатором V547 и V560, но, как и в случае с [V595](https://pvs-studio.ru/ru/docs/warnings/v595/), изучать эти предупреждения мне было неинтересно\. Было и так понятно, что мне хватит материала для написания статьи :\)\. Поэтому неизвестно, сколько всего можно выявить ошибок этого типа в LLVM с помощью PVS\-Studio\.

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

```cpp
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
                                          tok::TokenKind ClosingBraceKind) {
  bool HasError = false;
  ....
  HasError = true;
  if (!ContinueOnSemicolons)
    return !HasError;
  ....
}
```

Предупреждение PVS\-Studio: V547 \[CWE\-570\] Expression '\!HasError' is always false\. UnwrappedLineParser\.cpp 1635

**Фрагмент N30: Подозрительный return**

```cpp
static bool
isImplicitlyDef(MachineRegisterInfo &MRI, unsigned Reg) {
  for (MachineRegisterInfo::def_instr_iterator It = MRI.def_instr_begin(Reg),
      E = MRI.def_instr_end(); It != E; ++It) {
    return (*It).isImplicitDef();
  }
  ....
}
```

Предупреждение PVS\-Studio: [V612](https://pvs-studio.ru/ru/docs/warnings/v612/) \[CWE\-670\] An unconditional 'return' within a loop\. R600OptimizeVectorRegisters\.cpp 63

Это или ошибка, или специфический приём, который призван что\-то пояснить программистам, читающим код\. Мне такая конструкция ничего не поясняет и выглядит очень подозрительной\. Лучше так не писать :\)\.

Устали? Тогда время заварить чай или кофе\.

![0629_LLVM_8_ru/image3.png](https://import.viva64.com/docx/blog/0629_LLVM_8_ru/image3.png)

## Дефекты, выявленные новыми диагностиками

Думаю, 30 срабатываний старых диагностик достаточно\. Давайте теперь посмотрим, что интересного можно находить новыми диагностиками, которые появились в анализаторе уже после [предыдущей](https://pvs-studio.ru/ru/blog/posts/cpp/0446/) проверки\. Всего за это время в C\+\+ анализаторе добавилось 66 диагностик общего назначения\.

**Фрагмент N31: Недостижимый код**

```cpp
Error CtorDtorRunner::run() {
  ....
  if (auto CtorDtorMap =
          ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
                    NoDependenciesToRegister, true))
  {
    ....
    return Error::success();
  } else
    return CtorDtorMap.takeError();

  CtorDtorsByPriority.clear();

  return Error::success();
}
```

Предупреждение PVS\-Studio: [V779](https://pvs-studio.ru/ru/docs/warnings/v779/) \[CWE\-561\] Unreachable code detected\. It is possible that an error is present\. ExecutionUtils\.cpp 146

Как видите, обе ветки оператора _if_ заканчиваются вызовом оператора _return_\. Соответственно, контейнер _CtorDtorsByPriority_ никогда не будет очищен\.

**Фрагмент N32: Недостижимый код**

```cpp
bool LLParser::ParseSummaryEntry() {
  ....
  switch (Lex.getKind()) {
  case lltok::kw_gv:
    return ParseGVEntry(SummaryID);
  case lltok::kw_module:
    return ParseModuleEntry(SummaryID);
  case lltok::kw_typeid:
    return ParseTypeIdEntry(SummaryID);                        // <=
    break;                                                     // <=
  default:
    return Error(Lex.getLoc(), "unexpected summary kind");
  }
  Lex.setIgnoreColonInIdentifiers(false);                      // <=
  return false;
}
```

Предупреждение PVS\-Studio: V779 \[CWE\-561\] Unreachable code detected\. It is possible that an error is present\. LLParser\.cpp 835

Интересная ситуация\. Давайте рассмотрим в начале вот это место:

```cpp
return ParseTypeIdEntry(SummaryID);
break;
```

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

Анализатор выдаёт предупреждение на строчки:

```cpp
Lex.setIgnoreColonInIdentifiers(false);
return false;
```

И действительно, этот код недостижим\. Все случаи в _switch_ заканчиваются вызовом оператором _return_\. И теперь бессмысленный одинокий _break_ не выглядит таким безобидным\! Быть может одна из веток должна заканчиваться на _break_, а не на _return_?

**Фрагмент N33: Случайное обнуление старших бит**

```cpp
unsigned getStubAlignment() override {
  if (Arch == Triple::systemz)
    return 8;
  else
    return 1;
}

Expected<unsigned>
RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
                             const SectionRef &Section,
                             bool IsCode) {
  ....
  uint64_t DataSize = Section.getSize();
  ....
  if (StubBufSize > 0)
    DataSize &= ~(getStubAlignment() - 1);
  ....
}
```

Предупреждение PVS\-Studio: [V784](https://pvs-studio.ru/ru/docs/warnings/v784/) The size of the bit mask is less than the size of the first operand\. This will cause the loss of higher bits\. RuntimeDyld\.cpp 815

Обратите внимание, что функция _getStubAlignment_ возвращает тип _unsigned_\. Вычислим значение выражения, если предположить, что функция вернёт значение 8:

\~\(getStubAlignment\(\) \- 1\)

\~\(8u\-1\)

0xFFFFFFF8‬u

Теперь обратите внимание, что переменная _DataSize_ имеет 64\-битный беззнаковый тип\. Получается, что при выполнении операции DataSize & 0xFFFFFFF8‬u все тридцать два старших бита будут обнулены\. Скорее всего, это не то, что хотел программист\. Подозреваю, что он хотел вычислить: DataSize & 0xFFFFFFFFFFFFFFF8‬u\.

Чтобы исправить ошибку, следует написать так:

```cpp
DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);
Или так:
DataSize &= ~(getStubAlignment() - 1ULL);
```

**Фрагмент N34: Неудачное явное приведение типа**

```cpp
template <typename T>
void scaleShuffleMask(int Scale, ArrayRef<T> Mask,
                      SmallVectorImpl<T> &ScaledMask) {
  assert(0 < Scale && "Unexpected scaling factor");
  int NumElts = Mask.size();
  ScaledMask.assign(static_cast<size_t>(NumElts * Scale), -1);
  ....
}
```

Предупреждение PVS\-Studio: [V1028](https://pvs-studio.ru/ru/docs/warnings/v1028/) \[CWE\-190\] Possible overflow\. Consider casting operands of the 'NumElts \* Scale' operator to the 'size\_t' type, not the result\. X86ISelLowering\.h 1577

Явное приведение типа используется для того, чтобы не возникло переполнение при перемножении переменных типа _int_\. Однако, здесь явное приведение типа не защищает от переполнения\. В начале переменные будут перемножены, и только потом 32\-битный результат умножения будет расширен до типа [_size\_t_](https://pvs-studio.ru/ru/blog/terms/0044/)\.

**Фрагмент N35: Неудачный Copy\-Paste**

```cpp
Instruction *InstCombiner::visitFCmpInst(FCmpInst &I) {
  ....
  if (!match(Op0, m_PosZeroFP()) && isKnownNeverNaN(Op0, &TLI)) {
    I.setOperand(0, ConstantFP::getNullValue(Op0->getType()));
    return &I;
  }
  if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
    I.setOperand(1, ConstantFP::getNullValue(Op0->getType()));        // <=
    return &I;
  }
  ....
}
```

[V778](https://pvs-studio.ru/ru/docs/warnings/v778/) \[CWE\-682\] Two similar code fragments were found\. Perhaps, this is a typo and 'Op1' variable should be used instead of 'Op0'\. InstCombineCompares\.cpp 5507

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

Обратите внимание, что во втором блоке меняли _Op0_ на _Op1_\. Но в одном месте не поправили\. Скорее всего, должно было быть написано так:

```cpp
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
  I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
  return &I;
}
```

**Фрагмент N36: Путаница в переменных**

```cpp
struct Status {
  unsigned Mask;
  unsigned Mode;

  Status() : Mask(0), Mode(0){};

  Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
    Mode &= Mask;
  };
  ....
};
```

Предупреждение PVS\-Studio: [V1001](https://pvs-studio.ru/ru/docs/warnings/v1001/) \[CWE\-563\] The 'Mode' variable is assigned but is not used by the end of the function\. SIModeRegister\.cpp 48

Очень опасно давать аргументам функций те же самые имена, что и членам класса\. Очень легко запутаться\. Перед нами как раз такой случай\. Это выражение не имеет смысла:

```cpp
Mode &= Mask;
```

Меняется аргумент функции\. И всё\. Этот аргумент больше никак не используется\. Скорее всего, надо было написать так:

```cpp
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
  this->Mode &= Mask;
};
```

**Фрагмент N37: Путаница в переменных**

```cpp
class SectionBase {
  ....
  uint64_t Size = 0;
  ....
};

class SymbolTableSection : public SectionBase {
  ....
};

void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
                                   SectionBase *DefinedIn, uint64_t Value,
                                   uint8_t Visibility, uint16_t Shndx,
                                   uint64_t Size) {
  ....
  Sym.Value = Value;
  Sym.Visibility = Visibility;
  Sym.Size = Size;
  Sym.Index = Symbols.size();
  Symbols.emplace_back(llvm::make_unique<Symbol>(Sym));
  Size += this->EntrySize;
}
```

Предупреждение PVS\-Studio: V1001 \[CWE\-563\] The 'Size' variable is assigned but is not used by the end of the function\. Object\.cpp 424

Ситуация аналогична предыдущей\. Должно быть написано:

```cpp
this->Size += this->EntrySize;
```

**Фрагмент N38\-N47: Указатель забыли проверить**

Ранее мы рассматривали примеры срабатывания диагностики [V595](https://pvs-studio.ru/ru/docs/warnings/v595/)\. Её суть в том, что указатель в начале разыменовывается, а только потом проверяется\. Молодая диагностика [V1004](https://pvs-studio.ru/ru/docs/warnings/v1004/) является обратной ей по смыслу, но также обнаруживает очень много ошибок\. Она выявляет ситуации, когда указатель в начале проверяли, а затем забыли это сделать\. Рассмотрим такие случаи, найденные внутри LLVM\.

```cpp
int getGEPCost(Type *PointeeType, const Value *Ptr,
               ArrayRef<const Value *> Operands) {
  ....
  if (Ptr != nullptr) {                                            // <=
    assert(....);
    BaseGV = dyn_cast<GlobalValue>(Ptr->stripPointerCasts());
  }
  bool HasBaseReg = (BaseGV == nullptr);

  auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());  // <=
  ....
}
```

Предупреждение PVS\-Studio: V1004 \[CWE\-476\] The 'Ptr' pointer was used unsafely after it was verified against nullptr\. Check lines: 729, 738\. TargetTransformInfoImpl\.h 738

Переменная _Ptr_ может быть равна _nullptr_, о чём свидетельствует проверка:

```cpp
if (Ptr != nullptr)
```

Однако, ниже этот указатель разыменовывается уже без предварительной проверки:

```cpp
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());
```

Рассмотрим другой аналогичный случай\.

```cpp
llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
                                                          bool Stub) {
  ....
  auto *FD = dyn_cast<FunctionDecl>(GD.getDecl());
  SmallVector<QualType, 16> ArgTypes;
  if (FD)                                                                // <=
    for (const ParmVarDecl *Parm : FD->parameters())
      ArgTypes.push_back(Parm->getType());
  CallingConv CC = FD->getType()->castAs<FunctionType>()->getCallConv(); // <=
  ....
}
```

Предупреждение PVS\-Studio: V1004 \[CWE\-476\] The 'FD' pointer was used unsafely after it was verified against nullptr\. Check lines: 3228, 3231\. CGDebugInfo\.cpp 3231

Обратите внимание на указатель _FD_\. Уверен, проблема хорошо видна, и специальных пояснений не требуется\.

И ещё:

```cpp
static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result,
                                         Value *&BasePtr,
                                         const DataLayout &DL) {
  PointerType *PtrTy = dyn_cast<PointerType>(Ptr.getType());
  if (!PtrTy) {                                                   // <=
    Result = Polynomial();
    BasePtr = nullptr;
  }
  unsigned PointerBits =
      DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace());     // <=
  ....
}
```

Предупреждение PVS\-Studio: V1004 \[CWE\-476\] The 'PtrTy' pointer was used unsafely after it was verified against nullptr\. Check lines: 960, 965\. InterleavedLoadCombinePass\.cpp 965

Как защититься от таких ошибок? Будьте внимательней на Code\-Review и используйте для регулярной проверки кода статический анализатор PVS\-Studio\.

Приводить другие фрагменты кода с ошибками данного вида смысла нет\. Оставлю в статье только список предупреждений:

* V1004 \[CWE\-476\] The 'Expr' pointer was used unsafely after it was verified against nullptr\. Check lines: 1049, 1078\. DebugInfoMetadata\.cpp 1078
* V1004 \[CWE\-476\] The 'PI' pointer was used unsafely after it was verified against nullptr\. Check lines: 733, 753\. LegacyPassManager\.cpp 753
* V1004 \[CWE\-476\] The 'StatepointCall' pointer was used unsafely after it was verified against nullptr\. Check lines: 4371, 4379\. Verifier\.cpp 4379
* V1004 \[CWE\-476\] The 'RV' pointer was used unsafely after it was verified against nullptr\. Check lines: 2263, 2268\. TGParser\.cpp 2268
* V1004 \[CWE\-476\] The 'CalleeFn' pointer was used unsafely after it was verified against nullptr\. Check lines: 1081, 1096\. SimplifyLibCalls\.cpp 1096
* V1004 \[CWE\-476\] The 'TC' pointer was used unsafely after it was verified against nullptr\. Check lines: 1819, 1824\. Driver\.cpp 1824

**Фрагмент N48\-N60: Не критично, но дефект \(возможна утечка памяти\)**

```cpp
std::unique_ptr<IRMutator> createISelMutator() {
  ....
  std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
  Strategies.emplace_back(
      new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
  ....
}
```

Предупреждение PVS\-Studio: [V1023](https://pvs-studio.ru/ru/docs/warnings/v1023/) \[CWE\-460\] A pointer without owner is added to the 'Strategies' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-isel\-fuzzer\.cpp 58

Для добавлении элемента в конец контейнера типа _std::vector<std::unique\_ptr<X\>\>_ нельзя просто написать _xxx\.push\_back\(new X\)_, так как нет неявного преобразования из _X\*_ в _std::unique\_ptr<X\>_\.

Распространенным решением является написание _xxx\.emplace\_back\(new X\)_, так как он компилируется: метод _emplace\_back_ конструирует элемент непосредственно из аргументов и поэтому может использовать явные конструкторы\.

Это небезопасно\. Если вектор полон, то происходит перевыделение памяти\. Операция перевыделения памяти может закончиться неудачей, в результате чего будет сгенерировано исключение _std::bad\_alloc_\. В этом случае указатель будет потерян, и созданный объект никогда не будет удален\.

Безопасным решением является создание _unique\_ptr_, который будет владеть указателем до того, как вектор попытается перевыделить память:

```cpp
xxx.push_back(std::unique_ptr<X>(new X))
```

Начиная с C\+\+14, можно использовать 'std::make\_unique':

```cpp
xxx.push_back(std::make_unique<X>())
```

Данный вид дефекта не критичен для LLVM\. Если не удастся выделить память, то работа компилятора будет просто остановлена\. Однако, для приложений с долгим [аптаймом](https://ru.wikipedia.org/wiki/Аптайм), которые не могут просто так завершаться, если не удалось выделить память, это может быть настоящей неприятной ошибкой\.

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

Другие предупреждения данного типа:

* V1023 \[CWE\-460\] A pointer without owner is added to the 'Passes' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. PassManager\.h 546
* V1023 \[CWE\-460\] A pointer without owner is added to the 'AAs' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. AliasAnalysis\.h 324
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Entries' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. DWARFDebugFrame\.cpp 519
* V1023 \[CWE\-460\] A pointer without owner is added to the 'AllEdges' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. CFGMST\.h 268
* V1023 \[CWE\-460\] A pointer without owner is added to the 'VMaps' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. SimpleLoopUnswitch\.cpp 2012
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Records' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. FDRLogBuilder\.h 30
* V1023 \[CWE\-460\] A pointer without owner is added to the 'PendingSubmodules' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. ModuleMap\.cpp 810
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Objects' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. DebugMap\.cpp 88
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Strategies' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-isel\-fuzzer\.cpp 60
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 685
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 686
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 688
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 689
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 690
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 691
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 692
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 693
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Modifiers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. llvm\-stress\.cpp 694
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Operands' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. GlobalISelEmitter\.cpp 1911
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Stash' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. GlobalISelEmitter\.cpp 2100
* V1023 \[CWE\-460\] A pointer without owner is added to the 'Matchers' container by the 'emplace\_back' method\. A memory leak will occur in case of an exception\. GlobalISelEmitter\.cpp 2702

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

Всего я выписал 60 предупреждений, после чего остановился\. Есть ли другие дефекты, которые обнаруживает в LLVM анализатор PVS\-Studio? Да, есть\. Однако, когда я выписывал фрагменты кода для статьи, наступил поздний вечер, вернее, даже ночь, и я решил, что пора закругляться\.

Надеюсь, вам было интересно, и вы захотите попробовать анализатор PVS\-Studio\.

Вы можете скачать анализатор и получить триальный ключ на [этой странице](https://pvs-studio.ru/ru/pvs-studio/download/)\.

Самое главное, используйте статический анализ регулярно\. **Разовые проверки**, выполняемыми нами с целью популяризации методологии статического анализа и PVS\-Studio не являются нормальным сценарием\.

Удачи в улучшении качества и надёжности кода\!