Hľadanie chýb v LLVM 8 pomocou analyzátora PVS-Studio

Hľadanie chýb v LLVM 8 pomocou analyzátora PVS-Studio
Od poslednej kontroly kódu projektu LLVM pomocou nášho analyzátora PVS-Studio ubehli viac ako dva roky. Uistime sa, že analyzátor PVS-Studio je stále popredným nástrojom na identifikáciu chýb a potenciálnych zraniteľností. Za týmto účelom skontrolujeme a nájdeme nové chyby vo vydaní LLVM 8.0.0.

Článok na napísanie

Aby som bol úprimný, nechcel som písať tento článok. Nie je zaujímavé písať o projekte, ktorý sme už niekoľkokrát skontrolovali (1, 2, 3). Je lepšie písať o niečom novom, ale nemám na výber.

Zakaždým, keď je vydaná alebo aktualizovaná nová verzia LLVM Clang statický analyzátor, dostávame na našu poštu otázky nasledujúceho typu:

Pozrite, nová verzia Clang Static Analyzer sa naučila nájsť nové chyby! Zdá sa mi, že relevancia používania PVS-Studio klesá. Clang nachádza viac chýb ako predtým a doháňa možnosti PVS-Studio. Čo si o tom myslíš?

Na to chcem vždy odpovedať niečo ako:

Ani my nesedíme nečinne! Výrazne sme zlepšili možnosti analyzátora PVS-Studio. Takže sa nebojte, pokračujeme vo vedení ako doteraz.

Bohužiaľ, toto je zlá odpoveď. Nie sú v ňom žiadne dôkazy. A preto teraz píšem tento článok. Projekt LLVM bol teda opäť skontrolovaný a našli sa v ňom rôzne chyby. Teraz ukážem tie, ktoré sa mi zdali zaujímavé. Clang Static Analyzer nemôže nájsť tieto chyby (alebo je mimoriadne nepohodlné to urobiť s jeho pomocou). Ale môžeme. Navyše som všetky tieto chyby našiel a zapísal za jeden večer.

Napísanie článku však trvalo niekoľko týždňov. Nevedel som sa odhodlať dať toto všetko do textu :).

Mimochodom, ak vás zaujíma, aké technológie sa používajú v analyzátore PVS-Studio na identifikáciu chýb a potenciálnych zraniteľností, odporúčam vám zoznámiť sa s týmto Poznámka.

Nová a stará diagnostika

Ako už bolo uvedené, asi pred dvoma rokmi bol projekt LLVM opäť skontrolovaný a zistené chyby boli opravené. Teraz tento článok predstaví novú dávku chýb. Prečo sa našli nové chyby? Sú na to 3 dôvody:

  1. Projekt LLVM sa vyvíja, mení starý kód a pridáva nový kód. Prirodzene, v upravenom a napísanom kóde sú nové chyby. To jasne ukazuje, že statická analýza by sa mala používať pravidelne a nie príležitostne. Naše články dobre ukazujú možnosti analyzátora PVS-Studio, ale to nemá nič spoločné so zlepšením kvality kódu a znížením nákladov na opravu chýb. Pravidelne používajte analyzátor statického kódu!
  2. Dokončujeme a vylepšujeme existujúcu diagnostiku. Analyzátor preto dokáže identifikovať chyby, ktoré si nevšimol počas predchádzajúcich kontrol.
  3. V PVS-Studio sa objavila nová diagnostika, ktorá pred 2 rokmi neexistovala. Rozhodol som sa ich vyzdvihnúť v samostatnej časti, aby som názorne ukázal vývoj PVS-Studio.

Vady zistené diagnostikou, ktorá existovala pred 2 rokmi

Fragment N1: Copy-Paste

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
  ....
}

Upozornenie PVS-Studio: V501 [CWE-570] Naľavo a napravo od výrazu „||“ sú rovnaké podvýrazy 'Name.startswith("avx512.mask.permvar.")' operátor. AutoUpgrade.cpp 73

Je dvakrát skontrolované, či názov začína podreťazcom "avx512.mask.permvar.". Pri druhej kontrole chceli očividne napísať niečo iné, no zabudli opraviť skopírovaný text.

Fragment N2: preklep

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;
  ....
}

Upozornenie PVS-Studio: V501 Naľavo a napravo od '|' sú rovnaké podvýrazy 'CXNameRange_WantQualifier' operátor. CIndex.cpp 7245

Kvôli preklepu sa rovnaká pomenovaná konštanta použije dvakrát CXNameRange_WantQualifier.

Fragment N3: Zámena s prioritou operátora

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

Upozornenie PVS-Studio: V502 [CWE-783] Operátor '?:' možno funguje iným spôsobom, ako sa očakávalo. Operátor '?:' má nižšiu prioritu ako operátor '=='. PPCTargetTransformInfo.cpp 404

Podľa mňa je to veľmi krásny omyl. Áno, viem, že mám zvláštne predstavy o kráse :).

Teraz podľa priority operátorov, výraz sa vyhodnotí takto:

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

Z praktického hľadiska takáto podmienka nedáva zmysel, pretože ju možno redukovať na:

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

Toto je jasná chyba. S najväčšou pravdepodobnosťou chceli porovnať 0/1 s premennou index. Ak chcete opraviť kód, musíte pridať zátvorky okolo ternárneho operátora:

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

Mimochodom, ternárny operátor je veľmi nebezpečný a vyvoláva logické chyby. Buďte s ním veľmi opatrní a nebuďte lakomí so zátvorkami. Pozrel som sa na túto tému podrobnejšie tu, v kapitole „Pozor na ?: Operátor a vložte ho do zátvoriek“.

Fragment N4, N5: Nulový ukazovateľ

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;
  }
  ....
}

Upozornenie PVS-Studio: V522 [CWE-476] Môže dôjsť k dereferencovaniu nulového ukazovateľa „LHS“. TGParser.cpp 2152

Ak je ukazovateľ LHS je nulová, malo by sa vydať varovanie. Namiesto toho však bude tento rovnaký nulový ukazovateľ zrušený: LHS->getAsString().

Toto je veľmi typická situácia, keď je chyba skrytá v obslužnom programe chýb, pretože ich nikto netestuje. Statické analyzátory kontrolujú všetok dosiahnuteľný kód bez ohľadu na to, ako často sa používa. Toto je veľmi dobrý príklad toho, ako statická analýza dopĺňa iné techniky testovania a ochrany pred chybami.

Podobná chyba pri manipulácii s ukazovateľom RHS povolené v kóde nižšie: V522 [CWE-476] Môže dôjsť k dereferencovaniu nulového ukazovateľa 'RHS'. TGParser.cpp 2186

Fragment N6: Použitie ukazovateľa po presunutí

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);
  }
  ....
}

Varovanie PVS-Studio: V522 [CWE-476] Môže dôjsť k dereferencovaniu nulového ukazovateľa 'ProgClone'. Miscompilation.cpp 601

Na začiatku inteligentný ukazovateľ ProgClone prestane vlastniť predmet:

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

V skutočnosti teraz ProgClone je nulový ukazovateľ. Preto by sa mala dereferencia nulového ukazovateľa vyskytnúť tesne pod:

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

Ale v skutočnosti sa to nestane! Všimnite si, že slučka sa v skutočnosti nevykoná.

Na začiatku nádoby MiscompiledFunctions vymazané:

MiscompiledFunctions.clear();

Ďalej sa veľkosť tohto kontajnera použije v stave cyklu:

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

Je ľahké vidieť, že slučka sa nespustí. Myslím, že toto je tiež chyba a kód by mal byť napísaný inak.

Zdá sa, že sme narazili na tú povestnú paritu chýb! Jedna chyba maskuje druhú :).

Fragment N7: Použitie ukazovateľa po presunutí

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;
  }
  ....
}

Upozornenie PVS-Studio: V522 [CWE-476] Môže dôjsť k dereferencovaniu nulového ukazovateľa „Test“. Nesprávna kompilácia.cpp 709

Opäť tá istá situácia. Najprv sa obsah objektu presunie a potom sa používa, akoby sa nič nestalo. Túto situáciu vidím čoraz častejšie v programovom kóde po tom, čo sa v C++ objavila sémantika pohybu. To je dôvod, prečo milujem jazyk C++! Existuje stále viac nových spôsobov, ako si odstreliť vlastnú nohu. Analyzátor PVS-Studio bude vždy fungovať :).

Fragment N8: nulový ukazovateľ

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);
}

Upozornenie PVS-Studio: V522 [CWE-476] Môže dôjsť k dereferencovaniu nulového ukazovateľa „Typ“. PrettyFunctionDumper.cpp 233

Okrem obsluhy chýb sa zvyčajne netestujú funkcie ladenia tlače. Práve takýto prípad máme pred sebou. Funkcia čaká na používateľa, ktorý namiesto riešenia svojich problémov bude nútený ju opraviť.

opraviť:

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

Fragment N9: nulový ukazovateľ

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());
   ....
}

Varovanie PVS-Studio: V522 [CWE-476] Môže dôjsť k dereferencovaniu nulového ukazovateľa 'Ty'. SearchableTableEmitter.cpp 614

Myslím, že všetko je jasné a nevyžaduje si vysvetlenie.

Fragment N10: preklep

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;
}

Upozornenie PVS-Studio: V570 Premenná 'Identifier->Type' je priradená sama sebe. FormatTokenLexer.cpp 249

Nemá zmysel priraďovať premennú k sebe samej. S najväčšou pravdepodobnosťou chceli napísať:

Identifier->Type = Question->Type;

Fragment N11: Podozrivý zlom

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

Upozornenie PVS-Studio: V622 [CWE-478] Zvážte kontrolu vyhlásenia o prepnutí. Je možné, že prvý operátor „prípadu“ chýba. SystemZAsmParser.cpp 652

Na začiatku je veľmi podozrivý operátor rozbiť. Zabudol si sem ešte niečo napísať?

Fragment N12: Kontrola ukazovateľa po dereferencovaní

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");
  ....
}

Upozornenie PVS-Studio: V595 [CWE-476] Ukazovateľ 'Callee' bol použitý pred overením voči nullptr. Kontrolné riadky: 172, 174. AMDGPUInline.cpp 172

Index Callee na začiatku je dereferencovaná v čase volania funkcie getTTI.

A potom sa ukáže, že tento ukazovateľ by sa mal skontrolovať z hľadiska rovnosti nullptr:

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

Ale už je neskoro…

Fragment N13 - N...: Kontrola ukazovateľa po dereferencovaní

Situácia diskutovaná v predchádzajúcom fragmente kódu nie je jedinečná. Objavuje sa tu:

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()) {               // <=
  ....
}

Upozornenie PVS-Studio: V595 [CWE-476] Ukazovateľ 'CalleeFn' bol použitý predtým, ako bol overený voči nullptr. Kontrolné riadky: 1079, 1081. SimplifyLibCalls.cpp 1079

A tu:

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());     // <=
  ....
}

Upozornenie PVS-Studio: V595 [CWE-476] Ukazovateľ 'ND' bol použitý predtým, ako bol overený voči nullptr. Kontrolné riadky: 532, 534. SemaTemplateInstantiateDecl.cpp 532

A tu:

  • V595 [CWE-476] Ukazovateľ 'U' bol použitý pred overením voči nullptr. Kontrolné riadky: 404, 407. DWARFormValue.cpp 404
  • V595 [CWE-476] Ukazovateľ 'ND' bol použitý pred overením voči nullptr. Kontrolné riadky: 2149, 2151. SemaTemplateInstantiate.cpp 2149

A potom ma prestalo zaujímať študovať varovania s číslom V595. Neviem teda, či existuje viac podobných chýb okrem tých, ktoré sú tu uvedené. S najväčšou pravdepodobnosťou existuje.

Fragment N17, N18: Podozrivý posun

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

Upozornenie PVS-Studio: V629 [CWE-190] Zvážte kontrolu výrazu '~(Veľkosť - 1) << 1'. Bitový posun 32-bitovej hodnoty s následným rozšírením na 64-bitový typ. AArch64AddressingModes.h 260

Nemusí to byť chyba a kód funguje presne tak, ako má. Ale toto je zjavne veľmi podozrivé miesto a treba ho skontrolovať.

Povedzme premennú Veľkosť sa rovná 16 a potom autor kódu plánoval dostať ho do premennej NImms hodnota:

1111111111111111111111111111111111111111111111111111111111100000

V skutočnosti však bude výsledok:

0000000000000000000000000000000011111111111111111111111111100000

Faktom je, že všetky výpočty prebiehajú pomocou 32-bitového typu bez znamienka. A až potom bude tento 32-bitový nepodpísaný typ implicitne rozšírený na uint64_t. V tomto prípade budú najvýznamnejšie bity nula.

Situáciu môžete vyriešiť takto:

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

Podobná situácia: V629 [CWE-190] Zvážte kontrolu výrazu 'Immr << 6'. Bitový posun 32-bitovej hodnoty s následným rozšírením na 64-bitový typ. AArch64AddressingModes.h 269

Fragment N19: Chýba kľúčové slovo inak?

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");
  }
  ....
}

Upozornenie PVS-Studio: V646 [CWE-670] Zvážte kontrolu logiky aplikácie. Je možné, že kľúčové slovo „ostatné“ chýba. AMDGPUAsmParser.cpp 5655

Tu nie je chyba. Od vtedajšieho bloku prvého if končí s pokračovať, potom je to jedno, je tam kľúčové slovo inak alebo nie. V každom prípade bude kód fungovať rovnako. Stále chýba inak robí kód nejasnejším a nebezpečnejším. Ak v budúcnosti pokračovať zmizne, kód začne fungovať úplne inak. Podľa mňa je lepšie pridať inak.

Fragment N20: Štyri preklepy rovnakého typu

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;
}

Upozornenia PVS-Studio:

  • V655 [CWE-480] Reťazce boli zreťazené, ale nepoužívajú sa. Zvážte kontrolu výrazu 'Result + Name.str()'. Symbol.cpp 32
  • V655 [CWE-480] Reťazce boli zreťazené, ale nepoužívajú sa. Zvážte kontrolu výrazu 'Result + "(ObjC Class)" + Name.str()'. Symbol.cpp 35
  • V655 [CWE-480] Reťazce boli zreťazené, ale nepoužívajú sa. Zvážte kontrolu výrazu 'Result + "(ObjC Class EH) " + Name.str()'. Symbol.cpp 38
  • V655 [CWE-480] Reťazce boli zreťazené, ale nepoužívajú sa. Zvážte kontrolu výrazu 'Result + "(ObjC IVar)" + Name.str()'. Symbol.cpp 41

Náhodou sa namiesto operátora += používa operátor +. Výsledkom sú návrhy, ktoré nemajú zmysel.

Fragment N21: Nedefinované správanie

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();
    }
  }
}

Pokúste sa nájsť nebezpečný kód sami. A toto je obrázok na odpútanie pozornosti, aby ste sa hneď nepozreli na odpoveď:

Hľadanie chýb v LLVM 8 pomocou analyzátora PVS-Studio

Upozornenie PVS-Studio: V708 [CWE-758] Používa sa nebezpečná konštrukcia: 'FeaturesMap[Op] = FeaturesMap.size()', kde 'FeaturesMap' je triedy 'map'. To môže viesť k nedefinovanému správaniu. RISCVCompressInstEmitter.cpp 490

Problémový riadok:

FeaturesMap[Op] = FeaturesMap.size();

Ak prvok Op sa nenájde, potom sa v mape vytvorí nový prvok a zapíše sa tam počet prvkov v tejto mape. Nie je známe, či sa funkcia zavolá veľkosť pred alebo po pridaní nového prvku.

Fragment N22-N24: Opakované úlohy

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;
  }
  ....
}

Upozornenie PVS-Studio: V519 [CWE-563] Premennej „NType“ sú priradené hodnoty dvakrát za sebou. Možno je to chyba. Kontrolné riadky: 1663, 1664. MachOObjectFile.cpp 1664

Nemyslím si, že je tu skutočná chyba. Len zbytočné opakované zadanie. Ale stále je to omyl.

Podobne:

  • V519 [CWE-563] Premennej 'B.NDesc' sú priradené hodnoty dvakrát za sebou. Možno je to chyba. Kontrolné riadky: 1488, 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] Premennej sú priradené hodnoty dvakrát za sebou. Možno je to chyba. Kontrolné riadky: 59, 61. coff2yaml.cpp 61

Fragment N25-N27: Viac preradení

Teraz sa pozrime na trochu inú verziu preradenia.

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;
  ....
}

Upozornenie PVS-Studio: V519 [CWE-563] Premennej 'Alignment' sú priradené hodnoty dvakrát za sebou. Možno je to chyba. Kontrolné riadky: 1158, 1160. LoadStoreVectorizer.cpp 1160

Toto je veľmi zvláštny kód, ktorý zjavne obsahuje logickú chybu. Na začiatku variabilné zarovnanie hodnota je priradená v závislosti od stavu. A potom dôjde k priradeniu znova, ale teraz bez akejkoľvek kontroly.

Podobné situácie je možné vidieť tu:

  • V519 [CWE-563] Premennej „Efekty“ sú priradené hodnoty dvakrát za sebou. Možno je to chyba. Kontrolné riadky: 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] Premennej 'ExpectNoDerefChunk' sú priradené hodnoty dvakrát za sebou. Možno je to chyba. Kontrolné riadky: 4970, 4973. SemaType.cpp 4973

Fragment N28: Vždy pravdivý stav

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;
  }
  ....
}

Upozornenie PVS-Studio: V547 [CWE-571] Výraz 'nextByte != 0x90' je vždy pravdivý. X86DisassemblerDecoder.cpp 379

Kontrola nemá zmysel. Variabilné nextByte vždy sa nerovná hodnote 0x90, čo vyplýva z predchádzajúcej kontroly. Ide o nejaký druh logickej chyby.

Fragment N29 - N...: Vždy pravdivé/nepravdivé podmienky

Analyzátor vydáva veľa varovaní, že celý stav (V547) alebo jej časť (V560) je vždy pravda alebo nepravda. Často to nie sú skutočné chyby, ale jednoducho odfláknutý kód, výsledok rozšírenia makier a podobne. Dáva však zmysel pozrieť sa na všetky tieto upozornenia, pretože z času na čas sa vyskytnú skutočné logické chyby. Napríklad táto časť kódu je podozrivá:

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;
  ....
}

Upozornenie PVS-Studio: V560 [CWE-570] Časť podmieneného výrazu je vždy nepravdivá: RegNo == 0xe. ARMDisassembler.cpp 939

Konštanta 0xE je hodnota 14 v desiatkovej sústave. Vyšetrenie RegNo == 0xe nedáva zmysel, pretože ak RegNo > 13, potom funkcia dokončí svoju realizáciu.

S ID V547 a V560 bolo veľa ďalších upozornení, ale ako s V595, nemal som záujem študovať tieto upozornenia. Už bolo jasné, že materiálu na napísanie článku mám dosť :). Preto nie je známe, koľko chýb tohto typu je možné identifikovať v LLVM pomocou PVS-Studio.

Dám vám príklad, prečo je štúdium týchto spúšťačov nudné. Analyzátor má úplnú pravdu, keď vydáva varovanie pre nasledujúci kód. Ale to nie je chyba.

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

Upozornenie PVS-Studio: V547 [CWE-570] Výraz '!HasError' je vždy nepravdivý. UnwrappedLineParser.cpp 1635

Fragment N30: ​​​​Podozrivý návrat

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();
  }
  ....
}

Upozornenie PVS-Studio: V612 [CWE-670] Bezpodmienečný „návrat“ v rámci cyklu. R600OptimizeVectorRegisters.cpp 63

Ide buď o chybu, alebo o špecifickú techniku, ktorá má niečo vysvetliť programátorom čítajúcim kód. Tento dizajn mi nič nevysvetľuje a vyzerá veľmi podozrivo. Tak radšej nepísať :).

Unavený? Potom je čas na prípravu čaju alebo kávy.

Hľadanie chýb v LLVM 8 pomocou analyzátora PVS-Studio

Poruchy identifikované novou diagnostikou

Myslím, že 30 aktivácií starej diagnostiky je dosť. Poďme sa teraz pozrieť, čo zaujímavé sa dá nájsť s novou diagnostikou, ktorá sa objavila v analyzátore po predchádzajúca kontroly. Počas tejto doby bolo do analyzátora C++ pridaných celkom 66 univerzálnych diagnostik.

Fragment N31: Nedosiahnuteľný kód

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();
}

Upozornenie PVS-Studio: V779 [CWE-561] Zistil sa nedostupný kód. Je možné, že sa vyskytla chyba. ExecutionUtils.cpp 146

Ako vidíte, obe pobočky operátora if končí hovorom operátorovi návrat. V súlade s tým kontajner CtorDtorsByPriority nebude nikdy vyčistená.

Fragment N32: Nedosiahnuteľný kód

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;
}

Upozornenie PVS-Studio: V779 [CWE-561] Zistil sa nedostupný kód. Je možné, že sa vyskytla chyba. LLParser.cpp 835

Zaujímavá situácia. Najprv sa pozrime na toto miesto:

return ParseTypeIdEntry(SummaryID);
break;

Na prvý pohľad sa zdá, že tu nie je žiadna chyba. Vyzerá to ako operátor rozbiť je tu jeden navyše a môžete ho jednoducho odstrániť. Nie všetko je však také jednoduché.

Analyzátor vydá varovanie na riadkoch:

Lex.setIgnoreColonInIdentifiers(false);
return false;

A skutočne, tento kód je nedosiahnuteľný. Všetky prípady v prepínač končí hovorom od operátora návrat. A teraz nezmysel sám rozbiť nevyzerá tak neškodne! Možno by mala jedna z vetiev skončiť s rozbiť, nie na návrat?

Fragment N33: Náhodný reset vysokých bitov

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);
  ....
}

Upozornenie PVS-Studio: V784 Veľkosť bitovej masky je menšia ako veľkosť prvého operandu. To spôsobí stratu vyšších bitov. RuntimeDyld.cpp 815

Upozorňujeme, že funkcia getStubAlignment vráti typ nepodpísaný. Vypočítajme hodnotu výrazu za predpokladu, že funkcia vráti hodnotu 8:

~(getStubAlignment() - 1)

~(8u-1)

0xFFFFFFFF8u

Teraz si všimnite, že premenná Veľkosť údajov má 64-bitový typ bez znamienka. Ukazuje sa, že pri vykonávaní operácie DataSize & 0xFFFFFFF8u sa všetkých tridsaťdva bitov vyššieho rádu vynuluje. S najväčšou pravdepodobnosťou to nie je to, čo programátor chcel. Mám podozrenie, že chcel vypočítať: DataSize & 0xFFFFFFFFFFFFFF8u.

Ak chcete opraviť chybu, mali by ste napísať toto:

DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);

Alebo takto:

DataSize &= ~(getStubAlignment() - 1ULL);

Fragment N34: Pretypovanie explicitného typu zlyhalo

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);
  ....
}

Upozornenie PVS-Studio: V1028 [CWE-190] Možné pretečenie. Zvážte pretypovanie operandov operátora 'NumElts * Scale' na typ 'size_t', nie výsledok. X86ISelLowering.h 1577

Explicitné pretypovanie sa používa na zabránenie pretečeniu pri násobení typových premenných int. Explicitné typové obsadenie tu však nechráni pred pretečením. Najprv sa premenné vynásobia a až potom sa 32-bitový výsledok násobenia rozšíri na typ veľkosť_t.

Fragment N35: Kopírovanie a prilepenie zlyhalo

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 [CWE-682] Našli sa dva podobné fragmenty kódu. Možno ide o preklep a namiesto premennej „Op1“ by sa mala použiť premenná „Op0“. InstCombineCompares.cpp 5507

Táto nová zaujímavá diagnostika identifikuje situácie, kedy bol skopírovaný kus kódu a niektoré názvy v ňom sa začali meniť, no na jednom mieste ho neopravili.

Upozorňujeme, že v druhom bloku sa zmenili Op0 na Op1. Ale na jednom mieste to neopravili. S najväčšou pravdepodobnosťou to malo byť napísané takto:

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

Fragment N36: Variabilná zámena

struct Status {
  unsigned Mask;
  unsigned Mode;

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

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

Upozornenie PVS-Studio: V1001 [CWE-563] Premenná 'Mode' je priradená, ale na konci funkcie sa nepoužíva. SIModeRegister.cpp 48

Je veľmi nebezpečné dávať argumentom funkcií rovnaké mená ako členom triedy. Je veľmi ľahké sa zmiasť. Práve takýto prípad máme pred sebou. Tento výraz nedáva zmysel:

Mode &= Mask;

Argument funkcie sa zmení. To je všetko. Tento argument sa už nepoužíva. S najväčšou pravdepodobnosťou si to mal napísať takto:

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

Fragment N37: Variabilná zámena

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;
}

Upozornenie PVS-Studio: V1001 [CWE-563] Premenná 'Size' je priradená, ale do konca funkcie sa nepoužíva. Object.cpp 424

Situácia je podobná predchádzajúcej. Malo by byť napísané:

this->Size += this->EntrySize;

Fragment N38-N47: Zabudli skontrolovať index

Predtým sme sa pozreli na príklady diagnostického spúšťania V595. Jeho podstatou je, že ukazovateľ je na začiatku dereferencovaný a až potom kontrolovaný. Mladá diagnostika V1004 je významovo opačný, no zároveň odhaľuje množstvo chýb. Identifikuje situácie, kedy bol ukazovateľ skontrolovaný na začiatku a potom sa zabudlo urobiť. Pozrime sa na takéto prípady nájdené vo vnútri LLVM.

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());  // <=
  ....
}

Varovanie PVS-Studio: V1004 [CWE-476] Ukazovateľ 'Ptr' bol použitý nebezpečne po tom, čo bol overený voči nullptr. Kontrolné riadky: 729, 738. TargetTransformInfoImpl.h 738

Variabilné Ptr môžu byť rovnaké nullptr, o čom svedčí šek:

if (Ptr != nullptr)

Pod týmto ukazovateľom je však bez predbežnej kontroly dereferencovaný:

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

Pozrime sa na ďalší podobný prípad.

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(); // <=
  ....
}

Varovanie PVS-Studio: V1004 [CWE-476] Ukazovateľ 'FD' bol použitý nebezpečne potom, čo bol overený proti nullptr. Kontrolné riadky: 3228, 3231. CGDebugInfo.cpp 3231

Venujte pozornosť znameniu FD. Som si istý, že problém je jasne viditeľný a nevyžaduje sa žiadne špeciálne vysvetlenie.

A ďalej:

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());     // <=
  ....
}

Varovanie PVS-Studio: V1004 [CWE-476] Ukazovateľ 'PtrTy' bol po overení proti nullptr použitý nebezpečne. Kontrolné riadky: 960, 965. InterleavedLoadCombinePass.cpp 965

Ako sa chrániť pred takýmito chybami? Buďte pozornejší pri Code-Review a na pravidelnú kontrolu kódu používajte statický analyzátor PVS-Studio.

Nemá zmysel uvádzať ďalšie fragmenty kódu s chybami tohto typu. V článku ponechám iba zoznam upozornení:

  • V1004 [CWE-476] Ukazovateľ 'Expr' sa po overení proti nullptr používal nebezpečne. Kontrolné riadky: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] Ukazovateľ 'PI' sa po overení proti nullptr používal nebezpečne. Kontrolné riadky: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] Ukazovateľ 'StatepointCall' bol po overení proti nullptr použitý nebezpečne. Kontrolné riadky: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] Ukazovateľ 'RV' bol použitý nebezpečne po tom, čo bol overený voči nullptr. Kontrolné riadky: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] Ukazovateľ 'CalleeFn' sa po overení proti nullptr používal nebezpečne. Kontrolné riadky: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] Ukazovateľ 'TC' bol použitý nebezpečne po tom, čo bol overený voči nullptr. Kontrolné riadky: 1819, 1824. Driver.cpp 1824

Fragment N48-N60: Nie je kritický, ale chybný (možný únik pamäte)

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

Upozornenie PVS-Studio: V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Strategies' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-isel-fuzzer.cpp 58

Ak chcete pridať prvok na koniec kontajnera, ako je std::vektor > nevieš len tak písať xxx.push_back(nové X), keďže neexistuje žiadna implicitná konverzia z X* в std::unique_ptr.

Bežným riešením je písanie xxx.emplace_back(nové X)keďže zostavuje: metóda emplace_back vytvára prvok priamo z argumentov, a preto môže používať explicitné konštruktory.

Nie je to bezpečné. Ak je vektor plný, pamäť sa znova pridelí. Operácia prerozdelenia pamäte môže zlyhať, čo môže mať za následok vyvolanie výnimky std::bad_alloc. V tomto prípade sa ukazovateľ stratí a vytvorený objekt sa nikdy nevymaže.

Bezpečným riešením je vytvárať unique_ptrktorý bude vlastniť ukazovateľ predtým, ako sa vektor pokúsi prerozdeliť pamäť:

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

Od C++ 14 môžete použiť 'std::make_unique':

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

Tento typ defektu nie je pre LLVM kritický. Ak nie je možné prideliť pamäť, kompilátor sa jednoducho zastaví. Avšak pre aplikácie s dlhou uptime, ktorý sa nemôže len tak ukončiť, ak zlyhá alokácia pamäte, môže to byť naozaj nepríjemná chyba.

Takže aj keď tento kód nepredstavuje praktickú hrozbu pre LLVM, považoval som za užitočné hovoriť o tomto chybovom vzore ao tom, že analyzátor PVS-Studio sa ho naučil identifikovať.

Ďalšie upozornenia tohto typu:

  • V1023 [CWE-460] Ukazovateľ bez vlastníka je pridaný do kontajnera 'Passes' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. PassManager.h 546
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'AAs' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. AliasAnalysis.h 324
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Položky' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'AllEdges' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. CFGMST.h 268
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'VMaps' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Records' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. FDRLogBuilder.h 30
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'PendingSubmodules' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. ModuleMap.cpp 810
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Objects' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. DebugMap.cpp 88
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Strategies' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 685
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 686
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 688
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 689
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 690
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 691
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 692
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 693
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Modifiers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. llvm-stress.cpp 694
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Operandy' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Stash' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Ukazovateľ bez vlastníka sa pridá do kontajnera 'Matchers' metódou 'emplace_back'. V prípade výnimky dôjde k úniku pamäte. GlobalISelEmitter.cpp 2702

Záver

Celkovo som vydal 60 upozornení a potom som prestal. Existujú ďalšie chyby, ktoré analyzátor PVS-Studio deteguje v LLVM? Áno, mám. Keď som však vypisoval fragmenty kódu do článku, bol už neskorý večer, či skôr noc, a rozhodol som sa, že je čas nazvať to dňom.

Dúfam, že vás zaujal a budete chcieť vyskúšať analyzátor PVS-Studio.

Môžete si stiahnuť analyzátor a získať kľúč od mínolovky na na tejto stránke.

A čo je najdôležitejšie, pravidelne používajte statickú analýzu. Jednorazové kontroly, ktoré realizujeme za účelom popularizácie metodiky statickej analýzy a PVS-Studio nie sú bežným scenárom.

Veľa šťastia pri zlepšovaní kvality a spoľahlivosti vášho kódu!

Hľadanie chýb v LLVM 8 pomocou analyzátora PVS-Studio

Ak chcete zdieľať tento článok s anglicky hovoriacim publikom, použite odkaz na preklad: Andrey Karpov. Hľadanie chýb v LLVM 8 s PVS-Studio.

Zdroj: hab.com

Pridať komentár