Trovi cimojn en LLVM 8 uzante la analizilon PVS-Studio

Trovi cimojn en LLVM 8 uzante la analizilon PVS-Studio
Pli ol du jaroj pasis ekde la lasta koda kontrolo de la LLVM-projekto uzante nian analizilon PVS-Studio. Ni certigu, ke la analizilo PVS-Studio ankoraŭ estas ĉefa ilo por identigi erarojn kaj eblajn vundeblecojn. Por fari tion, ni kontrolos kaj trovos novajn erarojn en la eldono de LLVM 8.0.0.

Artikolo skribenda

Verdire, mi ne volis skribi ĉi tiun artikolon. Ne estas interese skribi pri projekto, kiun ni jam plurfoje kontrolis (1, 2, 3). Estas pli bone skribi pri io nova, sed mi ne havas elekton.

Ĉiufoje kiam nova versio de LLVM estas publikigita aŭ ĝisdatigita Clang Static Analizilo, ni ricevas demandojn de la jena tipo en nia poŝto:

Rigardu, la nova versio de Clang Static Analyzer lernis trovi novajn erarojn! Ŝajnas al mi, ke la graveco uzi PVS-Studio malpliiĝas. Clang trovas pli da eraroj ol antaŭe kaj atingas la kapablojn de PVS-Studio. Kion vi pensas pri ĉi tio?

Al tio mi ĉiam volas respondi ion kiel:

Ni ankaŭ ne sidas senlabore! Ni signife plibonigis la kapablojn de la analizilo PVS-Studio. Do ne maltrankviliĝu, ni daŭre gvidas kiel antaŭe.

Bedaŭrinde, ĉi tio estas malbona respondo. Ne estas pruvoj en ĝi. Kaj tial mi nun skribas ĉi tiun artikolon. Do, la LLVM-projekto denove estis kontrolita kaj diversaj eraroj estis trovitaj en ĝi. Mi nun montros tiujn, kiuj ŝajnis al mi interesaj. Clang Static Analyzer ne povas trovi ĉi tiujn erarojn (aŭ estas ege maloportune fari tion kun ĝia helpo). Sed ni povas. Cetere, mi trovis kaj notis ĉiujn ĉi tiujn erarojn en unu vespero.

Sed verki la artikolon daŭris plurajn semajnojn. Mi simple ne povis min enmeti ĉion ĉi en tekston :).

Cetere, se vi interesiĝas pri kiaj teknologioj estas uzataj en la analizilo PVS-Studio por identigi erarojn kaj eblajn vundeblecojn, tiam mi sugestas konatiĝi kun ĉi tio. notu.

Novaj kaj malnovaj diagnozoj

Kiel jam rimarkite, antaŭ ĉirkaŭ du jaroj la projekto LLVM denove estis kontrolita, kaj la trovitaj eraroj estis korektitaj. Nun ĉi tiu artikolo prezentos novan aron da eraroj. Kial novaj cimoj estis trovitaj? Estas 3 kialoj por ĉi tio:

  1. La LLVM-projekto evoluas, ŝanĝante malnovan kodon kaj aldonante novan kodon. Kompreneble, estas novaj eraroj en la modifita kaj skribita kodo. Ĉi tio klare montras, ke statika analizo devas esti uzata regule, kaj ne foje. Niaj artikoloj bone montras la kapablojn de la analizilo PVS-Studio, sed ĉi tio neniel rilatas al plibonigo de la kodkvalito kaj reduktado de la kosto ripari erarojn. Uzu senmovan kodan analizilon regule!
  2. Ni finas kaj plibonigas ekzistantajn diagnozojn. Tial la analizilo povas identigi erarojn, kiujn ĝi ne rimarkis dum antaŭaj skanadoj.
  3. Novaj diagnozoj aperis en PVS-Studio, kiuj ne ekzistis antaŭ 2 jaroj. Mi decidis reliefigi ilin en aparta sekcio por klare montri la evoluon de PVS-Studio.

Difektoj identigitaj per diagnozoj, kiuj ekzistis antaŭ 2 jaroj

Fragmento N1: Kopiu-Algluu

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

Averto de PVS-Studio: V501 [CWE-570] Estas identaj subesprimoj 'Name.startswith("avx512.mask.permvar.")' maldekstre kaj dekstre de la '||' operatoro. AutoUpgrade.cpp 73

Estas duoble kontrolite, ke la nomo komenciĝas per la subĉeno "avx512.mask.permvar.". En la dua kontrolo, ili evidente volis skribi ion alian, sed forgesis korekti la kopiitan tekston.

Fragmento N2: Tajpera

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

Averto PVS-Studio: V501 Estas identaj subesprimoj 'CXNameRange_WantQualifier' maldekstre kaj dekstre de la '|' operatoro. CIndex.cpp 7245

Pro mistajpo, la sama nomita konstanto estas uzata dufoje CXNameRange_WantQualifier.

Fragmento N3: Konfuzo kun funkciigisto-preteco

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

Averto de PVS-Studio: V502 [CWE-783] Eble la operatoro '?:' funkcias alimaniere ol ĝi estis atendita. La operatoro '?:' havas pli malaltan prioritaton ol la operatoro '=='. PPCTargetTransformInfo.cpp 404

Laŭ mi, ĉi tio estas tre bela eraro. Jes, mi scias, ke mi havas strangajn ideojn pri beleco :).

Nun, laŭ prioritatoj de operaciisto, la esprimo estas taksita jene:

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

De praktika vidpunkto, tia kondiĉo ne havas sencon, ĉar ĝi povas esti reduktita al:

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

Ĉi tio estas klara eraro. Plej verŝajne, ili volis kompari 0/1 kun variablo indekso. Por ripari la kodon vi devas aldoni krampojn ĉirkaŭ la ternara operatoro:

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

Cetere, la ternara operatoro estas tre danĝera kaj provokas logikajn erarojn. Estu tre singarda kun ĝi kaj ne estu avida kun krampoj. Mi rigardis ĉi tiun temon pli detale tie, en la ĉapitro "Atentu pri la ?: Operatoro kaj Enfermu Ĝin en Krampoj."

Fragmento N4, N5: Nula montrilo

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

Averto de PVS-Studio: V522 [CWE-476] Dereferencing de la nula montrilo 'LHS' povus okazi. TGParser.cpp 2152

Se la montrilo LHS estas nula, averto estu eligita. Tamen, anstataŭe, ĉi tiu sama nula montrilo estos dereferencita: LHS->getAsString().

Ĉi tio estas tre tipa situacio kiam eraro estas kaŝita en erartraktilo, ĉar neniu testas ilin. Senmovaj analiziloj kontrolas ĉiun atingeblan kodon, negrave kiom ofte ĝi estas uzata. Ĉi tio estas tre bona ekzemplo de kiel statika analizo kompletigas aliajn provojn kaj erarprotektajn teknikojn.

Simila eraro pri manipulado de montriloj RHS permesite en la kodo tuj sube: V522 [CWE-476] Dereferencing de la nula montrilo 'RHS' povus okazi. TGParser.cpp 2186

Fragmento N6: Uzante la montrilon post moviĝado

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 Averto: V522 [CWE-476] Dereferencing de la nula montrilo 'ProgClone' povus okazi. Miscompilation.cpp 601

Komence inteligenta montrilo ProgClone ĉesas posedi la objekton:

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

Fakte, nun ProgClone estas nula montrilo. Tial, nula montrilo dereferenco devus okazi ĝuste malsupre:

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

Sed, fakte, ĉi tio ne okazos! Notu, ke la buklo ne efektive estas efektivigita.

Komence de la ujo Miskompilitaj Funkcioj malbaris:

MiscompiledFunctions.clear();

Poste, la grandeco de ĉi tiu ujo estas uzata en la buklokondiĉo:

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

Estas facile vidi, ke la buklo ne komenciĝas. Mi pensas, ke ĉi tio ankaŭ estas cimo kaj la kodo estu skribita alie.

Ŝajnas, ke ni renkontis tiun faman egalecon de eraroj! Unu eraro maskas alian :).

Fragmento N7: Uzante la montrilon post moviĝado

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-averto: V522 [CWE-476] Dereferencing de la nula montrilo 'Testo' povus okazi. Miscompilation.cpp 709

La sama situacio denove. Komence, la enhavo de la objekto estas movita, kaj tiam ĝi estas uzata kvazaŭ nenio okazis. Mi vidas ĉi tiun situacion pli kaj pli ofte en programkodo post movada semantiko aperis en C++. Jen kial mi amas la C++-lingvon! Estas pli kaj pli novaj manieroj pafi vian propran kruron. La analizilo PVS-Studio ĉiam havos laboron :).

Fragmento N8: Nula montrilo

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-averto: V522 [CWE-476] Dereferencing de la nula montrilo 'Tipo' povus okazi. PrettyFunctionDumper.cpp 233

Krom erartraktiloj, sencimigaj presitaj funkcioj kutime ne estas testitaj. Ni havas ĝuste tian kazon antaŭ ni. La funkcio atendas la uzanton, kiu, anstataŭ solvi siajn problemojn, estos devigita ripari ĝin.

Ĝuste:

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

Fragmento N9: Nula montrilo

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-averto: V522 [CWE-476] Dereferencing de la nula montrilo 'Ty' povus okazi. SearchableTableEmitter.cpp 614

Mi pensas, ke ĉio estas klara kaj ne postulas klarigon.

Fragmento N10: Tajpera

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

Averto de PVS-Studio: V570 La variablo 'Identigilo->Tipo' estas atribuita al si mem. FormatTokenLexer.cpp 249

Ne utilas atribui variablon al si mem. Plej verŝajne ili volis skribi:

Identifier->Type = Question->Type;

Fragmento N11: Suspektinda rompo

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

Averto de PVS-Studio: V622 [CWE-478] Konsideru inspekti la "ŝaltilon" deklaron. Eblas ke la unua "kaza" operatoro mankas. SystemZAsmParser.cpp 652

Estas tre suspektinda operatoro ĉe la komenco paŭzo. Ĉu vi forgesis skribi ion alian ĉi tie?

Fragmento N12: Kontrolado de montrilo post dereferenco

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

Averto de PVS-Studio: V595 [CWE-476] La "Callee" montrilo estis utiligita antaŭ ol ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 172, 174. AMDGPUInline.cpp 172

Montrilo Callee ĉe la komenco estas dereferencita kiam la funkcio estas vokita getTTI.

Kaj tiam rezultas, ke ĉi tiu montrilo devus esti kontrolita por egaleco nullptr:

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

Sed estas tro malfrue...

Fragmento N13 - N...: Kontrolado de montrilo post dereferenco

La situacio diskutita en la antaŭa kodfragmento ne estas unika. Ĝi aperas ĉi tie:

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-averto: V595 [CWE-476] La 'CalleeFn' montrilo estis utiligita antaŭ ol ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 1079, 1081. SimplifyLibCalls.cpp 1079

Kaj ĉi tie:

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-averto: V595 [CWE-476] La "ND" montrilo estis uzita antaŭ ol ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 532, 534. SemaTemplateInstantiateDecl.cpp 532

Kaj ĉi tie:

  • V595 [CWE-476] La "U" montrilo estis utiligita antaŭ ol ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 404, 407. DWARFormValue.cpp 404
  • V595 [CWE-476] La "ND" montrilo estis utiligita antaŭ ol ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 2149, 2151. SemaTemplateInstantiate.cpp 2149

Kaj tiam mi malinteresiĝis studi la avertojn kun numero V595. Do mi ne scias ĉu estas pli similaj eraroj krom tiuj ĉi tie listigitaj. Plej verŝajne ekzistas.

Fragmento N17, N18: Suspektinda movo

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

Averto de PVS-Studio: V629 [CWE-190] Konsideru inspekti la esprimon '~(Size - 1) << 1'. Bitoŝanĝo de la 32-bita valoro kun posta vastiĝo al la 64-bita tipo. AArch64AddressingModes.h 260

Ĝi eble ne estas cimo kaj la kodo funkcias ĝuste kiel celite. Sed ĉi tio klare estas tre suspektinda loko kaj devas esti kontrolita.

Ni diru la variablo grandeco estas egala al 16, kaj tiam la aŭtoro de la kodo planis akiri ĝin en variablo NImms valoro:

1111111111111111111111111111111111111111111111111111111111100000

Tamen, fakte la rezulto estos:

0000000000000000000000000000000011111111111111111111111111100000

La fakto estas, ke ĉiuj kalkuloj okazas per la 32-bita sensigna tipo. Kaj nur tiam, ĉi tiu 32-bita nesubskribita tipo estos implicite vastigita al uint64_t. En ĉi tiu kazo, la plej signifaj bitoj estos nul.

Vi povas ripari la situacion jene:

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

Simila situacio: V629 [CWE-190] Konsideru inspekti la esprimon 'Immr << 6'. Bitoŝanĝo de la 32-bita valoro kun posta vastiĝo al la 64-bita tipo. AArch64AddressingModes.h 269

Fragmento N19: Mankas ŝlosilvorto alia?

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

Averto de PVS-Studio: V646 [CWE-670] Konsideru inspekti la logikon de la aplikaĵo. Eblas, ke mankas ŝlosilvorto "alia". AMDGPUAsmParser.cpp 5655

Ne estas eraro ĉi tie. Ekde la tiam-bloko de la unua if finiĝas per daŭrigi, tiam ne gravas, estas ŝlosilvorto alia aŭ ne. De ajna maniero la kodo funkcios same. Ankoraŭ maltrafis alia faras la kodon pli neklara kaj danĝera. Se en la estonteco daŭrigi malaperas, la kodo komencos funkcii tute alie. Miaopinie estas pli bone aldoni alia.

Fragmento N20: Kvar tajperaroj de la sama tipo

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

Avertoj de PVS-Studio:

  • V655 [CWE-480] La kordoj estis kunligitaj sed ne estas uzitaj. Konsideru inspekti la esprimon 'Result + Name.str()'. Simbolo.cpp 32
  • V655 [CWE-480] La kordoj estis kunligitaj sed ne estas uzitaj. Konsideru inspekti la esprimon 'Result + "(ObjC Class)" + Name.str()'. Simbolo.cpp 35
  • V655 [CWE-480] La kordoj estis kunligitaj sed ne estas uzitaj. Konsideru inspekti la 'Rezulton + "(ObjC Class EH) " + Name.str()' esprimon. Simbolo.cpp 38
  • V655 [CWE-480] La kordoj estis kunligitaj sed ne estas uzitaj. Konsideru inspekti la 'Rezulton + "(ObjC IVar)" + Name.str()' esprimon. Simbolo.cpp 41

Akcidento, la + operatoro estas uzata anstataŭ la += operatoro. La rezulto estas dezajnoj sen signifo.

Fragmento N21: Nedifinita konduto

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

Provu mem trovi la danĝeran kodon. Kaj ĉi tio estas bildo por distri la atenton por ne tuj rigardi la respondon:

Trovi cimojn en LLVM 8 uzante la analizilon PVS-Studio

Averto de PVS-Studio: V708 [CWE-758] Danĝera konstruo estas uzata: 'FeaturesMap[Op] = FeaturesMap.size()', kie 'FeaturesMap' estas de 'map' klaso. Ĉi tio povas konduki al nedifinita konduto. RISCVCompressInstEmitter.cpp 490

Problemlinio:

FeaturesMap[Op] = FeaturesMap.size();

Se elemento Op ne estas trovita, tiam nova elemento estas kreita en la mapo kaj la nombro da elementoj en ĉi tiu mapo estas skribita tie. Estas nur nekonate ĉu la funkcio estos nomita grandeco antaŭ aŭ post aldoni novan elementon.

Fragmento N22-N24: Ripetaj taskoj

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

Averto de PVS-Studio: V519 [CWE-563] La variablo 'NType' ricevas valorojn dufoje sinsekve. Eble ĉi tio estas eraro. Kontrolu liniojn: 1663, 1664. MachOObjectFile.cpp 1664

Mi ne pensas, ke estas vera eraro ĉi tie. Nur nenecesa ripeta tasko. Sed tamen eraro.

Same:

  • V519 [CWE-563] La variablo 'B.NDesc' ricevas valorojn dufoje sinsekve. Eble ĉi tio estas eraro. Kontrolu liniojn: 1488, 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] La variablo ricevas valorojn dufoje sinsekve. Eble ĉi tio estas eraro. Kontrolu liniojn: 59, 61. coff2yaml.cpp 61

Fragmento N25-N27: Pli da reasignoj

Nun ni rigardu iomete malsaman version de reasignado.

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

Averto de PVS-Studio: V519 [CWE-563] La variablo "Alineado" ricevas valorojn dufoje sinsekve. Eble ĉi tio estas eraro. Kontrolu liniojn: 1158, 1160. LoadStoreVectorizer.cpp 1160

Ĉi tio estas tre stranga kodo, kiu ŝajne enhavas logikan eraron. Komence, varia alineamiento valoro estas atribuita depende de la kondiĉo. Kaj tiam la tasko okazas denove, sed nun sen ajna kontrolo.

Similaj situacioj videblas ĉi tie:

  • V519 [CWE-563] La variablo "Efikoj" ricevas valorojn dufoje sinsekve. Eble ĉi tio estas eraro. Kontrolu liniojn: 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] La variablo "ExpectNoDerefChunk" ricevas valorojn dufoje sinsekve. Eble ĉi tio estas eraro. Kontrolu liniojn: 4970, 4973. SemaType.cpp 4973

Fragmento N28: Ĉiam vera kondiĉo

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

Averto de PVS-Studio: V547 [CWE-571] Esprimo 'nextByte != 0x90' ĉiam estas vera. X86DisassemblerDecoder.cpp 379

Kontrolado ne havas sencon. Variablo sekva Byte ĉiam ne egalas al la valoro 0x90, kiu sekvas el la antaŭa kontrolo. Ĉi tio estas ia logika eraro.

Fragmento N29 - N...: Ĉiam veraj/malveraj kondiĉoj

La analizilo donas multajn avertojn, ke la tuta kondiĉo (V547) aŭ parto de ĝi (V560) estas ĉiam vera aŭ malvera. Ofte ĉi tiuj ne estas veraj eraroj, sed simple fuŝa kodo, la rezulto de makro-vastigo, kaj similaj. Tamen, estas senco rigardi ĉiujn ĉi tiujn avertojn, ĉar aŭtentaj logikaj eraroj okazas de tempo al tempo. Ekzemple, ĉi tiu sekcio de kodo estas suspektinda:

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

Averto de PVS-Studio: V560 [CWE-570] Parto de kondiĉa esprimo ĉiam estas malvera: RegNo == 0xe. ARMDisassembler.cpp 939

La konstanta 0xE estas la valoro 14 en decimalo. Ekzameno RegNo == 0xe ne havas sencon ĉar se RegNo > 13, tiam la funkcio kompletigos sian ekzekuton.

Estis multaj aliaj avertoj kun ID-oj V547 kaj V560, sed kiel kun V595, Mi ne interesis studi ĉi tiujn avertojn. Jam estis klare, ke mi havas sufiĉe da materialo por verki artikolon :). Tial, estas nekonate kiom da eraroj de ĉi tiu tipo povas esti identigitaj en LLVM uzante PVS-Studio.

Mi donos al vi ekzemplon pri kial studi ĉi tiujn ellasilon estas enuiga. La analizilo tute pravas eligi averton pri la sekva kodo. Sed ĉi tio ne estas eraro.

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

PVS-Studio Averto: V547 [CWE-570] Esprimo '!HasError' ĉiam estas malvera. UnwrappedLineParser.cpp 1635

Fragmento N30: ​​Suspektinda reveno

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

Averto de PVS-Studio: V612 [CWE-670] Senkondiĉa 'reveno' ene de buklo. R600OptimizeVectorRegisters.cpp 63

Ĉi tio estas aŭ eraro aŭ specifa tekniko, kiu celas klarigi ion al programistoj, kiuj legas la kodon. Ĉi tiu dezajno klarigas nenion al mi kaj aspektas tre suspektinda. Pli bone ne skribi tiel :).

Laca? Tiam estas tempo fari teon aŭ kafon.

Trovi cimojn en LLVM 8 uzante la analizilon PVS-Studio

Difektoj identigitaj per novaj diagnozoj

Mi pensas, ke 30 aktivigoj de malnovaj diagnozoj sufiĉas. Ni vidu nun, kiajn interesajn aferojn oni povas trovi kun la novaj diagnozoj, kiuj poste aperis en la analizilo antaŭa ĉekoj. Dum tiu tempo, totalo de 66 ĝeneraluzeblaj diagnozoj estis aldonita al la C++-analizilo.

Fragmento N31: Neatingebla kodo

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

Averto de PVS-Studio: V779 [CWE-561] Neatingebla kodo detektita. Eblas, ke eraro ĉeestas. ExecutionUtils.cpp 146

Kiel vi povas vidi, ambaŭ branĉoj de la telefonisto if finiĝas per voko al la telefonisto reveno. Sekve, la ujo CtorDtorsByPriority neniam estos purigita.

Fragmento N32: Neatingebla kodo

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-averto: V779 [CWE-561] Neatingebla kodo detektita. Eblas, ke eraro ĉeestas. LLParser.cpp 835

Interesa situacio. Ni unue rigardu ĉi tiun lokon:

return ParseTypeIdEntry(SummaryID);
break;

Unuavide, ŝajnas, ke ĉi tie ne estas eraro. Ĝi aspektas kiel la operatoro paŭzo estas ekstra ĉi tie, kaj vi povas simple forigi ĝin. Tamen, ne ĉio estas tiel simpla.

La analizilo eligas averton sur la linioj:

Lex.setIgnoreColonInIdentifiers(false);
return false;

Kaj efektive, ĉi tiu kodo estas neatingebla. Ĉiuj kazoj en ŝaltilo finiĝas per voko de la telefonisto reveno. Kaj nun sensenca sole paŭzo ne aspektas tiel sendanĝera! Eble unu el la branĉoj devus finiĝi per paŭzo, ne plu reveno?

Fragmento N33: Hazarda restarigo de altaj bitoj

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

Averto de PVS-Studio: V784 La grandeco de la bita masko estas malpli ol la grandeco de la unua operando. Ĉi tio kaŭzos la perdon de pli altaj bitoj. RuntimeDyld.cpp 815

Bonvolu noti, ke la funkcio getStubAlignment redonas tipon sennoma. Ni kalkulu la valoron de la esprimo, supozante ke la funkcio liveras la valoron 8:

~(getStubAlignment() - 1)

~(8u-1)

0xFFFFFFFF8u

Nun rimarku, ke la variablo DataSize havas 64-bitan nesubskribitan tipon. Rezultas, ke kiam oni plenumas la operacion DataSize & 0xFFFFFFF8u, ĉiuj tridek du alt-ordaj bitoj estos rekomencigitaj al nulo. Plej verŝajne, ĉi tio ne deziris la programisto. Mi suspektas, ke li volis kalkuli: DataSize & 0xFFFFFFFFFFFFFFF8u.

Por ripari la eraron, vi devus skribi ĉi tion:

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

Aŭ tiel:

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

Fragmento N34: Malsukcesa eksplicita tipo-rolantaro

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

Averto de PVS-Studio: V1028 [CWE-190] Ebla superfluo. Konsideru ĵeti operandojn de la operatoro 'NumElts * Scale' al la tipo 'size_t', ne la rezulton. X86ISelLowering.h 1577

Eksplicita tipa fandado estas uzata por eviti superfluon dum multobligado de tipvariabloj int. Tamen, eksplicita tipa gisado ĉi tie ne protektas kontraŭ superfluo. Unue, la variabloj estos multobligitaj, kaj nur tiam la 32-bita rezulto de la multipliko estos vastigita al la tipo grandeco_t.

Fragmento N35: Malsukcesa Kopi-Alglui

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] Du similaj kodfragmentoj estis trovitaj. Eble, ĉi tio estas tajperaro kaj 'Op1' variablo devus esti uzata anstataŭ 'Op0'. InstCombineCompares.cpp 5507

Ĉi tiu nova interesa diagnozo identigas situaciojn kie peco de kodo estis kopiita kaj kelkaj nomoj komencis esti ŝanĝitaj en ĝi, sed en unu loko ili ne korektis ĝin.

Bonvolu noti, ke en la dua bloko ili ŝanĝiĝis Op0 sur Op1. Sed en unu loko ili ne riparis ĝin. Plej verŝajne ĝi devus esti skribita tiel:

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

Fragmento N36: Varia Konfuzo

struct Status {
  unsigned Mask;
  unsigned Mode;

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

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

Averto de PVS-Studio: V1001 [CWE-563] La variablo 'Reĝimo' estas asignita sed ne estas uzata antaŭ la fino de la funkcio. SIModeRegister.cpp 48

Estas tre danĝere doni al funkcio-argumentoj la samajn nomojn kiel klasanoj. Estas tre facile konfuziĝi. Ni havas ĝuste tian kazon antaŭ ni. Ĉi tiu esprimo ne havas sencon:

Mode &= Mask;

La funkcio argumento ŝanĝiĝas. Tio estas ĉio. Ĉi tiu argumento ne plu estas uzata. Plej verŝajne vi devus esti skribinta ĝin tiel:

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

Fragmento N37: Varia Konfuzo

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

Averto PVS-Studio: V1001 [CWE-563] La variablo 'Size' estas asignita sed ne estas uzata antaŭ la fino de la funkcio. Objekto.cpp 424

La situacio estas simila al la antaŭa. Ĝi devus esti skribita:

this->Size += this->EntrySize;

Fragmento N38-N47: Ili forgesis kontroli la indekson

Antaŭe, ni rigardis ekzemplojn de diagnoza ekigado V595. Ĝia esenco estas, ke la montrilo estas dereferencita komence, kaj nur tiam kontrolita. Juna diagnozo V1004 estas la malo en signifo, sed ankaŭ malkaŝas multajn erarojn. Ĝi identigas situaciojn kie la montrilo estis kontrolita komence kaj tiam forgesita fari tion. Ni rigardu tiajn kazojn trovitajn ene de 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());  // <=
  ....
}

PVS-Studio-averto: V1004 [CWE-476] La "Ptr" montrilo estis uzata nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 729, 738. TargetTransformInfoImpl.h 738

Variablo Ptr povas esti egala nullptr, kiel pruvas la ĉeko:

if (Ptr != nullptr)

Tamen, sub ĉi tiu montrilo estas dereferencita sen antaŭkontrolo:

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

Ni konsideru alian similan kazon.

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-averto: V1004 [CWE-476] La "FD" montrilo estis uzata nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 3228, 3231. CGDebugInfo.cpp 3231

Atentu la signon FD. Mi certas, ke la problemo estas klare videbla kaj ne necesas speciala klarigo.

Kaj plue:

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

Averto de PVS-Studio: V1004 [CWE-476] La montrilo 'PtrTy' estis uzata nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 960, 965. InterleavedLoadCombinePass.cpp 965

Kiel protekti vin kontraŭ tiaj eraroj? Estu pli atenta pri Code-Review kaj uzu la statikan analizilon de PVS-Studio por regule kontroli vian kodon.

Ne utilas citi aliajn kodfragmentojn kun ĉi-tipaj eraroj. Mi lasos nur liston de avertoj en la artikolo:

  • V1004 [CWE-476] La "Expr" montrilo estis uzata nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] La "PI" montrilo estis uzata nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] La montrilo 'StatepointCall' estis uzata nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] La "RV" montrilo estis uzita nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] La montrilo 'CalleeFn' estis uzata nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] La "TC" montrilo estis uzita nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 1819, 1824. Driver.cpp 1824

Fragmento N48-N60: Ne kritika, sed difekto (ebla memorforko)

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

Averto de PVS-Studio: V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Strategioj' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-isel-fuzzer.cpp 58

Por aldoni elementon al la fino de ujo kiel std::vektoro > vi ne povas simple skribi xxx.push_back(nova X), ĉar ekzistas neniu implica konvertiĝo de X* в std::unique_ptr.

Ofta solvo estas skribi xxx.emplace_back(nova X)ĉar ĝi kompilas: metodo emplace_back konstruas elementon rekte el ĝiaj argumentoj kaj povas tial uzi eksplicitajn konstrukcilojn.

Ĝi ne estas sekura. Se la vektoro estas plena, tiam memoro estas re-asignita. La memora reasigna operacio povas malsukcesi, rezultigante escepton esti ĵetita std::bad_alloc. En ĉi tiu kazo, la montrilo estos perdita kaj la kreita objekto neniam estos forigita.

Sekura solvo estas krei unika_ptrkiu posedos la montrilon antaŭ ol la vektoro provas reasigni memoron:

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

Ekde C++14, vi povas uzi 'std::make_unique':

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

Ĉi tiu speco de difekto ne estas kritika por LLVM. Se memoro ne povas esti asignita, la kompililo simple haltos. Tamen, por aplikoj kun longaj uptempo, kiu ne povas nur ĉesigi se memora asigno malsukcesas, tio povas esti vera malbona cimo.

Do, kvankam ĉi tiu kodo ne prezentas praktikan minacon al LLVM, mi trovis ĝin utila paroli pri ĉi tiu erara ŝablono kaj ke la analizilo de PVS-Studio lernis identigi ĝin.

Aliaj avertoj de ĉi tiu tipo:

  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la 'Passes'-ujo per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. PassManager.h 546
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'AAs' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. AliasAnalysis.h 324
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Enskriboj' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'AllEdges' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. CFGMST.h 268
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'VMaps' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Rekordoj' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. FDRLogBuilder.h 30
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'PendingSubmodules' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. ModuleMap.cpp 810
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Objektoj' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. DebugMap.cpp 88
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Strategioj' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 685
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 686
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 688
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 689
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 690
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 691
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 692
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 693
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Modifiers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. llvm-stress.cpp 694
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Operandoj' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Stash' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Montrilo sen posedanto estas aldonita al la ujo 'Matchers' per la metodo 'emplace_back'. Memorfuĝo okazos en kazo de escepto. GlobalISelEmitter.cpp 2702

konkludo

Mi eligis 60 avertojn entute kaj poste ĉesis. Ĉu ekzistas aliaj difektoj, kiujn la analizilo PVS-Studio detektas en LLVM? Jes mi havas. Tamen, kiam mi skribis kodfragmentojn por la artikolo, estis malfrua vespero, aŭ pli ĝuste eĉ nokto, kaj mi decidis, ke estas tempo nomi ĝin tago.

Mi esperas, ke vi trovis ĝin interesa kaj volos provi la analizilon PVS-Studio.

Vi povas elŝuti la analizilon kaj akiri la minforigan ŝlosilon ĉe ĉi tiu paĝo.

Plej grave, uzu statikan analizon regule. Unufojaj kontroloj, efektivigitaj de ni por popularigi la metodaron de statika analizo kaj PVS-Studio ne estas normala scenaro.

Bonŝancon en plibonigo de la kvalito kaj fidindeco de via kodo!

Trovi cimojn en LLVM 8 uzante la analizilon PVS-Studio

Se vi volas dividi ĉi tiun artikolon kun anglalingva publiko, bonvolu uzi la tradukan ligilon: Andrey Karpov. Trovi Cimojn en LLVM 8 kun PVS-Studio.

fonto: www.habr.com

Aldoni komenton