
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 (, , ). 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 , 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 .
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 :
- 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 !
- 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.
- 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 : [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 : [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 , l'expression est calculée comme suit :
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0D'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 , 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 : [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 : 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 : [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 : [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 : [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 : [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 :

Avertissement PVS-Studio : [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 : [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 : [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 () ou une partie de celle-ci () 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 : [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 , 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 : [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é.

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 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 : [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 : 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 : [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. .
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;
}
....
}[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 : [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 . Son essence est que le pointeur est d'abord dĂ©fĂ©rencĂ©, puis vĂ©rifiĂ©. Un jeune diagnostic 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 : [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 , 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 .
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 !
Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction : Andrey Karpov. .
Source : habr.com
