
Es sind mehr als zwei Jahre vergangen, seit die letzte Überprüfung des LLVM-Projekts mit unserem PVS-Studio-Analyzer erfolgt ist. Lassen Sie uns sicherstellen, dass der PVS-Studio-Analyzer nach wie vor das führende Werkzeug zur Identifizierung von Fehlern und potenziellen Sicherheitslücken ist. Dazu überprüfen wir und finden neue Fehler in der Version LLVM 8.0.0.
Ein Artikel, der geschrieben werden soll
Um ehrlich zu sein, wollte ich diesen Artikel nicht schreiben. Es macht keinen Spaß, über ein Projekt zu schreiben, das wir bereits mehrmals überprüft haben (, , ). Es wäre besser, über etwas Neues zu schreiben, aber ich habe keine Wahl.
Jedes Mal, wenn eine neue Version von LLVM erscheint oder , erhalten wir in unseren E-Mails Fragen in der folgenden Art:
Sehen Sie, die neue Version des Clang Static Analyzer kann neue Fehler finden! Ich habe das Gefühl, dass die Relevanz von PVS-Studio abnimmt. Clang findet mehr Fehler als früher und holt in Bezug auf die Funktionen von PVS-Studio auf. Was denken Sie darüber?
Darauf möchte ich immer etwas in der Art antworten:
Wir sitzen auch nicht untätig! Wir haben die Möglichkeiten des PVS-Studio-Analyzers erheblich verbessert. Machen Sie sich also keine Sorgen, wir bleiben wie gewohnt führend.
Leider ist das eine schlechte Antwort. Es gibt keine Beweise dafür. Und genau deshalb schreibe ich jetzt diesen Artikel. Das LLVM-Projekt wurde erneut überprüft und es wurden zahlreiche Fehler gefunden. Die, die ich interessant fand, werde ich jetzt zeigen. Diese Fehler kann Clang Static Analyzer nicht finden (oder es ist äußerst mühsam, dies mit ihm zu tun). Aber wir können es. Dazu habe ich alle diese Fehler an einem Abend gefunden und aufgelistet.
Das Schreiben des Artikels hat sich jedoch über mehrere Wochen hingezogen. Ich konnte mich einfach nicht dazu bringen, alles in Form von Text zu bringen :).
Übrigens, wenn Sie interessiert sind, welche Technologien im PVS-Studio-Analyzer zur Identifizierung von Fehlern und potenziellen Sicherheitslücken verwendet werden, dann lade ich Sie ein, sich mit dieser .
Neue und alte Diagnosen
Wie bereits erwähnt, wurde das LLVM-Projekt vor etwa zwei Jahren erneut überprüft, und die gefundenen Fehler wurden behoben. In diesem Artikel wird nun eine neue Reihe von Fehlern vorgestellt. Warum wurden neue Fehler gefunden? Dafür gibt es drei Gründe:
- Das LLVM-Projekt entwickelt sich weiter, der alte Code wird geändert und neuer Code entsteht. Natürlich gibt es in dem geänderten und neuen Code auch neue Fehler. Dies zeigt deutlich, dass eine statische Analyse regelmäßig und nicht nur sporadisch durchgeführt werden sollte. Unsere Artikel demonstrieren gut die Möglichkeiten des PVS-Studio-Analyzers, aber das hat nichts mit der Verbesserung der Codequalität und der Senkung der Kosten für die Fehlerbehebung zu tun. Verwenden Sie den statischen Code-Analyzer regelmäßig!
- Wir überarbeiten und verbessern bereits bestehende Diagnosen. Daher kann der Analyzer Fehler erkennen, die bei früheren Prüfungen nicht bemerkt wurden.
- Im PVS-Studio sind neue Diagnosen hinzugekommen, die es vor 2 Jahren noch nicht gab. Ich habe beschlossen, sie in einem separaten Abschnitt hervorzuheben, um die Entwicklung des PVS-Studio deutlich zu zeigen.
Vorfälle, die von Diagnosen aufgedeckt wurden, die vor 2 Jahren existierten
Fragment N1: Copy-Paste
static bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) {
if (Name == "addcarryx.u32" || // Hinzugefügt in 8.0
....
Name == "avx512.mask.cvtps2pd.128" || // Hinzugefügt in 7.0
Name == "avx512.mask.cvtps2pd.256" || // Hinzugefügt in 7.0
Name == "avx512.cvtusi2sd" || // Hinzugefügt in 7.0
Name.startswith("avx512.mask.permvar.") || // Hinzugefügt in 7.0 // <=
Name.startswith("avx512.mask.permvar.") || // Hinzugefügt in 7.0 // <=
Name == "sse2.pmulu.dq" || // Hinzugefügt in 7.0
Name == "sse41.pmuldq" || // Hinzugefügt in 7.0
Name == "avx2.pmulu.dq" || // Hinzugefügt in 7.0
....
}Warnung PVS-Studio: [CWE-570] Es gibt identische Teil-Ausdrücke ‘Name.startswith(«avx512.mask.permvar.»)’ links und rechts des ‘||’-Operators. AutoUpgrade.cpp 73
Es wird zweimal überprüft, ob der Name mit dem Substring «avx512.mask.permvar.» beginnt. Bei der zweiten Überprüfung wollte man offensichtlich etwas anderes schreiben, hat aber vergessen, den kopierten Text zu ändern.
Fragment N2: Tippfehler
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;
....
}Warnung PVS-Studio: V501 Es gibt identische Teil-Ausdrücke ‘CXNameRange_WantQualifier’ links und rechts des ‘|’-Operators. CIndex.cpp 7245
Aufgrund eines Tippfehlers wird dieselbe benannte Konstante zweimal verwendet. CXNameRange_WantQualifier.
Fragment N3: Verwirrung mit den Operatorprioritäten
int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
....
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
return 0;
....
}Warnung PVS-Studio: [CWE-783] Vielleicht funktioniert der ‘?:’-Operator anders als erwartet. Der ‘?:’-Operator hat eine niedrigere Priorität als der ‘==’-Operator. PPCTargetTransformInfo.cpp 404
Meiner Meinung nach ist das ein sehr schöner Fehler. Ja, ich weiß, dass ich seltsame Vorstellungen von Schönheit habe :.
Jetzt, gemäß , wird der Ausdruck wie folgt berechnet:
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0Aus praktischer Sicht hat diese Bedingung keinen Sinn, da sie auf folgende Weise verkürzt werden kann:
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())Dies ist ein klarer Fehler. Wahrscheinlich sollte 0/1 mit der Variable verglichen werden Index. Um den Code zu korrigieren, müssen Klammern um den ternären Operator hinzugefügt werden:
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))Übrigens ist der ternäre Operator sehr gefährlich und kann logische Fehler hervorrufen. Seien Sie sehr vorsichtig damit und sparen Sie nicht an runden Klammern. Dieses Thema habe ich , im Kapitel "Hüten Sie sich vor dem Operator ?: und schließen Sie ihn in runde Klammern ein".
Fragment N4, N5: Null-Zeiger
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("kann '") + LHS->getAsString() +
"' nicht in einen String umwandeln");
return nullptr;
}
....
}Warnung PVS-Studio: [CWE-476] Dereferenzierung des Nullzeigers 'LHS' könnte stattfinden. TGParser.cpp 2152
Wenn der Zeiger LHS null ist, sollte eine Warnung ausgegeben werden. Stattdessen findet jedoch die Dereferenzierung dieses Nullzeigers statt: LHS->getAsString().
Dies ist eine sehr typische Situation, in der ein Fehler im Fehlerbehandler verborgen ist, da niemand sie testet. Statische Analyzer überprüfen den gesamten erreichbaren Code, unabhängig davon, wie oft er verwendet wird. Dies ist ein sehr gutes Beispiel dafür, wie statische Analyse andere Test- und Fehlerschutzmethoden ergänzt.
Ein ähnlicher Fehler bei der Behandlung des Zeigers RHS wurde im folgenden Code gemacht: V522 [CWE-476] Dereferenzierung des Nullzeigers 'RHS' könnte stattfinden. TGParser.cpp 2186
Fragment N6: Verwendung des Zeigers nach der Verschiebung
static Expected
ExtractBlocks(....)
{
....
std::unique_ptr ProgClone = CloneModule(BD.getProgram(), VMap);
....
BD.setNewProgram(std::move(ProgClone)); // getFunction(MisCompFunctions[i].first); // <=
assert(NewF && "Funktion nicht gefunden??");
MiscompiledFunctions.push_back(NewF);
}
....
}Warnung PVS-Studio: V522 [CWE-476] Dereferenzierung des Nullzeigers 'ProgClone' könnte stattfinden. Miscompilation.cpp 601
Zu Beginn hört der intelligente Zeiger ProgClone auf, das Objekt zu besitzen:
BD.setNewProgram(std::move(ProgClone));Tatsächlich gehört es jetzt ProgClone — dies ist ein null-Zeiger. Daher sollte etwas weiter unten dereferenziert werden:
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);Aber in Wirklichkeit wird das nicht passieren! Beachten Sie, dass die Schleife tatsächlich nicht ausgeführt wird.
Zu Beginn wird der Container MiscompiledFunctions geleert:
MiscompiledFunctions.clear();Anschließend wird die Größe dieses Containers in der Schleifenbedingung verwendet:
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {Es ist leicht zu erkennen, dass die Schleife nicht gestartet wird. Ich denke, das ist auch ein Fehler, und der Code sollte anders geschrieben werden.
Es scheint, dass wir auf die berüchtigte Fehlerparität gestoßen sind! Ein Fehler verdeckt den anderen :).
Fragment N7: Verwendung des Zeigers nach der Verschiebung
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() << " Fehler bei der Ausführung dieser Pass-Sequenz"
<< " auf dem Eingabeprogramm!n";
BD.setNewProgram(std::move(Test)); // <=
BD.EmitProgressBitcode(*Test, "pass-error", false); // <=
if (Error E = BD.debugOptimizerCrash())
return std::move(E);
return false;
}
....
}Warnung PVS-Studio: V522 [CWE-476] Dereferenzierung des null Zeigers ‘Test’ könnte stattfinden. Miscompilation.cpp 709
Wieder die gleiche Situation. Zuerst wird der Inhalt des Objekts verschoben und anschließend wird es ohne Weiteres verwendet. Ich treffe immer häufiger auf diese Situation in Programmcodes, seit C++ die Semantik der Verschiebung eingeführt hat. Dafür liebe ich die Sprache C++! Es entstehen immer neue Möglichkeiten, sich selbst ins Bein zu schießen. Der PVS-Studio-Analysator hat immer Arbeit :).
Fragment N8: Nullzeiger
void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
uint32_t TypeId = Symbol.getTypeId();
auto Type = Symbol.getSession().getSymbolById(TypeId);
if (Type)
Printer << "";
else
Type->dump(*this);
}Warnung PVS-Studio: V522 [CWE-476] Dereferenzierung des null Zeigers ‘Type’ könnte stattfinden. PrettyFunctionDumper.cpp 233
Neben Fehlerbehandlungsroutinen werden oft auch Funktionen zur Debug-Ausgabe von Daten nicht getestet. Hier haben wir einen solchen Fall. Die Funktion wartet auf den Benutzer, der anstelle seiner Probleme zu lösen, gezwungen sein wird, sie zu beheben.
Richtig:
if (Type)
Type->dump(*this);
else
Printer << "";Fragment N9: Nullzeiger
void SearchableTableEmitter::collectTableEntries(
GenericTable &Table, const std::vector &Items) {
....
RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
if (!Ty) // getAsString() + " vs. " + // getType()->getAsString());
....
}Warnung PVS-Studio: V522 [CWE-476] Dereferenzierung des Nullzeigers ‘Ty’ könnte stattfinden. SearchableTableEmitter.cpp 614
Ich denke, das ist bereits klar und bedarf keiner Erläuterung.
Fragment N10: Tippfehler
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;
}Warnung PVS-Studio: Die Variable ‘Identifier->Type’ wird sich selbst zugewiesen. FormatTokenLexer.cpp 249
Es macht keinen Sinn, eine Variable sich selbst zuzuweisen. Wahrscheinlich wollte man schreiben:
Identifier->Type = Question->Type;Fragment N11: Verdächtiger break
void SystemZOperand::print(raw_ostream &OS) const {
switch (Kind) {
break;
case KindToken:
OS << "Token:" << getToken();
break;
case KindReg:
OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
break;
....
}Warnung PVS-Studio: [CWE-478] Ziehen Sie in Betracht, die ‘switch’-Anweisung zu überprüfen. Es ist möglich, dass der erste ‘case’-Operator fehlt. SystemZAsmParser.cpp 652
Zu Beginn gibt es einen sehr verdächtigen Operator break. Haben wir hier vielleicht etwas vergessen zu schreiben?
Fragment N12: Überprüfung des Zeigers nach Dereferenzierung
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("undefinierter callee");
....
}Warnung PVS-Studio: [CWE-476] Der ‘Callee’-Zeiger wurde verwendet, bevor er auf nullptr überprüft wurde. Überprüfen Sie die Zeilen: 172, 174. AMDGPUInline.cpp 172
Zeiger Callee wird zu Beginn dereferenziert, wenn die Funktion aufgerufen wird getTTI.
Und dann stellt sich heraus, dass dieser Zeiger auf Gleichheit überprüft werden sollte nullptr:
if (!Callee || Callee->isDeclaration())Aber es ist schon zu spät...
Fragment N13 — N…: Überprüfung des Zeigers nach Dereferenzierung
Die im vorherigen Codefragment beschriebene Situation ist nicht einzigartig. Sie tritt hier auf:
static Value *optimizeDoubleFP(CallInst *CI, IRBuilder &B,
bool isBinary, bool isPrecise = false) {
....
Function *CalleeFn = CI->getCalledFunction();
StringRef CalleeNm = CalleeFn->getName(); // getAttributes();
if (CalleeFn && !CalleeFn->isIntrinsic()) { // <=
....
}Warnung PVS-Studio: V595 [CWE-476] Der ‘CalleeFn’-Zeiger wurde verwendet, bevor er auf nullptr überprüft wurde. Überprüfen Sie die Zeilen: 1079. 1081. SimplifyLibCalls.cpp 1079
Und hier:
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()); // <=
....
}Warnung PVS-Studio: V595 [CWE-476] Der Pointer ‚ND‘ wurde genutzt, bevor er auf nullptr geprüft wurde. Überprüfen Sie die Zeilen: 532, 534. SemaTemplateInstantiateDecl.cpp 532
Und hier:
- V595 [CWE-476] Der Pointer ‚U‘ wurde genutzt, bevor er auf nullptr geprüft wurde. Überprüfen Sie die Zeilen: 404, 407. DWARFFormValue.cpp 404
- V595 [CWE-476] Der Pointer ‚ND‘ wurde genutzt, bevor er auf nullptr geprüft wurde. Überprüfen Sie die Zeilen: 2149, 2151. SemaTemplateInstantiate.cpp 2149
Danach war es mir nicht mehr interessant, Warnungen mit der Nummer V595 zu studieren. Daher weiß ich nicht, ob es noch ähnliche Fehler gibt, abgesehen von den hier aufgeführten. Wahrscheinlich gibt es welche.
Fragment N17, N18: Verdächtiger Shift
static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
uint64_t &Encoding) {
....
unsigned Size = RegSize;
....
uint64_t NImms = ~(Size-1) << 1;
....
}Warnung PVS-Studio: [CWE-190] Erwägen Sie, den Ausdruck ‚~(Size — 1) << 1‘ zu überprüfen. Bitverschiebung des 32-Bit-Wertes mit einer anschließenden Erweiterung auf den 64-Bit-Typ. AArch64AddressingModes.h 260
Möglicherweise ist das kein Fehler, und der Code funktioniert genau so, wie vorgesehen. Aber es ist eindeutig ein sehr verdächtiger Punkt, und er sollte überprüft werden.
Angenommen, die Variable Size ist 16, und der Autor des Codes wollte in der Variable NImms den Wert erhalten:
1111111111111111111111111111111111111111111111111111111111100000
Tatsächlich wird jedoch der Wert erhalten:
0000000000000000000000000000000011111111111111111111111111100000
Das Problem ist, dass alle Berechnungen unter Verwendung des 32-Bit-unsigned-Typs durchgeführt werden. Und erst danach wird dieser 32-Bit-unsigned-Typ implizit auf uint64_t. Dabei sind die höheren Bits null.
Die Situation kann so korrigiert werden:
uint64_t NImms = ~static_cast(Size-1) << 1;Ähnliche Situation: V629 [CWE-190] Erwägen Sie, den Ausdruck ‚Immr << 6‘ zu überprüfen. Bitverschiebung des 32-Bit-Wertes mit einer anschließenden Erweiterung auf den 64-Bit-Typ. AArch64AddressingModes.h 269
Fragment N19: Schlüsselwort fehlt else?
void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
....
if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
// VOP2b (v_add_u32, v_sub_u32 ...) dpp verwendet das Token "vcc".
// Überspringen.
continue;
} if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) { // <=
Op.addRegWithFPInputModsOperands(Inst, 2);
} else if (Op.isDPPCtrl()) {
Op.addImmOperands(Inst, 1);
} else if (Op.isImm()) {
// optionale Argumente behandeln
OptionalIdx[Op.getImmTy()] = I;
} else {
llvm_unreachable("Ungültiger Operandtyp");
}
....
}Warnung PVS-Studio: [CWE-670] Erwägen Sie, die Logik der Anwendung zu überprüfen. Es ist möglich, dass das Schlüsselwort ‚else‘ fehlt. AMDGPUAsmParser.cpp 5655
Hier gibt es keinen Fehler. Da der then-Block des ersten if am Ende ist continue, es ist unwichtig, ob es ein Schlüsselwort gibt else oder nicht. In jedem Fall wird der Code gleich funktionieren. Dennoch macht ein fehlender else den Code unverständlicher und gefährlicher. Wenn er später continue verschwindet, wird der Code ganz anders funktionieren. Meiner Meinung nach ist es besser, else.
Fragment N20: Vier gleiche Tippfehler
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 Klasse) " + Name.str(); \/\/ <=
break;
case SymbolKind::ObjectiveCClassEHType:
Result + "(ObjC Klasse EH) " + Name.str(); \/\/ <=
break;
case SymbolKind::ObjectiveCInstanceVariable:
Result + "(ObjC IVar) " + Name.str(); \/\/ <=
break;
}
OS << Result;
}PVS-Studio Warnungen:
- V655 [CWE-480] Die Strings wurden zusammengefügt, aber nicht verwendet. Überprüfen Sie den Ausdruck ‚Result + Name.str()‘. Symbol.cpp 32
- V655 [CWE-480] Die Strings wurden zusammengefügt, aber nicht verwendet. Überprüfen Sie den Ausdruck ‚Result + „(ObjC Klasse) ” + Name.str()‘. Symbol.cpp 35
- V655 [CWE-480] Die Strings wurden zusammengefügt, aber nicht verwendet. Überprüfen Sie den Ausdruck ‚Result + „(ObjC Klasse EH) ” + Name.str()‘. Symbol.cpp 38
- V655 [CWE-480] Die Strings wurden zusammengefügt, aber nicht verwendet. Überprüfen Sie den Ausdruck ‚Result + „(ObjC IVar) ” + Name.str()‘. Symbol.cpp 41
Aus Versehen wird der Operator += anstelle des Operators + verwendet. Dadurch entstehen konstruktionen ohne Sinn.
Fragment N21: Unbestimmtes Verhalten
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 darf nicht leer sein");
for (auto &Op : Ops) {
assert(!Op.empty() && "Leerer Operator");
if (FeaturesMap.find(Op) == FeaturesMap.end())
FeaturesMap[Op] = FeaturesMap.size();
}
}
}Versuchen Sie, gefährlichen Code selbst zu finden. Das hier ist ein Bild zur Ablenkung, damit Sie nicht sofort die Antwort sehen:

Warnung PVS-Studio: [CWE-758] Gefährliche Konstruktion wird verwendet: ‚FeaturesMap[Op] = FeaturesMap.size()‘, wobei ‚FeaturesMap‘ der Klasse ‚map‘ angehört. Dies kann zu unbestimmtem Verhalten führen. RISCVCompressInstEmitter.cpp 490
Problematische Zeile:
FeaturesMap[Op] = FeaturesMap.size();Wenn das Element Op nicht gefunden wird, wird ein neues Element in der Karte erstellt und die Anzahl der Elemente in dieser Karte wird dort gespeichert. Nur ist unklar, ob die Funktion Größe vor oder nach dem Hinzufügen des neuen Elements aufgerufen wird.
Fragment N22-N24: Wiederholte Zuweisungen
Fehler 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;
}
....
}Warnung PVS-Studio: [CWE-563] Die Variable „NType“ wird nacheinander zweimal zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 1663, 1664. MachOObjectFile.cpp 1664
Ich denke, dass hier kein echter Fehler vorliegt. Es ist einfach eine überflüssige doppelte Zuweisung. Trotzdem ist es ein Patzer.
Ähnlich:
- V519 [CWE-563] Die Variable „B.NDesc“ wird nacheinander zweimal zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] Die Variable wird nacheinander zweimal zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 59, 61. coff2yaml.cpp 61
Fragment N25-N27: Weitere doppelte Zuweisungen
Betrachten wir nun eine etwas andere Variante der doppelten Zuweisung.
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;
....
}Warnung PVS-Studio: V519 [CWE-563] Die Variable „Alignment“ wird nacheinander zweimal zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 1158, 1160. LoadStoreVectorizer.cpp 1160
Das ist ein sehr seltsamer Code, der offensichtlich einen logischen Fehler enthält. Zunächst wird der Variable Alignment ein Wert abhängig von einer Bedingung zugewiesen. Dann erfolgt jedoch erneut eine Zuweisung, diesmal jedoch ohne jede Prüfung.
Ähnliche Situationen sind hier zu sehen:
- V519 [CWE-563] Die Variable „Effects“ wird nacheinander zweimal zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] Die Variable „ExpectNoDerefChunk“ wird nacheinander zweimal zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 4970, 4973. SemaType.cpp 4973
Fragment N28: Immer wahre Bedingung
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-Befehl Unterstützung // <=
break;
}
....
}Warnung PVS-Studio: [CWE-571] Der Ausdruck „nextByte != 0x90“ ist immer wahr. X86DisassemblerDecoder.cpp 379
Die Überprüfung macht keinen Sinn. Die Variable nextByte ist immer ungleich dem Wert 0x90, was sich aus der vorherigen Überprüfung ableitet. Das ist ein logischer Fehler.
Fragment N29 — N…: Immer wahre/falsche Bedingungen
Der Analyzer gibt viele Warnungen aus, dass die gesamte Bedingung () oder ein Teil davon () immer wahr oder falsch. Oft sind das keine echten Fehler, sondern einfach unordentlicher Code, das Ergebnis von Makrobereitstellungen und ähnlichem. Dennoch macht es Sinn, all diese Warnungen zu betrachten, da gelegentlich echte Logikfehler auftreten. Zum Beispiel ist dieser Codeabschnitt verdächtig:
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;
....
}Warnung PVS-Studio: [CWE-570] Ein Teil des bedingten Ausdrucks ist immer falsch: RegNo == 0xe. ARMDisassembler.cpp 939
Die Konstante 0xE ist der Wert 14 im Dezimalsystem. Die Überprüfung RegNo == 0xe macht keinen Sinn, da wenn RegNo > 13, die Funktion ihre Ausführung beendet.
Es gab viele andere Warnungen mit der Kennung V547 und V560, aber wie bei , war es für mich uninteressant, diese Warnungen zu изучать. Es war auch so klar, dass ich genug Material für den Artikel hatte :). Daher ist unklar, wie viele solcher Fehler in LLVM mit PVS-Studio gefunden werden können.
Ich gebe ein Beispiel, warum das Studium dieser Auslösungen langweilig ist. Der Analysator hat vollkommen recht, eine Warnung für den folgenden Code auszugeben. Aber das ist kein Fehler.
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
tok::TokenKind ClosingBraceKind) {
bool HasError = false;
....
HasError = true;
if (!ContinueOnSemicolons)
return !HasError;
....
}PVS-Studio-Warnung: V547 [CWE-570] Ausdruck ‘!HasError’ ist immer falsch. UnwrappedLineParser.cpp 1635
Fragment N30: Verdächtiger return
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();
}
....
}Warnung PVS-Studio: [CWE-670] Ein bedingungsloser ‘return’ innerhalb einer Schleife. R600OptimizeVectorRegisters.cpp 63
Das ist entweder ein Fehler oder ein spezifisches Verfahren, das etwas den Programmierern, die den Code lesen, erläutern soll. Mir erklärt eine solche Konstruktion nichts und sieht sehr verdächtig aus. Es ist besser, so nicht zu schreiben :).
Müde? Dann ist es Zeit, Tee oder Kaffee zuzubereiten.

Fehler, die durch neue Diagnosen festgestellt wurden
Ich denke, 30 Auslösungen der alten Diagnosen sind genug. Lassen Sie uns jetzt schauen, was Interessantes mit den neuen Diagnosen gefunden werden kann, die im Analysator nach Überprüfung hinzugefügt wurden. Insgesamt hat der C++-Analysator in dieser Zeit 66 Diagnosen allgemeiner Art hinzugefügt.
Fragment N31: Unerreichbarer Code
Fehler CtorDtorRunner::run() {
....
wenn (auto CtorDtorMap =
ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
NoDependenciesToRegister, true))
{
....
return Error::success();
} sonst
return CtorDtorMap.takeError();
CtorDtorsByPriority.clear();
return Error::success();
}Warnung PVS-Studio: [CWE-561] Unerreichbarer Code erkannt. Es ist möglich, dass ein Fehler vorliegt. ExecutionUtils.cpp 146
Wie Sie sehen können, enden beide Äste des Operators if mit einem Aufruf des Operators return. Dementsprechend wird der Container CtorDtorsByPriority niemals geleert.
Fragment N32: Unerreichbarer Code
bool LLParser::ParseSummaryEntry() {
....
switch (Lex.getKind()) {
Fall lltok::kw_gv:
return ParseGVEntry(SummaryID);
Fall lltok::kw_module:
return ParseModuleEntry(SummaryID);
Fall lltok::kw_typeid:
return ParseTypeIdEntry(SummaryID); \/\/ <=
break; \/\/ <=
standard:
return Error(Lex.getLoc(), "unerwarteter Zusammenfassungsart");
}
Lex.setIgnoreColonInIdentifiers(false); \/\/ <=
return false;
}Warnung PVS-Studio: V779 [CWE-561] Unerreichbarer Code erkannt. Es ist möglich, dass ein Fehler vorliegt. LLParser.cpp 835
Eine interessante Situation. Lassen Sie uns zunächst diesen Punkt betrachten:
return ParseTypeIdEntry(SummaryID);
break;Auf den ersten Blick scheint es, als gäbe es hier keinen Fehler. Es scheint, dass der Operator break hier überflüssig ist und einfach gelöscht werden kann. Aber es ist nicht so einfach.
Der Analyzer gibt eine Warnung für die Zeilen aus:
Lex.setIgnoreColonInIdentifiers(false);
return false;Und tatsächlich ist dieser Code unerreichbar. Alle Fälle in switch enden mit einem Aufruf des Operators return. Und jetzt sieht der sinnlose einsame break nicht mehr so harmlos aus! Vielleicht sollte einer der Äste mit dem enden break, und nicht mit return?
Fragment N33: Zufälliges Zurücksetzen der höheren Bits
unsigned getStubAlignment() override {
wenn (Arch == Triple::systemz)
return 8;
sonst
return 1;
}
Erwartet
RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
const SectionRef &Section,
bool IsCode) {
....
uint64_t DataSize = Section.getSize();
....
wenn (StubBufSize > 0)
DataSize &= ~(getStubAlignment() - 1);
....
}Warnung PVS-Studio: Die Größe der Bitmaske ist kleiner als die Größe des ersten Operanden. Dies wird den Verlust höherer Bits verursachen. RuntimeDyld.cpp 815
Beachten Sie, dass die Funktion getStubAlignment den Typ unsigned. Lassen Sie uns den Wert des Ausdrucks berechnen, wenn wir annehmen, dass die Funktion den Wert 8 zurückgibt:
~(getStubAlignment() - 1)
~(8u-1)
0xFFFFFFF8u
Beachten Sie nun, dass die Variable DataSize einen 64-Bit-unsigned Typ hat. Das bedeutet, dass bei der Ausführung der Operation DataSize & 0xFFFFFFF8u alle dreißig zwei höheren Bits zurückgesetzt werden. Wahrscheinlich ist das nicht das, was der Programmierer wollte. Ich vermute, er wollte berechnen: DataSize & 0xFFFFFFFFFFFFFFF8u.
Um den Fehler zu beheben, sollte man es so schreiben:
DataSize &= ~(static_cast(getStubAlignment()) - 1);Oder so:
DataSize &= ~(getStubAlignment() - 1ULL);Fragment N34: Fehlgeschlagenes explizites Typcasting
template <typename T>
void scaleShuffleMask(int Scale, ArrayRef<T> Mask,
SmallVectorImpl<T> &ScaledMask) {
assert(0 < Scale && "Unerwarteter Skalierungsfaktor");
int NumElts = Mask.size();
ScaledMask.assign(static_cast<size_t>(NumElts * Scale), -1);
....
}Warnung PVS-Studio: [CWE-190] Mögliche Überlaufgefahr. Ziehen Sie in Betracht, die Operanden des ‚NumElts * Scale‘-Operators in den ‚size_t‘-Typ zu ändern, nicht das Ergebnis. X86ISelLowering.h 1577
Explizites Typcasting wird verwendet, um Überläufe beim Multiplizieren von Variablen zu vermeiden. intHier schützt das explizite Typcasting jedoch nicht vor Überläufen. Zunächst werden die Variablen multipliziert, und erst danach wird das 32-Bit-Ergebnis der Multiplikation auf den Typ erweitert. .
Fragment N35: Fehlgeschlagenes Copy-Paste
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] Zwei ähnliche Codefragmente wurden gefunden. Vielleicht handelt es sich um einen Schreibfehler und die ‚Op1‘-Variable sollte anstelle von ‚Op0‘ verwendet werden. InstCombineCompares.cpp 5507
Diese neue interessante Diagnose identifiziert Situationen, in denen ein Codefragment kopiert wurde und einige Namen geändert wurden, aber an einer Stelle nicht korrigiert wurde.
Bitte beachten Sie, dass im zweiten Block geändert wurde Op0 auf Op1. Aber an einer Stelle wurde es nicht korrigiert. Wahrscheinlich hätte es so geschrieben werden sollen:
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
return &I;
}Fragment N36: Verwirrung mit den Variablen
struct Status {
unsigned Mask;
unsigned Mode;
Status() : Mask(0), Mode(0){};
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
Mode &= Mask;
};
....
};Warnung PVS-Studio: [CWE-563] Die ‚Mode‘-Variable wird zugewiesen, aber am Ende der Funktion nicht verwendet. SIModeRegister.cpp 48
Es ist sehr gefährlich, den Funktionsparametern die gleichen Namen wie den Klassenmitgliedern zu geben. Es ist sehr einfach, sich zu verwirren. Dies ist genau ein solcher Fall. Dieser Ausdruck ergibt keinen Sinn:
Mode &= Mask;Der Funktionsparameter wird geändert. Und das war's. Dieser Parameter wird nicht weiter verwendet. Wahrscheinlich hätte es so geschrieben werden sollen:
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
this->Mode &= Mask;
};Fragment N37: Verwirrung mit den Variablen
class SectionBase {
....
uint64_t Größe = 0;
....
};
class SymbolTableSection : public SectionBase {
....
};
void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
SectionBase *DefinedIn, uint64_t Wert,
uint8_t Sichtbarkeit, uint16_t Shndx,
uint64_t Größe) {
....
Sym.Wert = Wert;
Sym.Sichtbarkeit = Sichtbarkeit;
Sym.Größe = Größe;
Sym.Index = Symbole.size();
Symbole.emplace_back(llvm::make_unique(Sym));
Größe += this->Eintragsgröße;
}Warnung PVS-Studio: V1001 [CWE-563] Die Variable ‘Größe’ wird zugewiesen, aber am Ende der Funktion nicht verwendet. Object.cpp 424
Die Situation ist analog zur vorherigen. Es sollte geschrieben werden:
this->Größe += this->Eintragsgröße;Fragment N38-N47: Der Zeiger wurde vergessen zu überprüfen
Früher haben wir Beispiele für das Auslösen der Diagnose betrachtet . Ihr Wesen besteht darin, dass der Zeiger zu Beginn dereferenziert wird, und erst dann wird überprüft. Neuartige Diagnose ist im Sinne umgekehrt, entdeckt jedoch ebenfalls sehr viele Fehler. Sie erfasst Situationen, in denen der Zeiger zu Beginn überprüft wurde, aber danach vergessen wurde dies zu tun. Lassen Sie uns solche Fälle betrachten, die innerhalb von LLVM gefunden wurden.
int getGEPCost(Type *PointeeType, const Value *Ptr,
ArrayRef Operanden) {
....
if (Ptr != nullptr) { // <=
assert(....);
BaseGV = dyn_cast(Ptr->stripPointerCasts());
}
bool HatBaseReg = (BaseGV == nullptr);
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType()); // <=
....
}Warnung PVS-Studio: V1004 [CWE-476] Der Zeiger ‘Ptr’ wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 729, 738. TargetTransformInfoImpl.h 738
Variable Ptr kann gleich sein nullptr, was durch die Überprüfung angezeigt wird:
if (Ptr != nullptr)Jedoch wird dieser Zeiger weiter unten bereits ohne vorherige Überprüfung dereferenziert:
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());Betrachten wir einen anderen ähnlichen Fall.
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(); // <=
....
}Warnung PVS-Studio: V1004 [CWE-476] Der Zeiger ‘FD’ wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 3228, 3231. CGDebugInfo.cpp 3231
Beachten Sie den Zeiger FD. Ich bin mir sicher, dass das Problem gut sichtbar ist und keine speziellen Erklärungen benötigt werden.
Und noch:
static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result,
Value *&BasePtr,
const DataLayout &DL) {
PointerType *PtrTy = dyn_cast(Ptr.getType());
if (!PtrTy) { // getPointerAddressSpace()); // <=
....
}Warnung PVS-Studio: V1004 [CWE-476] Der 'PtrTy'-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 960, 965. InterleavedLoadCombinePass.cpp 965
Wie kann man sich vor solchen Fehlern schützen? Seien Sie vorsichtiger beim Code-Review und verwenden Sie einen statischen Analyzer wie PVS-Studio für regelmäßige Codeüberprüfungen.
Es macht keinen Sinn, andere Codefragmente mit solchen Fehlern zu zeigen. Ich lasse in dem Artikel nur die Liste der Warnungen stehen:
- V1004 [CWE-476] Der 'Expr'-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] Der 'PI'-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] Der 'StatepointCall'-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] Der 'RV'-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 2263, 2268. TGParser.cpp 2268
- V1004 [CWE-476] Der 'CalleeFn'-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 1081, 1096. SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] Der 'TC'-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 1819, 1824. Driver.cpp 1824
Fragment N48-N60: Nicht kritisch, aber ein Defekt (möglicher Speicherleck)
std::unique_ptr createISelMutator() {
....
std::vector<std::unique_ptr> Strategies;
Strategies.emplace_back(
new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
....
}Warnung PVS-Studio: [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Strategies' hinzugefügt. Ein Speicherleck tritt im Falle einer Ausnahme auf. llvm-isel-fuzzer.cpp 58
Um ein Element am Ende eines Containers des Typs hinzuzufügen std::vector<std::unique_ptr> kann man nicht einfach schreiben xxx.push_back(new X), da es keine implizite Umwandlung von X* in std::unique_ptr.
Eine gängige Lösung ist es, xxx.emplace_back(new X), da es kompiliert: die Methode emplace_back konstruiert das Element direkt aus den Argumenten und kann daher explizite Konstruktoren verwenden.
Das ist unsicher. Wenn der Vektor voll ist, erfolgt eine Speicherneuvergabe. Die Speicherneuvergabe kann fehlschlagen, was zu einer Ausnahme führen kann std::bad_alloc. In diesem Fall geht der Zeiger verloren und das erstellte Objekt wird niemals gelöscht.
Eine sichere Lösung ist die Erstellung eines unique_ptr, der den Zeiger besitzt, bevor der Vektor versucht, den Speicher neu zuzuweisen:
xxx.push_back(std::unique_ptr(new X))Seit C++14 kann 'std::make_unique' verwendet werden:
xxx.push_back(std::make_unique())Diese Art von Fehler ist für LLVM nicht kritisch. Wenn der Speicher nicht zugewiesen werden kann, wird die Arbeit des Compilers einfach gestoppt. Für Anwendungen mit langem , die nicht einfach beendet werden können, wenn der Speicher nicht zugewiesen werden konnte, kann dies jedoch ein ernsthaftes Problem darstellen.
Obwohl dieser Code also keine praktische Gefahr für LLVM darstellt, hielt ich es für nützlich, dieses Fehler-Muster zu besprechen und dass der PVS-Studio-Analyzer gelernt hat, es zu erkennen.
Weitere Warnungen dieses Typs:
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Passes' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. PassManager.h 546
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'AAs' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. AliasAnalysis.h 324
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Entries' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. DWARFDebugFrame.cpp 519
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'AllEdges' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. CFGMST.h 268
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'VMaps' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Records' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. FDRLogBuilder.h 30
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'PendingSubmodules' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. ModuleMap.cpp 810
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Objects' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. DebugMap.cpp 88
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Strategies' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. llvm-stress.cpp 685
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. llvm-stress.cpp 686
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. llvm-stress.cpp 688
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. llvm-stress.cpp 689
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. llvm-stress.cpp 690
- V1023 [CWE-460] Ein Zeiger ohne Besitzer wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Es tritt ein Speicherleck auf, wenn eine Ausnahme auftritt. llvm-stress.cpp 691
- V1023 [CWE-460] Ein Besitzer-loser Zeiger wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Bei einer Ausnahme tritt ein Speicherleck auf. llvm-stress.cpp 692
- V1023 [CWE-460] Ein Besitzer-loser Zeiger wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Bei einer Ausnahme tritt ein Speicherleck auf. llvm-stress.cpp 693
- V1023 [CWE-460] Ein Besitzer-loser Zeiger wird durch die Methode 'emplace_back' zum Container 'Modifiers' hinzugefügt. Bei einer Ausnahme tritt ein Speicherleck auf. llvm-stress.cpp 694
- V1023 [CWE-460] Ein Besitzer-loser Zeiger wird durch die Methode 'emplace_back' zum Container 'Operands' hinzugefügt. Bei einer Ausnahme tritt ein Speicherleck auf. GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] Ein Besitzer-loser Zeiger wird durch die Methode 'emplace_back' zum Container 'Stash' hinzugefügt. Bei einer Ausnahme tritt ein Speicherleck auf. GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] Ein Besitzer-loser Zeiger wird durch die Methode 'emplace_back' zum Container 'Matchers' hinzugefügt. Bei einer Ausnahme tritt ein Speicherleck auf. GlobalISelEmitter.cpp 2702
Fazit
Insgesamt habe ich 60 Warnungen aufgeschrieben und dann aufgehört. Gibt es andere Defekte, die der PVS-Studio-Analyzer in LLVM erkennt? Ja, gibt es. Als ich jedoch die Codefragmente für den Artikel aufschrieb, war es spät am Abend, besser gesagt, bereits Nacht, und ich beschloss, dass es Zeit zum Aufhören sei.
Ich hoffe, es hat Ihnen gefallen und Sie möchten den PVS-Studio-Analyzer ausprobieren.
Sie können den Analyzer herunterladen und einen Testschlüssel auf .
Das Wichtigste ist, verwenden Sie die statische Analyse regelmäßig. Einmalige Überprüfungen, die wir durchführen, um die Methodik der statischen Analyse und PVS-Studio bekannt zu machen, sind kein normales Szenario.
Viel Erfolg bei der Verbesserung der Qualität und Zuverlässigkeit Ihres Codes!
Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Andrey Karpov. .
Quelle: habr.com
