Nous trouvons des bugs dans LLVM 8 Ă  l'aide de l'analysateur PVS-Studio

Finding Bugs in LLVM 8 with PVS-Studio
Il y a plus de deux ans, nous avons examiné le code du projet LLVM à l'aide de notre analyseur PVS-Studio. Assurons-nous que l'analyseur PVS-Studio reste l'outil de choix pour identifier les erreurs et les vulnérabilités potentielles. Pour cela, examinons et trouvons de nouvelles erreurs dans la version LLVM 8.0.0.

Article à rédiger

Pour ĂȘtre honnĂȘte, je n'avais pas envie d'Ă©crire cet article. Ce n'est pas intĂ©ressant d'Ă©crire sur un projet que nous avons dĂ©jĂ  vĂ©rifiĂ© maintes fois (1, 2, 3). Mieux vaut Ă©crire sur quelque chose de nouveau, mais je n'ai pas le choix.

Chaque fois qu'une nouvelle version de LLVM est publiée ou mise à jour Clang Static Analyzer, nous recevons des questions de ce type par e-mail :

Regardez, la nouvelle version de Clang Static Analyzer a appris à détecter de nouvelles erreurs ! J'ai l'impression que l'utilité de PVS-Studio diminue. Clang trouve plus d'erreurs qu'auparavant et rattrape les capacités de PVS-Studio. Qu'en pensez-vous ?

À cela, j'ai toujours envie de rĂ©pondre quelque chose comme :

Nous ne restons pas inactifs non plus ! Nous avons considérablement amélioré les capacités de l'analyseur PVS-Studio. Ne vous inquiétez pas, nous continuons à mener, comme avant.

Malheureusement, c'est une mauvaise rĂ©ponse. Elle n'a pas de preuves. Et c'est pourquoi je rĂ©dige cet article. Ainsi, le projet LLVM a de nouveau Ă©tĂ© vĂ©rifiĂ© et diverses erreurs ont Ă©tĂ© trouvĂ©es. Celles qui m'ont semblĂ© intĂ©ressantes, je vais maintenant les dĂ©montrer. Ces erreurs ne peuvent pas ĂȘtre trouvĂ©es par Clang Static Analyzer (ou c'est trĂšs difficile Ă  faire avec cet outil). Pourtant, nous pouvons les dĂ©tecter. Et j'ai trouvĂ© et notĂ© toutes ces erreurs en une seule soirĂ©e.

En revanche, l'écriture de l'article a pris plusieurs semaines. Je n'arrivais pas à me convaincre de tout mettre par écrit :).

À propos, si cela vous intĂ©resse, quelles technologies sont utilisĂ©es dans l'analyseur PVS-Studio pour dĂ©tecter les erreurs et les vulnĂ©rabilitĂ©s potentielles, je vous propose de dĂ©couvrir cette note.

Nouvelles et anciennes diagnostics

Comme déjà mentionné, il y a environ deux ans, le projet LLVM a été vérifié à nouveau, et les erreurs trouvées ont été corrigées. Maintenant, cet article présentera un nouvel ensemble d'erreurs. Pourquoi de nouvelles erreurs ont-elles été trouvées ? Il y a trois raisons à cela :

  1. Le projet LLVM Ă©volue, le vieux code est modifiĂ© et de nouveaux Ă©lĂ©ments apparaissent. Il est donc Ă©vident que le code modifiĂ© ou Ă©crit contient de nouvelles erreurs. Cela illustre bien que l'analyse statique doit ĂȘtre appliquĂ©e rĂ©guliĂšrement et pas au coup par coup. Nos articles montrent bien les capacitĂ©s de l'analiseur PVS-Studio, mais cela n'a rien Ă  voir avec l'amĂ©lioration de la qualitĂ© du code et la rĂ©duction du coĂ»t de correction des erreurs. Utilisez un analyseur statique de code rĂ©guliĂšrement !
  2. Nous améliorons et perfectionnons les diagnostics existants. Ainsi, l'analiseur peut identifier des erreurs qui n'étaient pas détectées lors des vérifications précédentes.
  3. De nouveaux diagnostics ont été ajoutés à PVS-Studio, qui n'étaient pas disponibles il y a deux ans. J'ai décidé de les mettre dans une section à part pour montrer clairement l'évolution de PVS-Studio.

Défauts détectés par des diagnostics qui existaient il y a deux ans

Fragment N1 : Copie-Collage

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

Avertissement PVS-Studio : V501 [CWE-570] Il y a des sous-expressions identiques ‘Name.startswith(«avx512.mask.permvar.»)’ Ă  gauche et Ă  droite de l’opĂ©rateur ‘||’. AutoUpgrade.cpp 73

Il est vérifié deux fois que le nom commence par la sous-chaßne «avx512.mask.permvar.». Lors de la deuxiÚme vérification, on voulait manifestement écrire autre chose, mais on a oublié de corriger le texte copié.

Fragment N2 : Faute de frappe

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

Avertissement PVS-Studio : V501 Il y a des sous-expressions identiques ‘CXNameRange_WantQualifier’ Ă  gauche et Ă  droite de l’opĂ©rateur ‘|’. CIndex.cpp 7245

À cause d'une faute de frappe, la mĂȘme constante nommĂ©e est utilisĂ©e deux fois CXNameRange_WantQualifier.

Fragment N3 : Confusion avec les priorités des opérateurs

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

Avertissement PVS-Studio : V502 [CWE-783] Peut-ĂȘtre que l’opĂ©rateur ‘?:’ fonctionne d'une maniĂšre diffĂ©rente de ce qui Ă©tait attendu. L’opĂ©rateur ‘?:’ a une prioritĂ© infĂ©rieure Ă  celle de l’opĂ©rateur ‘==’. PPCTargetTransformInfo.cpp 404

À mon avis, c'est une belle erreur. Oui, je sais que j'ai des idĂ©es Ă©tranges sur la beautĂ© :).

Actuellement, selon les priorités des opérateurs, l'expression est calculée comme suit :

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

D'un point de vue pratique, cette condition n'a pas de sens, car elle peut ĂȘtre simplifiĂ©e en :

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

C'est une erreur manifeste. Il est probable qu'on voulait comparer 0/1 avec la variable Index. Pour corriger le code, il faut ajouter des parenthÚses autour de l'opérateur ternaire :

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

Au fait, l'opérateur ternaire est trÚs dangereux et provoque des erreurs logiques. Soyez trÚs prudent avec lui et ne soyez pas avare en parenthÚses. J'ai déjà abordé ce sujet ici, dans le chapitre « Méfiez-vous de l'opérateur ?: et enfermez-le dans des parenthÚses ».

Fragment N4, N5 : Pointeur nul

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

Avertissement PVS-Studio : V522 [CWE-476] La dĂ©fĂ©ration du pointeur nul ‘LHS’ pourrait se produire. TGParser.cpp 2152

Si le pointeur LHS s'avĂšre ĂȘtre nul, un avertissement doit ĂȘtre Ă©mis. Cependant, au lieu de cela, il y aura une dĂ©fĂ©ration de ce mĂȘme pointeur nul : LHS->getAsString().

C'est une situation typique oĂč une erreur se cache dans le gestionnaire d'erreurs, car personne ne les teste. Les analyseurs statiques vĂ©rifient tout le code atteignable, peu importe Ă  quelle frĂ©quence il est utilisĂ©. C'est un trĂšs bon exemple de la façon dont l'analyse statique complĂšte d'autres mĂ©thodes de test et de protection contre les erreurs.

Une erreur similaire de gestion de pointeur RHS est prĂ©sente dans le code un peu plus bas : V522 [CWE-476] La dĂ©fĂ©ration du pointeur nul ‘RHS’ pourrait se produire. TGParser.cpp 2186

Fragment N6 : Utilisation d'un pointeur aprÚs déplacement

static Expected
ExtractBlocks(....)
{
  ....
  std::unique_ptr ProgClone = CloneModule(BD.getProgram(), VMap);
  ....
  BD.setNewProgram(std::move(ProgClone));                                // getFunction(MisCompFunctions[i].first);  // <=
    assert(NewF && "Function not found??");
    MiscompiledFunctions.push_back(NewF);
  }
  ....
}

Avertissement PVS-Studio : V522 [CWE-476] La dĂ©fĂ©ration du pointeur nul ‘ProgClone’ pourrait se produire. Miscompilation.cpp 601

Au début, le pointeur intelligent ProgClone cesse de posséder l'objet :

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

En fait, maintenant ProgClone — c'est un pointeur nul. Donc, un peu plus bas, nous devrions dereferencer ce pointeur nul :

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

Mais, en réalité, cela ne se produira pas ! Notez que la boucle ne s'exécute pas réellement.

Au début du conteneur MiscompiledFunctions est nettoyé :

MiscompiledFunctions.clear();

Ensuite, la taille de ce conteneur est utilisée dans la condition de la boucle :

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

Il est facile de voir que la boucle ne dĂ©marre pas. Je pense que c'est aussi une erreur, et que le code devrait ĂȘtre Ă©crit diffĂ©remment.

Il semble que nous ayons rencontré cette fameuse parité d'erreurs ! Une erreur masque une autre :).

Fragment N7 : Utilisation d'un pointeur aprÚs déplacement

static Expected TestOptimizer(BugDriver &BD, std::unique_ptr Test,
                                    std::unique_ptr Safe) {
  outs() << "  Optimizing functions being tested: ";
  std::unique_ptr 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;
  }
  ....
}

Avertissement PVS-Studio : V522 [CWE-476] La dĂ©rĂ©rĂ©dĂ©rence du pointeur nul ‘Test’ pourrait avoir lieu. Miscompilation.cpp 709

Encore la mĂȘme situation. Au dĂ©but, le contenu de l'objet est dĂ©placĂ©, puis il est utilisĂ© comme si de rien n'Ă©tait. Je rencontre de plus en plus cette situation dans le code des programmes, depuis que la sĂ©mantique de dĂ©placement a Ă©tĂ© introduite en C++. C'est pour ça que j'aime le C++ ! De nouvelles façons de se tirer dans le pied apparaissent constamment. L'analyste PVS-Studio aura toujours du travail :).

Fragment N8 : Pointeur nul

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

Avertissement PVS-Studio : V522 [CWE-476] La dĂ©rĂ©rĂ©dĂ©rence du pointeur nul ‘Type’ pourrait avoir lieu. PrettyFunctionDumper.cpp 233

En plus des gestionnaires d'erreurs, les fonctions de débogage de l'impression des données ne sont généralement pas testées. Nous avons ici un tel cas. La fonction attend un utilisateur, qui au lieu de résoudre ses problÚmes, sera contraint de s'occuper de sa correction.

Correct :

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

Fragment N9 : Pointeur nul

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

Avertissement PVS-Studio : V522 [CWE-476] Le déréférencement du pointeur nul 'Ty' pourrait avoir lieu. SearchableTableEmitter.cpp 614

Je pense que tout est clair et ne nécessite pas d'explications.

Fragment N10 : Typo

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

Avertissement PVS-Studio : V570 La variable 'Identifier->Type' est assignĂ©e Ă  elle-mĂȘme. FormatTokenLexer.cpp 249

Il est inutile d’assigner une variable Ă  elle-mĂȘme. Il est probable que vous vouliez Ă©crire :

Identifier->Type = Question->Type;

Fragment N11 : break suspect

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

Avertissement PVS-Studio : V622 [CWE-478] Considérez l'inspection de l'instruction 'switch'. Il est possible que le premier opérateur 'case' soit manquant. SystemZAsmParser.cpp 652

Il y a une instruction trÚs suspecte au début break. Avez-vous oublié d'écrire quelque chose ici ?

Fragment N12 : Vérification du pointeur aprÚs déréférencement

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("call de fonction indéfini");
  ....
}

Avertissement PVS-Studio : V595 [CWE-476] Le pointeur 'Callee' a Ă©tĂ© utilisĂ© avant d’ĂȘtre vĂ©rifiĂ© contre nullptr. VĂ©rifiez les lignes : 172, 174. AMDGPUInline.cpp 172

Pointeur Callee est déréférencé au moment de l'appel de la fonction getTTI.

Et il s'avĂšre ensuite que ce pointeur doit ĂȘtre vĂ©rifiĂ© pour Ă©galitĂ© nullptr:

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

Mais il est déjà trop tard


Fragment N13 — N
 : VĂ©rification du pointeur aprĂšs dĂ©rĂ©fĂ©rencement

La situation décrite dans le fragment précédent n'est pas unique. Elle se retrouve ici :

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

Avertissement PVS-Studio : V595 [CWE-476] Le pointeur 'CalleeFn' a Ă©tĂ© utilisĂ© avant d'ĂȘtre vĂ©rifiĂ© contre nullptr. VĂ©rifiez les lignes : 1079, 1081. SimplifyLibCalls.cpp 1079

Et ici :

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

Avertissement PVS-Studio : V595 [CWE-476] Le pointeur 'ND' a Ă©tĂ© utilisĂ© avant d'ĂȘtre vĂ©rifiĂ© contre nullptr. VĂ©rifiez les lignes : 532, 534. SemaTemplateInstantiateDecl.cpp 532

Et ici :

  • V595 [CWE-476] Le pointeur 'U' a Ă©tĂ© utilisĂ© avant d'ĂȘtre vĂ©rifiĂ© contre nullptr. VĂ©rifiez les lignes : 404, 407. DWARFFormValue.cpp 404
  • V595 [CWE-476] Le pointeur 'ND' a Ă©tĂ© utilisĂ© avant d'ĂȘtre vĂ©rifiĂ© contre nullptr. VĂ©rifiez les lignes : 2149, 2151. SemaTemplateInstantiate.cpp 2149

Puis, je n'ai pas trouvé intéressant d'étudier les avertissements numérotés V595. Donc, je ne sais pas s'il y a d'autres erreurs similaires en plus de celles mentionnées ici. Il est probable qu'il y en ait.

Fragment N17, N18 : Décalage suspect

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

Avertissement PVS-Studio : V629 [CWE-190] Envisagez d'examiner l'expression '~(Size — 1) << 1'. DĂ©calage de bits de la valeur 32 bits avec une expansion subsĂ©quente au type 64 bits. AArch64AddressingModes.h 260

Peut-ĂȘtre que ce n'est pas une erreur, et que le code fonctionne exactement comme prĂ©vu. Mais c'est clairement un endroit trĂšs suspect, et il doit ĂȘtre vĂ©rifiĂ©.

Supposons que la variable Taille vaille 16, et alors l'auteur du code prévoit d'obtenir dans la variable NImms la valeur :

1111111111111111111111111111111111111111111111111111111111100000

Cependant, en réalité, cela donnera la valeur :

0000000000000000000000000000000011111111111111111111111111100000

Le fait est que tous les calculs sont effectués en utilisant un type unsigned 32 bits. Et seulement ensuite, ce type sans signe de 32 bits sera implicitement étendu à uint64_t. Ainsi, les bits supérieurs seront nuls.

La situation peut ĂȘtre corrigĂ©e comme suit :

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

Situation similaire : V629 [CWE-190] Envisagez d'examiner l'expression 'Immr << 6'. Décalage de bits de la valeur 32 bits avec une expansion subséquente au type 64 bits. AArch64AddressingModes.h 269

Fragment N19 : Mot-clé manquant sinon?

void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
  ....
  if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
    // VOP2b (v_add_u32, v_sub_u32 ...) utilisation de 'vcc' token.
    // Ignorer.
    continue;
  } if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) {    // <=
    Op.addRegWithFPInputModsOperands(Inst, 2);
  } else if (Op.isDPPCtrl()) {
    Op.addImmOperands(Inst, 1);
  } else if (Op.isImm()) {
    // Gérer les arguments optionnels
    OptionalIdx[Op.getImmTy()] = I;
  } else {
    llvm_unreachable("Type d'opérande invalide");
  }
  ....
}

Avertissement PVS-Studio : V646 [CWE-670] Envisagez d'examiner la logique de l'application. Il est possible que le mot-clé 'else' soit manquant. AMDGPUAsmParser.cpp 5655

Il n'y a pas d'erreur ici. Car le bloc then du premier if se termine par continuer, alors peu importe, s'il y a un mot-clĂ© sinon ou non. De toute façon, le code fonctionnera de la mĂȘme maniĂšre. Cependant, un code omis sinon rend le code plus difficile Ă  comprendre et dangereux. Si Ă  l'avenir continuer cela disparaĂźt, le code commencera Ă  fonctionner de maniĂšre complĂštement diffĂ©rente. À mon avis, il vaut mieux ajouter sinon.

Fragment N20 : Quatre fautes de frappe similaires

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

Avertissements PVS-Studio :

  • V655 [CWE-480] Les chaĂźnes ont Ă©tĂ© concatĂ©nĂ©es mais ne sont pas utilisĂ©es. Pensez Ă  vĂ©rifier l'expression ‘Result + Name.str()’. Symbol.cpp 32
  • V655 [CWE-480] Les chaĂźnes ont Ă©tĂ© concatĂ©nĂ©es mais ne sont pas utilisĂ©es. Pensez Ă  vĂ©rifier l'expression ‘Result + «(ObjC Class) » + Name.str()’. Symbol.cpp 35
  • V655 [CWE-480] Les chaĂźnes ont Ă©tĂ© concatĂ©nĂ©es mais ne sont pas utilisĂ©es. Pensez Ă  vĂ©rifier l'expression ‘Result + «(ObjC Class EH) » + Name.str()’. Symbol.cpp 38
  • V655 [CWE-480] Les chaĂźnes ont Ă©tĂ© concatĂ©nĂ©es mais ne sont pas utilisĂ©es. Pensez Ă  vĂ©rifier l'expression ‘Result + «(ObjC IVar) » + Name.str()’. Symbol.cpp 41

Au lieu de l'opérateur +=, l'opérateur + est utilisé par accident. Cela donne des constructions dépourvues de sens.

Fragment N21 : Comportement indéfini

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

    SmallVector Ops;
    SplitString(AsmCondString, Ops, ",");
    assert(!Ops.empty() && "AssemblerCondString ne peut pas ĂȘtre vide");

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

Essayez de trouver le code dangereux par vous-mĂȘme. Et voici une image pour vous distraire, afin de ne pas regarder immĂ©diatement la rĂ©ponse :

Finding Bugs in LLVM 8 with PVS-Studio

Avertissement PVS-Studio : V708 [CWE-758] Une construction dangereuse est utilisĂ©e : ‘FeaturesMap[Op] = FeaturesMap.size()’, oĂč ‘FeaturesMap’ est de la classe ‘map’. Cela peut mener Ă  un comportement indĂ©fini. RISCVCompressInstEmitter.cpp 490

Ligne problématique :

FeaturesMap[Op] = FeaturesMap.size();

Si l'élément Op n'est pas trouvé, alors un nouvel élément est créé dans la carte et le nombre d'éléments dans cette carte est enregistré. Mais il est incertain si la fonction taille sera appelée avant ou aprÚs l'ajout du nouvel élément.

Fragment N22-N24 : Assignations répétées

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

Avertissement PVS-Studio : V519 [CWE-563] La variable ‘NType’ est assignĂ©e deux fois successivement. Il s'agit peut-ĂȘtre d'une erreur. VĂ©rifiez les lignes : 1663, 1664. MachOObjectFile.cpp 1664

Je ne pense pas qu'il y ait une vĂ©ritable erreur ici. C'est juste une assignation rĂ©pĂ©tĂ©e superflue. Mais c'est tout de mĂȘme une nĂ©gligence.

De mĂȘme :

  • V519 [CWE-563] La variable ‘B.NDesc’ est assignĂ©e deux fois successivement. Il s'agit peut-ĂȘtre d'une erreur. VĂ©rifiez les lignes : 1488, 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] La variable est assignĂ©e deux fois successivement. Il s'agit peut-ĂȘtre d'une erreur. VĂ©rifiez les lignes : 59, 61. coff2yaml.cpp 61

Fragment N25-N27 : Autres assignations répétées

Examinons maintenant un cas légÚrement différent d'assignation répétée.

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

Avertissement PVS-Studio : V519 [CWE-563] La variable ‘Alignment’ est assignĂ©e deux fois successivement. Il s'agit peut-ĂȘtre d'une erreur. VĂ©rifiez les lignes : 1158, 1160. LoadStoreVectorizer.cpp 1160

C'est un code trÚs étrange, qui semble contenir une erreur logique. Au début, la variable Alignment est assignée en fonction d'une condition. Puis il y a à nouveau une assignation, mais cette fois sans aucune vérification.

Des situations similaires peuvent ĂȘtre vues ici :

  • V519 [CWE-563] La variable ‘Effects’ est assignĂ©e deux fois successivement. Il s'agit peut-ĂȘtre d'une erreur. VĂ©rifiez les lignes : 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] La variable ‘ExpectNoDerefChunk’ est assignĂ©e deux fois successivement. Il s'agit peut-ĂȘtre d'une erreur. VĂ©rifiez les lignes : 4970, 4973. SemaType.cpp 4973

Fragment N28 : Condition toujours vraie

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) // Support d'instruction PAUSE             // <=
      break;
  }
  ....
}

Avertissement PVS-Studio : V547 [CWE-571] L'expression ‘nextByte != 0x90’ est toujours vraie. X86DisassemblerDecoder.cpp 379

La vérification n'a pas de sens. La variable nextByte est toujours différente de la valeur 0x90, ce qui découle de la vérification précédente. C'est une erreur logique.

Fragment N29 — N
 : Conditions toujours vraies/fausses

L'analyseur émet de nombreux avertissements sur le fait que toute condition (V547) ou une partie de celle-ci (V560) est toujours vrai ou faux. Souvent, ce ne sont pas de vraies erreurs, mais simplement un code négligent, le résultat du déploiement de macros, etc. Néanmoins, il est utile de examiner tous ces avertissements, car il y a parfois de véritables erreurs logiques. Par exemple, ce segment de code est suspect :

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

Avertissement PVS-Studio : V560 [CWE-570] Une partie de l'expression conditionnelle est toujours fausse : RegNo == 0xe. ARMDisassembler.cpp 939

La constante 0xE est la valeur 14 en base décimale. La vérification RegNo == 0xe n'a pas de sens, car si RegNo > 13, la fonction terminera son exécution.

Il y a eu de nombreux autres avertissements avec les identifiants V547 et V560, mais comme dans le cas de V595, il ne m'a pas intéressé d'étudier ces avertissements. Il était déjà clair que j'avais suffisamment de matériel pour écrire un article :). Il est donc inconnu combien d'autres erreurs de ce type on peut identifier dans LLVM avec PVS-Studio.

Je vais donner un exemple de pourquoi étudier ces déclenchements est ennuyeux. L'analyste a tout à fait raison de donner un avertissement sur le code suivant. Mais ce n'est pas une erreur.

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

Avertissement PVS-Studio : V547 [CWE-570] L'expression ‘!HasError’ est toujours fausse. UnwrappedLineParser.cpp 1635

Fragment N30 : Retour suspect

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

Avertissement PVS-Studio : V612 [CWE-670] Un ‘return’ inconditionnel dans une boucle. R600OptimizeVectorRegisters.cpp 63

C'est soit une erreur, soit une technique spécifique destinée à clarifier quelque chose pour les programmeurs lisant le code. Une telle construction ne m'éclaire en rien et semble trÚs suspecte. Mieux vaut ne pas écrire comme ça :).

Fatigué ? Alors, c'est le moment de faire du thé ou du café.

Finding Bugs in LLVM 8 with PVS-Studio

Défauts détectés par de nouveaux diagnostics

Je pense qu'une trentaine de déclenchements d'anciens diagnostics est suffisante. Voyons maintenant ce que l'on peut trouver d'intéressant avec les nouveaux diagnostics qui ont été ajoutés à l'analysateur aprÚs précédent la vérification. Pendant ce temps, 66 diagnostics généraux ont été ajoutés à l'analysateur C++.

Fragment N31 : Code inaccessible

Erreur CtorDtorRunner::run() {
  ....
  si (auto CtorDtorMap =
          ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
                    NoDependenciesToRegister, true))
  {
    ....
    return Error::success();
  } sinon
    return CtorDtorMap.takeError();

  CtorDtorsByPriority.clear();

  return Error::success();
}

Avertissement PVS-Studio : V779 [CWE-561] Code inaccessible détecté. Il est possible qu'une erreur soit présente. ExecutionUtils.cpp 146

Comme vous pouvez le voir, les deux branches de l'opérateur if se terminent par un appel à l'opérateur retourner. Par conséquent, le conteneur CtorDtorsByPriority ne sera jamais vidé.

Fragment N32: Code inaccessible

bool LLParser::ParseSummaryEntry() {
  ....
  switch (Lex.getKind()) {
  cas lltok::kw_gv:
    return ParseGVEntry(SummaryID);
  cas lltok::kw_module:
    return ParseModuleEntry(SummaryID);
  cas lltok::kw_typeid:
    return ParseTypeIdEntry(SummaryID);                        \/\/ <=
    break;                                                     \/\/ <=
  défaut:
    return Error(Lex.getLoc(), "type de résumé inattendu");
  }
  Lex.setIgnoreColonInIdentifiers(false);                      \/\/ <=
  return false;
}

Avertissement PVS-Studio: V779 [CWE-561] Code inaccessible détecté. Il est possible qu'une erreur soit présente. LLParser.cpp 835

Situation intéressante. Examinons d'abord cet endroit :

return ParseTypeIdEntry(SummaryID);
break;

À premiĂšre vue, il semble qu'il n'y ait pas d'erreur ici. Il semble que l'opĂ©rateur break soit superflu ici, et qu'il puisse simplement ĂȘtre supprimĂ©. Cependant, ce n'est pas si simple.

L'analyseur émet un avertissement sur les lignes :

Lex.setIgnoreColonInIdentifiers(false);
return false;

Et en effet, ce code est inaccessible. Tous les cas dans switch se terminent par l'appel de l'opĂ©rateur retourner. Et maintenant, le solitaire sans sens break ne semble plus si inoffensif ! Peut-ĂȘtre qu'une des branches devrait se terminer par break, et non par retourner?

Fragment N33: Réinitialisation aléatoire des bits supérieurs

unsigned getStubAlignment() override {
  si (Arch == Triple::systemz)
    return 8;
  sinon
    return 1;
}

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

Avertissement PVS-Studio : V784 La taille du masque de bits est inférieure à celle du premier opérande. Cela entraßnera la perte de bits supérieurs. RuntimeDyld.cpp 815

Remarquez que la fonction getStubAlignment retourne un type unsigned. Calculons la valeur de l'expression, en supposant que la fonction renvoie la valeur 8 :

~(getStubAlignment() - 1)

~(8u-1)

0xFFFFFFF8‬u

Maintenant, remarquez que la variable DataSize est de type entier non signĂ© 64 bits. Ainsi, lorsque l'opĂ©ration DataSize & 0xFFFFFFF8‬u est effectuĂ©e, tous les trente-deux bits supĂ©rieurs seront annulĂ©s. C'est probablement pas ce que le programmeur voulait. Je soupçonne qu'il voulait calculer : DataSize & 0xFFFFFFFFFFFFFFF8‬u.

Pour corriger l'erreur, il faut écrire comme suit :

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

Ou ainsi :

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

Fragment N34 : Conversion de type explicite échouée

template <typename T>
void scaleShuffleMask(int Scale, ArrayRef<T> Mask,
                      SmallVectorImpl<T> &ScaledMask) {
  assert(0 < Scale && "Facteur d'échelle inattendu");
  int NumElts = Mask.size();
  ScaledMask.assign(static_cast<size_t>(NumElts * Scale), -1);
  ....
}

Avertissement PVS-Studio : V1028 [CWE-190] Débordement possible. Envisagez de convertir les opérandes de l'opérateur 'NumElts * Scale' au type 'size_t', et non le résultat. X86ISelLowering.h 1577

La conversion de type explicite est utilisée pour éviter le débordement lors de la multiplication de variables de type. int. Cependant, ici, la conversion de type explicite ne protÚge pas contre le débordement. Au début, les variables seront multipliées, et ensuite le résultat 32 bits de la multiplication sera étendu au type. size_t.

Fragment N35 : Copy-Paste échoué

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] Deux fragments de code similaires ont Ă©tĂ© trouvĂ©s. Peut-ĂȘtre est-ce une faute de frappe et la variable 'Op1' devrait ĂȘtre utilisĂ©e Ă  la place de 'Op0'. InstCombineCompares.cpp 5507

Ce nouveau diagnostic intĂ©ressant identifie les situations oĂč un fragment de code a Ă©tĂ© copiĂ©, et dans lequel certains noms ont Ă©tĂ© modifiĂ©s, mais Ă  un endroit, cela n'a pas Ă©tĂ© corrigĂ©.

Notez que dans le deuxiĂšme bloc, on a modifiĂ© Op0 sur Op1. Mais Ă  un endroit, cela n'a pas Ă©tĂ© corrigĂ©. Cela devait probablement ĂȘtre Ă©crit comme suit :

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

Fragment N36 : Confusion dans les variables

struct Status {
  unsigned Mask;
  unsigned Mode;

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

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

Avertissement PVS-Studio : V1001 [CWE-563] La variable 'Mode' est assignée mais n'est pas utilisée à la fin de la fonction. SIModeRegister.cpp 48

Il est trĂšs dangereux de donner aux arguments des fonctions les mĂȘmes noms que ceux des membres de la classe. Il est trĂšs facile de se mĂ©prendre. Nous sommes justement dans ce cas. Cette expression n'a pas de sens :

Mode &= Mask;

L'argument de la fonction change. Et c'est tout. Cet argument n'est plus utilisĂ©. Cela aurait probablement dĂ» ĂȘtre Ă©crit comme suit :

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

Fragment N37 : Confusion dans les variables

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(Sym));
  Size += this->EntrySize;
}

Avertissement PVS-Studio : V1001 [CWE-563] La variable 'Size' est assignée mais n'est pas utilisée à la fin de la fonction. Object.cpp 424

La situation est similaire Ă  la prĂ©cĂ©dente. Il devrait ĂȘtre Ă©crit :

this->Size += this->EntrySize;

Fragment N38-N47 : Le pointeur n'a pas été vérifié

Nous avons prĂ©cĂ©demment examinĂ© des exemples de dĂ©clenchement de diagnostics V595. Son essence est que le pointeur est d'abord dĂ©fĂ©rencĂ©, puis vĂ©rifiĂ©. Un jeune diagnostic V1004 est opposĂ© Ă  celui-ci par son sens, mais dĂ©tecte Ă©galement beaucoup d'erreurs. Il met en Ă©vidence les situations oĂč le pointeur a d'abord Ă©tĂ© vĂ©rifiĂ©, puis oubliĂ© de le faire. Examinons de tels cas trouvĂ©s dans LLVM.

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

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

Avertissement PVS-Studio : V1004 [CWE-476] Le pointeur 'Ptr' a été utilisé de maniÚre non sécurisée aprÚs avoir été vérifié contre nullptr. Vérifiez les lignes : 729, 738. TargetTransformInfoImpl.h 738

Variable Ptr peut ĂȘtre Ă©gal Ă  nullptr, comme le montre la vĂ©rification :

if (Ptr != nullptr)

Cependant, ci-dessous ce pointeur est déjà déférencé sans vérification préalable :

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

Examinons un autre cas similaire.

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

Avertissement PVS-Studio : V1004 [CWE-476] Le pointeur 'FD' a été utilisé de maniÚre non sécurisée aprÚs avoir été vérifié contre nullptr. Vérifiez les lignes : 3228, 3231. CGDebugInfo.cpp 3231

Notez le pointeur FD. Je suis sûr que le problÚme est bien visible et qu'aucune explication spéciale n'est nécessaire.

Et encore :

static void computePolynomialFromPointer(Value & Ptr, Polynomial & Result,
                                         Value *& BasePtr,
                                         const DataLayout & DL) {
  PointerType *PtrTy = dyn_cast(Ptr.getType());
  if (!PtrTy) {                                                   // getPointerAddressSpace());     // <=
  ....
}

Avertissement PVS-Studio : V1004 [CWE-476] Le pointeur ‘PtrTy’ a Ă©tĂ© utilisĂ© de maniĂšre non sĂ©curisĂ©e aprĂšs avoir Ă©tĂ© vĂ©rifiĂ© par rapport Ă  nullptr. VĂ©rifiez les lignes : 960, 965. InterleavedLoadCombinePass.cpp 965

Comment se prémunir contre de telles erreurs ? Soyez plus attentif lors des revues de code et utilisez un analyseur statique comme PVS-Studio pour des vérifications réguliÚres du code.

Il n'est pas utile de fournir d'autres fragments de code contenant ce type d'erreurs. Je laisserai dans l'article uniquement la liste des avertissements :

  • V1004 [CWE-476] Le pointeur ‘Expr’ a Ă©tĂ© utilisĂ© de maniĂšre non sĂ©curisĂ©e aprĂšs avoir Ă©tĂ© vĂ©rifiĂ© par rapport Ă  nullptr. VĂ©rifiez les lignes : 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] Le pointeur ‘PI’ a Ă©tĂ© utilisĂ© de maniĂšre non sĂ©curisĂ©e aprĂšs avoir Ă©tĂ© vĂ©rifiĂ© par rapport Ă  nullptr. VĂ©rifiez les lignes : 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] Le pointeur ‘StatepointCall’ a Ă©tĂ© utilisĂ© de maniĂšre non sĂ©curisĂ©e aprĂšs avoir Ă©tĂ© vĂ©rifiĂ© par rapport Ă  nullptr. VĂ©rifiez les lignes : 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] Le pointeur ‘RV’ a Ă©tĂ© utilisĂ© de maniĂšre non sĂ©curisĂ©e aprĂšs avoir Ă©tĂ© vĂ©rifiĂ© par rapport Ă  nullptr. VĂ©rifiez les lignes : 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] Le pointeur ‘CalleeFn’ a Ă©tĂ© utilisĂ© de maniĂšre non sĂ©curisĂ©e aprĂšs avoir Ă©tĂ© vĂ©rifiĂ© par rapport Ă  nullptr. VĂ©rifiez les lignes : 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] Le pointeur ‘TC’ a Ă©tĂ© utilisĂ© de maniĂšre non sĂ©curisĂ©e aprĂšs avoir Ă©tĂ© vĂ©rifiĂ© par rapport Ă  nullptr. VĂ©rifiez les lignes : 1819, 1824. Driver.cpp 1824

Fragment N48-N60 : Pas critique, mais un défaut (risque de fuite de mémoire)

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

Avertissement PVS-Studio : V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur ‘Strategies’ par la mĂ©thode ’emplace_back’. Une fuite de mĂ©moire se produira en cas d'exception. llvm-isel-fuzzer.cpp 58

Pour ajouter un élément à la fin d'un conteneur de type std::vector<std::unique_ptr> il ne suffit pas d'écrire xxx.push_back(new X), car il n'y a pas de conversion implicite de X* dans std::unique_ptr.

Une solution courante consiste à écrire xxx.emplace_back(new X), car cela compile : la méthode emplace_back construit un élément directement à partir des arguments et peut donc utiliser des constructeurs explicites.

Ce n'est pas sûr. Si le vecteur est plein, il se produit une réallocation de mémoire. L'opération de réallocation peut échouer, ce qui entraßnerait une exception std::bad_alloc. Dans ce cas, le pointeur sera perdu et l'objet créé ne sera jamais supprimé.

Une solution sécurisée consiste à créer un unique_ptr, qui possédera le pointeur jusqu'à ce que le vecteur essaie de réallouer de la mémoire :

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

À partir de C++14, vous pouvez utiliser 'std::make_unique':

xxx.push_back(std::make_unique())

Ce type de dĂ©faut n'est pas critique pour LLVM. Si la mĂ©moire ne peut pas ĂȘtre allouĂ©e, le fonctionnement du compilateur sera simplement interrompu. Cependant, pour les applications avec un long temps de disponibilitĂ©, qui ne peuvent pas simplement s'arrĂȘter si la mĂ©moire n'a pas pu ĂȘtre allouĂ©e, cela peut ĂȘtre une vraie erreur dĂ©sagrĂ©able.

Ainsi, bien que ce code ne présente pas de danger pratique pour LLVM, j'ai jugé utile de parler de ce type de modÚle d'erreur et que l'analyseur PVS-Studio a appris à l'identifier.

D'autres avertissements de ce type :

  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Passes' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. PassManager.h 546
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'AAs' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. AliasAnalysis.h 324
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Entries' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'AllEdges' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. CFGMST.h 268
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'VMaps' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Records' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. FDRLogBuilder.h 30
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'PendingSubmodules' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. ModuleMap.cpp 810
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Objects' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. DebugMap.cpp 88
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Strategies' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Modifiers' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 685
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Modifiers' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 686
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Modifiers' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 688
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Modifiers' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 689
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Modifiers' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 690
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur 'Modifiers' par la mĂ©thode 'emplace_back'. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 691
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur ‘Modifiers’ par la mĂ©thode ’emplace_back’. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 692
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur ‘Modifiers’ par la mĂ©thode ’emplace_back’. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 693
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur ‘Modifiers’ par la mĂ©thode ’emplace_back’. Une fuite de mĂ©moire se produira en cas d'exception. llvm-stress.cpp 694
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur ‘Operands’ par la mĂ©thode ’emplace_back’. Une fuite de mĂ©moire se produira en cas d'exception. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur ‘Stash’ par la mĂ©thode ’emplace_back’. Une fuite de mĂ©moire se produira en cas d'exception. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Un pointeur sans propriĂ©taire est ajoutĂ© au conteneur ‘Matchers’ par la mĂ©thode ’emplace_back’. Une fuite de mĂ©moire se produira en cas d'exception. GlobalISelEmitter.cpp 2702

Conclusion

J'ai notĂ© au total 60 avertissements, aprĂšs quoi je me suis arrĂȘtĂ©. Y a-t-il d'autres dĂ©fauts que le vĂ©rificateur PVS-Studio dĂ©couvre dans LLVM ? Oui, en effet. Cependant, lorsque j'Ă©crivais des extraits de code pour l'article, il Ă©tait tard le soir, en fait, dĂ©jĂ  la nuit, et j'ai dĂ©cidĂ© qu'il Ă©tait temps de conclure.

J'espÚre que vous avez trouvé cela intéressant et que vous voudrez essayer le vérificateur PVS-Studio.

Vous pouvez télécharger le vérificateur et obtenir une clé d'essai sur cette page.

Surtout, utilisez une analyse statique réguliÚrement. Des vérifications ponctuelles, réalisées par nous dans le but de promouvoir la méthodologie de l'analyse statique et PVS-Studio, ne constituent pas un scénario normal.

Bonne chance pour améliorer la qualité et la fiabilité du code !

Finding Bugs in LLVM 8 with PVS-Studio

Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction : Andrey Karpov. Trouver des bugs dans LLVM 8 avec PVS-Studio.

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster