Vigade leidmine LLVM 8-st, kasutades PVS-Studio analüsaatorit

Vigade leidmine LLVM 8-st, kasutades PVS-Studio analüsaatorit
LLVM projekti viimasest koodikontrollist meie PVS-Studio analüsaatori abil on möödas üle kahe aasta. Veenduge, et PVS-Studio analüsaator on endiselt juhtiv tööriist vigade ja potentsiaalsete haavatavuste tuvastamiseks. Selleks kontrollime ja leiame uued vead LLVM 8.0.0 versioonis.

Artikkel tuleb kirjutada

Ausalt öeldes ei tahtnud ma seda artiklit kirjutada. Pole huvitav kirjutada projektist, mida oleme juba mitu korda kontrollinud (1, 2, 3). Parem on kirjutada millestki uuest, aga mul pole muud valikut.

Iga kord, kui LLVM-i uus versioon välja antakse või värskendatakse Clangi staatiline analüsaator, saame meiliga järgmist tüüpi küsimusi:

Vaata, Clang Static Analyzeri uus versioon on õppinud uusi vigu leidma! Mulle tundub, et PVS-Studio kasutamise asjakohasus väheneb. Clang leiab varasemast rohkem vigu ja jõuab järele PVS-Studio võimalustele. Mida te sellest arvate?

Sellele tahan alati vastata midagi sellist:

Me ei istu ka tegevusetult! Oleme oluliselt täiustanud PVS-Studio analüsaatori võimalusi. Nii et ärge muretsege, me juhime jätkuvalt nagu varem.

Kahjuks on see halb vastus. Selles pole tõendeid. Ja sellepärast ma seda artiklit praegu kirjutan. Niisiis on LLVM-i projekt veel kord kontrollitud ja selles on leitud mitmesuguseid vigu. Nüüd demonstreerin neid, mis tundusid mulle huvitavad. Clang Static Analyzer ei leia neid vigu (või on seda tema abiga äärmiselt ebamugav teha). Aga me saame. Pealegi leidsin ja kirjutasin kõik need vead ühe õhtuga üles.

Kuid artikli kirjutamine võttis mitu nädalat. Ma lihtsalt ei suutnud seda kõike teksti panna :).

Muide, kui olete huvitatud sellest, milliseid tehnoloogiaid PVS-Studio analüsaatoris kasutatakse vigade ja võimalike haavatavuste tuvastamiseks, siis soovitan sellega tutvuda Märge.

Uus ja vana diagnostika

Nagu juba märgitud, kontrolliti umbes kaks aastat tagasi LLVM-i projekti uuesti ja leitud vead parandati. Nüüd tutvustatakse selles artiklis uut vigade kogumit. Miks leiti uusi vigu? Sellel on 3 põhjust:

  1. LLVM projekt areneb, muutes vana koodi ja lisades uut koodi. Loomulikult on muudetud ja kirjutatud koodis uusi vigu. See näitab selgelt, et staatilist analüüsi tuleks kasutada regulaarselt, mitte aeg-ajalt. Meie artiklid näitavad hästi PVS-Studio analüsaatori võimalusi, kuid sellel pole midagi pistmist koodikvaliteedi parandamise ja vigade parandamise kulude vähendamisega. Kasutage regulaarselt staatilise koodianalüsaatorit!
  2. Viimistleme ja täiustame olemasolevat diagnostikat. Seetõttu suudab analüsaator tuvastada vead, mida ta eelmiste skaneeringute käigus ei märganud.
  3. PVS-Stuudiosse on ilmunud uus diagnostika, mida 2 aastat tagasi ei eksisteerinud. Otsustasin need eraldi jaotises esile tõsta, et PVS-Stuudio arengut selgelt näidata.

Diagnostikaga tuvastatud defektid, mis olid olemas 2 aastat tagasi

Fragment N1: Kopeeri-kleebi

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-Stuudio hoiatus: V501 [CWE-570] '||' vasakul ja paremal on identsed alamväljendid 'Name.startswith("avx512.mask.permvar.")' operaator. AutoUpgrade.cpp 73

Kontrollitakse kaks korda, et nimi algaks alamstringiga "avx512.mask.permvar.". Teises kontrollis tahtsid nad ilmselgelt midagi muud kirjutada, kuid unustasid kopeeritud teksti parandada.

Fragment N2: kirjaviga

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

Hoiatus PVS-Studio: V501 '|' vasakul ja paremal on identsed alamväljendid 'CXNameRange_WantQualifier'. operaator. CIndex.cpp 7245

Trükivea tõttu kasutatakse sama nimega konstanti kaks korda CXNameRange_WantQualifier.

Fragment N3: Segadus operaatori eelisseisundiga

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

PVS-Stuudio hoiatus: V502 [CWE-783] Võib-olla töötab operaator „?:” oodatust erineval viisil. Operaatoril "?:" on madalam prioriteet kui operaatoril "==". PPCTargetTransformInfo.cpp 404

Minu meelest on see väga ilus viga. Jah, ma tean, et mul on ilu kohta kummalised ideed :).

Nüüd, vastavalt operaatori prioriteedid, hinnatakse avaldist järgmiselt:

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

Praktilisest seisukohast ei ole sellisel tingimusel mõtet, kuna seda saab taandada:

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

See on selge viga. Tõenäoliselt taheti võrrelda 0/1 muutujaga indeks. Koodi parandamiseks peate kolmeosalise operaatori ümber lisama sulud:

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

Muide, kolmekomponentne operaator on väga ohtlik ja kutsub esile loogikavigu. Olge sellega väga ettevaatlik ja ärge olge ahne sulgudega. Vaatasin seda teemat lähemalt siin, peatükis "Hoiduge ?: operaatorist ja lisage see sulgudesse."

Fragment N4, N5: nullkursor

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-Stuudio hoiatus: V522 [CWE-476] Nullkursori 'LHS' viitamise tühistamine võib toimuda. TGParser.cpp 2152

Kui osuti LHS on tühine, tuleks teha hoiatus. Selle asemel tühistatakse viide sellele samale nullkursorile: LHS->getAsString().

See on väga tüüpiline olukord, kui viga on veakäsitlejas peidetud, kuna keegi ei testi neid. Staatilised analüsaatorid kontrollivad kogu kättesaadavat koodi, olenemata sellest, kui sageli seda kasutatakse. See on väga hea näide sellest, kuidas staatiline analüüs täiendab teisi testimis- ja veakaitsetehnikaid.

Sarnane osuti käsitlemise viga RHS Lubatud just allolevas koodis: V522 [CWE-476] Võib toimuda nullkursori 'RHS' viitamise tühistamine. TGParser.cpp 2186

Fragment N6: kursori kasutamine pärast liigutamist

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 hoiatus: V522 [CWE-476] Nullkursori 'ProgClone' viitamise tühistamine võib toimuda. Miscompilation.cpp 601

Alguses nutikas osuti ProgClone lakkab omamast objekti:

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

Tegelikult nüüd ProgClone on null osuti. Seetõttu peaks nullkursori viide toimuma allpool:

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

Kuid tegelikkuses seda ei juhtu! Pange tähele, et silmust tegelikult ei käivitata.

Konteineri alguses Valesti koostatud funktsioonid kustutatud:

MiscompiledFunctions.clear();

Järgmisena kasutatakse selle konteineri suurust silmuse tingimusel:

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

On lihtne näha, et silmus ei käivitu. Ma arvan, et see on ka viga ja kood tuleks kirjutada teisiti.

Näib, et oleme kohanud seda kuulsat vigade pariteeti! Üks viga varjab teist :).

Fragment N7: kursori kasutamine pärast liigutamist

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 hoiatus: V522 [CWE-476] Võib toimuda nullkursori 'Test' viitamise tühistamine. Miscompilation.cpp 709

Jälle sama olukord. Alguses liigutatakse objekti sisu ja seejärel kasutatakse seda nii, nagu poleks midagi juhtunud. Näen seda olukorda järjest sagedamini programmikoodis pärast seda, kui C++-s ilmus liikumissemantika. See on põhjus, miks ma armastan C++ keelt! Üha rohkem on uusi viise, kuidas endal jalg maha tulistada. PVS-Studio analüsaatoril on alati tööd :).

Fragment N8: nullkursor

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 hoiatus: V522 [CWE-476] Võib toimuda nullkursori 'Tüüp' viitamise tühistamine. PrettyFunctionDumper.cpp 233

Lisaks veakäsitlejatele ei testita tavaliselt ka silumisprintimise funktsioone. Meil on just selline juhtum ees. Funktsioon ootab kasutajat, kes oma probleemide lahendamise asemel on sunnitud selle parandama.

Õigesti:

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

Fragment N9: nullkursor

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 hoiatus: V522 [CWE-476] Nullkursori 'Ty' viitamise tühistamine võib toimuda. OtsitavTableEmitter.cpp 614

Ma arvan, et kõik on selge ega vaja selgitusi.

Fragment N10: kirjaviga

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-Stuudio hoiatus: V570 Muutuja 'Identifier->Type' on määratud iseendale. FormatTokenLexer.cpp 249

Muutujat pole mõtet endale omistada. Tõenäoliselt tahtsid nad kirjutada:

Identifier->Type = Question->Type;

Fragment N11: kahtlane katkestus

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-Stuudio hoiatus: V622 [CWE-478] Kaaluge "lüliti" avalduse kontrollimist. Võimalik, et esimene juhtum on puudu. SystemZAsmParser.cpp 652

Alguses on väga kahtlane operaator murdma. Kas unustasite siia veel midagi kirjutada?

Fragment N12: kursori kontrollimine pärast viitamise tühistamist

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-Stuudio hoiatus: V595 [CWE-476] 'Callee' osutit kasutati enne nullptr-i kontrollimist. Kontrollread: 172, 174. AMDGPUInline.cpp 172

Pointer Callee alguses eemaldatakse funktsiooni kutsumise ajal viide saadaTTI.

Ja siis selgub, et selle osuti võrdsust tuleks kontrollida nullptr:

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

Aga on juba hilja…

Fragment N13 - N...: kursori kontrollimine pärast viitamise tühistamist

Eelmises koodifragmendis käsitletud olukord ei ole ainulaadne. See ilmub siin:

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 hoiatus: V595 [CWE-476] 'CalleeFn' osutit kasutati enne, kui see kontrolliti nullptr suhtes. Kontrollread: 1079, 1081. SimplifyLibCalls.cpp 1079

Ja siin:

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 hoiatus: V595 [CWE-476] "ND" osutit kasutati enne, kui see kontrolliti nullptr-i suhtes. Kontrollread: 532, 534. SemaTemplateInstantiateDecl.cpp 532

Ja siin:

  • V595 [CWE-476] U-osutit kasutati enne nullptr-i kontrollimist. Kontrollread: 404, 407. DWARFormValue.cpp 404
  • V595 [CWE-476] 'ND' osutit kasutati enne nullptr-i kontrollimist. Kontrollread: 2149, 2151. SemaTemplateInstantiate.cpp 2149

Ja siis ei huvitanud mind V595 numbriga hoiatuste uurimine. Nii et ma ei tea, kas peale siinloetletute on veel sarnaseid vigu. Suure tõenäosusega on.

Fragment N17, N18: kahtlane nihe

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

PVS-Stuudio hoiatus: V629 [CWE-190] Kaaluge avaldise '~(Suurus - 1) << 1' kontrollimist. 32-bitise väärtuse bitivahetus koos järgneva laiendamisega 64-bitisele tüübile. AArch64AddressingModes.h 260

See ei pruugi olla viga ja kood töötab täpselt nii, nagu ette nähtud. Aga see on selgelt väga kahtlane koht ja seda tuleb kontrollida.

Ütleme, et muutuja SUURUS on võrdne 16-ga ja siis plaanis koodi autor saada selle muutujana NImms väärtus:

1111111111111111111111111111111111111111111111111111111111100000

Kuid tegelikkuses on tulemus:

0000000000000000000000000000000011111111111111111111111111100000

Fakt on see, et kõik arvutused tehakse 32-bitise märgita tüübi abil. Ja alles siis laiendatakse seda 32-bitist allkirjastamata tüüpi kaudselt uint64_t. Sel juhul on kõige olulisemad bitid null.

Saate olukorra parandada järgmiselt:

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

Sarnane olukord: V629 [CWE-190] Kaaluge avaldise 'Immr << 6' kontrollimist. 32-bitise väärtuse bitivahetus koos järgneva laiendamisega 64-bitisele tüübile. AArch64AddressingModes.h 269

Fragment N19: puudub märksõna teine?

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-Stuudio hoiatus: V646 [CWE-670] Kaaluge rakenduse loogika kontrollimist. Võimalik, et märksõna „muu” on puudu. AMDGPUAsmParser.cpp 5655

Siin pole viga. Alates esimese plokist if lõpeb jätkama, siis pole vahet, on märksõna teine või mitte. Mõlemal juhul töötab kood samamoodi. Ikka jäi vahele teine muudab koodi ebaselgemaks ja ohtlikumaks. Kui tulevikus jätkama kaob, hakkab kood hoopis teistmoodi tööle. Minu arvates on parem lisada teine.

Fragment N20: neli sama tüüpi kirjaviga

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-Stuudio hoiatused:

  • V655 [CWE-480] Stringid olid ühendatud, kuid neid ei kasutata. Kaaluge avaldise 'Result + Name.str()' kontrollimist. Sümbol.cpp 32
  • V655 [CWE-480] Stringid olid ühendatud, kuid neid ei kasutata. Kaaluge avaldise 'Result + "(ObjC Class)" + Name.str()' kontrollimist. Sümbol.cpp 35
  • V655 [CWE-480] Stringid olid ühendatud, kuid neid ei kasutata. Kaaluge avaldise 'Result + "(ObjC Class EH)" + Name.str()' kontrollimist. Sümbol.cpp 38
  • V655 [CWE-480] Stringid olid ühendatud, kuid neid ei kasutata. Kaaluge avaldise 'Result + "(ObjC Ivar)" + Name.str()' kontrollimist. Sümbol.cpp 41

Juhuslikult kasutatakse += operaatori asemel operaatorit +. Tulemuseks on kujundused, millel puudub tähendus.

Fragment N21: määratlemata käitumine

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

Proovige ohtlik kood ise üles leida. Ja see on pilt tähelepanu hajutamiseks, et mitte kohe vastust vaadata:

Vigade leidmine LLVM 8-st, kasutades PVS-Studio analüsaatorit

PVS-Stuudio hoiatus: V708 [CWE-758] Kasutatakse ohtlikku konstruktsiooni: 'FeaturesMap[Op] = FeaturesMap.size()', kus 'FeaturesMap' kuulub 'map' klassi. See võib viia ebamäärase käitumiseni. RISCVCompressInstEmitter.cpp 490

Probleemi rida:

FeaturesMap[Op] = FeaturesMap.size();

Kui element Op ei leita, siis luuakse kaardil uus element ja sinna kirjutatakse selle kaardi elementide arv. Pole teada, kas funktsiooni kutsutakse suurus enne või pärast uue elemendi lisamist.

Fragment N22-N24: Korduvad ülesanded

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-Stuudio hoiatus: V519 [CWE-563] Muutujale NType omistatakse väärtused kaks korda järjest. Võib-olla on see viga. Kontrollread: 1663, 1664. MachOObjectFile.cpp 1664

Ma arvan, et siin pole tõsist viga. Lihtsalt tarbetu korduv ülesanne. Aga eksitus ikkagi.

Samamoodi:

  • V519 [CWE-563] Muutujale 'B.NDesc' omistatakse väärtused kaks korda järjest. Võib-olla on see viga. Kontrollread: 1488, 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] Muutujale omistatakse väärtused kaks korda järjest. Võib-olla on see viga. Kontrollread: 59, 61. coff2yaml.cpp 61

Fragment N25-N27: rohkem ümbermääramisi

Vaatame nüüd ümbermääramise veidi teistsugust versiooni.

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 hoiatus: V519 [CWE-563] Muutujale 'Joondamine' määratakse väärtused kaks korda järjest. Võib-olla on see viga. Kontrollread: 1158, 1160. LoadStoreVectorizer.cpp 1160

See on väga kummaline kood, mis ilmselt sisaldab loogikaviga. Alguses muutuv Joondumine olenevalt olukorrast määratakse väärtus. Ja siis toimub määramine uuesti, kuid nüüd ilma igasuguse kontrollita.

Sarnaseid olukordi saab näha siit:

  • V519 [CWE-563] Muutujale „Efektid” omistatakse väärtused kaks korda järjest. Võib-olla on see viga. Kontrollread: 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] Muutujale 'ExpectNoDerefChunk' määratakse väärtused kaks korda järjest. Võib-olla on see viga. Kontrollread: 4970, 4973. SemaType.cpp 4973

Fragment N28: alati korralik

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-Stuudio hoiatus: V547 [CWE-571] Avaldis 'nextByte != 0x90' on alati tõene. X86DisassemblerDecoder.cpp 379

Kontrollimisel pole mõtet. Muutuv nextByte ei ole alati võrdne väärtusega 0x90, mis tuleneb eelmisest kontrollist. See on mingi loogikaviga.

Fragment N29 - N...: alati tõesed/valed tingimused

Analüsaator annab palju hoiatusi, et kogu seisund (V547) või osa sellest (V560) on alati tõene või vale. Tihti pole need päris vead, vaid lihtsalt lohakas kood, makro laiendamise tulemus jms. Siiski on mõistlik kõiki neid hoiatusi vaadata, kuna aeg-ajalt tuleb ette tõelisi loogikavigu. Näiteks see koodiosa on kahtlane:

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-Stuudio hoiatus: V560 [CWE-570] Osa tingimusavaldisest on alati väär: RegNo == 0xe. ARMDisassembler.cpp 939

Konstant 0xE on kümnendkoha väärtus 14. Läbivaatus RegNo == 0xe pole mõtet, sest kui RegNo > 13, siis funktsioon lõpetab täitmise.

ID-dega V547 ja V560 oli palju muid hoiatusi, kuid nagu ka V595, ma ei olnud huvitatud nende hoiatuste uurimisest. Juba oli selge, et mul on artikli kirjutamiseks piisavalt materjali :). Seetõttu pole teada, kui palju seda tüüpi vigu saab PVS-Studio abil LLVM-is tuvastada.

Toon teile näite, miks nende päästikute uurimine on igav. Analüsaatoril on järgmise koodi eest hoiatuse andmisel täiesti õigus. Kuid see pole viga.

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

PVS-Studio hoiatus: V547 [CWE-570] Avaldis '!HasError' on alati vale. UnwrappedLineParser.cpp 1635

Fragment N30: ​​kahtlane tagasitulek

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-Stuudio hoiatus: V612 [CWE-670] Tingimusteta "tagasitulek" tsükli sees. R600OptimizeVectorRegisters.cpp 63

See on kas viga või spetsiifiline tehnika, mis on mõeldud koodi lugevatele programmeerijatele midagi selgitama. See disain ei selgita mulle midagi ja tundub väga kahtlane. Parem mitte nii kirjutada :).

Väsinud? Siis on aeg teed või kohvi keeta.

Vigade leidmine LLVM 8-st, kasutades PVS-Studio analüsaatorit

Uue diagnostikaga tuvastatud defektid

Arvan, et 30 vana diagnostika aktiveerimist on piisav. Vaatame nüüd, mida huvitavat pärast analüsaatorisse ilmunud uue diagnostikaga leida võib eelmine kontrollid. Selle aja jooksul lisati C++ analüsaatorisse kokku 66 üldotstarbelist diagnostikat.

Fragment N31: kättesaamatu kood

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-Stuudio hoiatus: V779 [CWE-561] Tuvastati kättesaamatu kood. Võimalik, et tegemist on veaga. ExecutionUtils.cpp 146

Nagu näete, operaatori mõlemad harud if lõpeb kõnega operaatorile tagasipöördumine. Vastavalt sellele konteiner CtorDtorsByPriority ei puhastata kunagi.

Fragment N32: kättesaamatu kood

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 hoiatus: V779 [CWE-561] Tuvastati kättesaamatu kood. Võimalik, et tegemist on veaga. LLParser.cpp 835

Huvitav olukord. Vaatame kõigepealt seda kohta:

return ParseTypeIdEntry(SummaryID);
break;

Esmapilgul tundub, et viga siin pole. See näeb välja nagu operaator murdma siin on veel üks ja saate selle lihtsalt kustutada. Siiski pole kõik nii lihtne.

Analüsaator annab joontele hoiatuse:

Lex.setIgnoreColonInIdentifiers(false);
return false;

Ja tõepoolest, see kood on kättesaamatu. Kõik juhtumid sisse lüliti lõpeb operaatori kõnega tagasipöördumine. Ja nüüd üksi mõttetu murdma ei tundu nii kahjutu! Võib-olla peaks üks harudest lõppema murdma, mitte edasi tagasipöördumine?

Fragment N33: kõrgete bittide juhuslik lähtestamine

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-Stuudio hoiatus: V784 Bitimaski suurus on väiksem kui esimese operandi suurus. See põhjustab kõrgemate bittide kadumise. RuntimeDyld.cpp 815

Pange tähele, et funktsioon getStubAlignment tagastab tüübi allakirjutamata. Arvutame avaldise väärtuse, eeldades, et funktsioon tagastab väärtuse 8:

~(getStubAlignment() - 1)

~ (8u-1)

0xFFFFFFFF8u

Nüüd pange tähele, et muutuja DataSize on 64-bitine allkirjastamata tüüp. Selgub, et toimingu DataSize & 0xFFFFFFF8u sooritamisel nullitakse kõik kolmkümmend kaks kõrget järgu bitti. Tõenäoliselt ei tahtnud programmeerija seda. Kahtlustan, et ta tahtis arvutada: DataSize & 0xFFFFFFFFFFFFFFFF8u.

Vea parandamiseks peaksite kirjutama järgmise:

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

Või nii:

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

Fragment N34: selgesõnaline ülekandmine ebaõnnestus

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-Stuudio hoiatus: V1028 [CWE-190] Võimalik ülevool. Kaaluge operaatori 'NumElts * Scale' operandide ülekandmist tüübile 'size_t', mitte tulemusele. X86ISelLowering.h 1577

Selgesõnalist tüüpi valamist kasutatakse tüübimuutujate korrutamisel ülevoolu vältimiseks int. Selgesõnaline tüübivalamine ei kaitse aga ülevoolu eest. Esiteks korrutatakse muutujad ja alles seejärel laiendatakse korrutamise 32-bitine tulemus tüübiks suurus_t.

Fragment N35: Kopeerimine ja kleepimine ebaõnnestus

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] Leiti kaks sarnast koodifragmenti. Võib-olla on see kirjaviga ja muutuja "Op1" asemel tuleks kasutada muutujat "Op0". InstCombineCompares.cpp 5507

See uus huvitav diagnostika tuvastab olukorrad, kus koodijupp on kopeeritud ja mõnda nime selles on hakatud muutma, kuid ühes kohas pole seda parandatud.

Pange tähele, et teises plokis need muutusid Op0 edasi Op1. Kuid ühes kohas nad seda ei parandanud. Tõenäoliselt oleks see pidanud olema kirjutatud nii:

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

Fragment N36: muutuv segadus

struct Status {
  unsigned Mask;
  unsigned Mode;

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

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

PVS-Stuudio hoiatus: V1001 [CWE-563] Muutuja 'Mode' on määratud, kuid seda ei kasutata funktsiooni lõpuks. SIModeRegister.cpp 48

Väga ohtlik on anda funktsiooni argumentidele klassiliikmetega samad nimed. Väga lihtne on segadusse sattuda. Meil on just selline juhtum ees. Sellel väljendil pole mõtet:

Mode &= Mask;

Funktsiooni argument muutub. See on kõik. Seda argumenti enam ei kasutata. Tõenäoliselt oleksite pidanud selle kirjutama nii:

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

Fragment N37: muutuv segadus

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

Hoiatus PVS-Studio: V1001 [CWE-563] Muutuja Size on määratud, kuid seda funktsiooni lõpuks ei kasutata. Object.cpp 424

Olukord on sarnane eelmisega. See peaks olema kirjutatud:

this->Size += this->EntrySize;

Fragment N38-N47: nad unustasid indeksit kontrollida

Varem vaatlesime diagnostilise käivitamise näiteid V595. Selle olemus seisneb selles, et kursor eemaldatakse alguses ja alles seejärel kontrollitakse. Noor diagnostika V1004 on tähenduselt vastupidine, kuid paljastab ka palju vigu. See tuvastab olukorrad, kus kursorit kontrolliti alguses ja siis unustati see teha. Vaatame selliseid LLVM-i sees leitud juhtumeid.

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 hoiatus: V1004 [CWE-476] Ptr osutit kasutati ebaturvaliselt pärast seda, kui see kontrolliti nullptri suhtes. Kontrollread: 729, 738. TargetTransformInfoImpl.h 738

Muutuv Ptr võib olla võrdne nullptr, mida tõendab tšekk:

if (Ptr != nullptr)

Siiski on alloleval kursoril viide ilma eelneva kontrollita:

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

Vaatleme teist sarnast juhtumit.

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 hoiatus: V1004 [CWE-476] 'FD' osutit kasutati ebaturvaliselt pärast seda, kui see kontrolliti nullptr-i suhtes. Kontrollread: 3228, 3231. CGDebugInfo.cpp 3231

Pöörake tähelepanu märgile FD. Olen kindel, et probleem on selgelt nähtav ja erilist selgitust pole vaja.

Ja edasi:

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 hoiatus: V1004 [CWE-476] 'PtrTy' osutit kasutati pärast nullptr-i suhtes kontrollimist ebaturvaliselt. Kontrollread: 960, 965. InterleavedLoadCombinePass.cpp 965

Kuidas end selliste vigade eest kaitsta? Olge Code-Review'is tähelepanelikum ja kasutage oma koodi korrapäraseks kontrollimiseks PVS-Studio staatilist analüsaatorit.

Teistele seda tüüpi vigadega koodifragmentidele pole mõtet viidata. Jätan artiklisse ainult hoiatuste loendi:

  • V1004 [CWE-476] „Expr” osutit kasutati ebaturvaliselt pärast seda, kui see kontrolliti nullptr suhtes. Kontrollread: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] PI-osutit kasutati pärast nullptr-i suhtes kontrollimist ebaturvaliselt. Kontrollread: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] Osutit „StatepointCall” kasutati pärast nullptr-i suhtes kontrollimist ebaturvaliselt. Kontrollread: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] RV osutit kasutati ebaturvaliselt pärast seda, kui see kontrolliti nullptr-i suhtes. Kontrollread: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] 'CalleeFn' osutit kasutati pärast nullptr-i suhtes kontrollimist ebaturvaliselt. Kontrollread: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] TC osutit kasutati ebaturvaliselt pärast seda, kui see kontrolliti nullptr-i suhtes. Kontrollread: 1819, 1824. Driver.cpp 1824

Fragment N48-N60: pole kriitiline, kuid defekt (võimalik mäluleke)

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

PVS-Stuudio hoiatus: V1023 [CWE-460] Ilma omanikuta kursor lisatakse konteinerisse 'Strategies' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-isel-fuzzer.cpp 58

Elemendi lisamiseks konteineri lõppu nagu std::vektor > sa ei saa lihtsalt kirjutada xxx.push_back(uus X), kuna puudub kaudne teisendus X* в std::unikaalne_ptr.

Levinud lahendus on kirjutada xxx.emplace_back(uus X)kuna see koostab: meetod emplace_back konstrueerib elemendi otse oma argumentidest ja saab seetõttu kasutada eksplitsiitseid konstruktoreid.

See ei ole ohutu. Kui vektor on täis, eraldatakse mälu uuesti. Mälu ümberjaotamise toiming võib ebaõnnestuda, mille tulemuseks on erand std::bad_alloc. Sel juhul kursor kaob ja loodud objekti ei kustutata kunagi.

Ohutu lahendus on luua unikaalne_ptrmis omab kursorit enne, kui vektor proovib mälu ümber jaotada:

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

Alates C++14-st saate kasutada 'std::make_unique':

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

Seda tüüpi defekt ei ole LLVM-i jaoks kriitiline. Kui mälu ei saa eraldada, siis kompilaator lihtsalt peatub. Kuid rakenduste jaoks, millel on pikk tööaeg, mis ei saa lihtsalt katkeda, kui mälu eraldamine ebaõnnestub, see võib olla tõeline vastik viga.

Ehkki see kood LLVM-ile praktilist ohtu ei kujuta, leidsin, et kasulik on rääkida sellest veamustrist ja sellest, et PVS-Studio analüsaator on õppinud seda tuvastama.

Muud seda tüüpi hoiatused:

  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse konteinerisse „Läbib” meetodiga „emplace_back”. Erandi korral tekib mäluleke. PassManager.h 546
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse AA-de konteinerisse meetodiga 'emplace_back'. Erandi korral tekib mäluleke. AliasAnalysis.h 324
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse konteinerisse 'Entries' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse konteinerisse 'AllEdges' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. CFGMST.h 268
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse VMapsi konteinerisse meetodiga 'emplace_back'. Erandi korral tekib mäluleke. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse 'emplace_back' meetodil kirjed konteinerisse. Erandi korral tekib mäluleke. FDRLogBuilder.h 30
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse „PendingSubmodules” meetodiga „emplace_back”. Erandi korral tekib mäluleke. ModuleMap.cpp 810
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse 'Objects' konteinerisse meetodiga 'emplace_back'. Erandi korral tekib mäluleke. DebugMap.cpp 88
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse strateegia 'emplace_back' meetodil konteinerisse "Strategies". Erandi korral tekib mäluleke. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 685
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 686
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 688
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 689
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 690
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 691
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 692
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 693
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse 'Muutajad' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. llvm-stress.cpp 694
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse konteinerisse 'Operandid' meetodiga 'emplace_back'. Erandi korral tekib mäluleke. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] Ilma omanikuta kursor lisatakse konteinerisse "Stash" meetodiga "emplace_back". Erandi korral tekib mäluleke. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Ilma omanikuta osuti lisatakse konteinerisse „Sobitajad” meetodiga „emplace_back”. Erandi korral tekib mäluleke. GlobalISelEmitter.cpp 2702

Järeldus

Andsin kokku 60 hoiatust ja siis lõpetasin. Kas on muid defekte, mida PVS-Studio analüsaator LLVM-is tuvastab? Jah mul on. Kui aga artikli jaoks koodijuppe välja kirjutasin, oli käes hilisõhtu või õigemini isegi öö ja otsustasin, et on aeg seda päevaks nimetada.

Loodan, et see oli teile huvitav ja soovite PVS-Studio analüsaatorit proovida.

Analüsaatori saate alla laadida ja miinijahtija võtme hankida aadressil see leht.

Kõige tähtsam on kasutada regulaarselt staatilist analüüsi. Ühekordsed kontrollid, mille oleme läbi viinud staatilise analüüsi metoodika populariseerimiseks ja PVS-Stuudio ei ole tavaline stsenaarium.

Edu koodi kvaliteedi ja usaldusväärsuse parandamisel!

Vigade leidmine LLVM 8-st, kasutades PVS-Studio analüsaatorit

Kui soovite seda artiklit inglise keelt kõneleva publikuga jagada, kasutage tõlkelinki: Andrey Karpov. Vigade leidmine LLVM 8-st PVS-Studio abil.

Allikas: www.habr.com

Lisa kommentaar