
Ka kaluar më shumë se dy vjet nga kontrolli i fundit i kodit të projektit LLVM me analizuesin tonë PVS-Studio. Le të sigurohemi që analizuesi PVS-Studio mbetet instrumenti kryesor për identifikimin e gabimeve dhe potencialeve të cenueshmërisë. Për këtë, do të kontrollojmë dhe gjejmë gabime të reja në versionin LLVM 8.0.0.
Artikulli që duhet të shkruhet
Më të vërtetë, nuk e kisha dëshirë të shkruaja këtë artikull. Nuk është interesante të shkruash për një projekt që ne e kemi kontrolluar tashmë disa herë (, , ). Më mirë do ishte të shkruaja për diçka të re, por nuk kam zgjedhje.
Ădo herĂ« qĂ« del njĂ« version i ri LLVM ose pĂ«rmirĂ«sohet , nĂ« postĂ«n tonĂ« vijnĂ« pyetje tĂ« tilla:
Shih, versioni i ri i Clang Static Analyzer e ka mĂ«suar tĂ« gjejĂ« gabime tĂ« reja! MĂ« duket se relevanca e pĂ«rdorimit tĂ« PVS-Studio po zvogĂ«lohet. Clang gjen mĂ« shumĂ« gabime se mĂ« parĂ« dhe po arrin aftĂ«sitĂ« e PVS-Studio. ĂfarĂ« mendoni pĂ«r kĂ«tĂ«?
Këtë gjithmonë më pëlqen ta përgjigjem me diçka në frymën e:
Ne gjithashtu nuk jemi ulur pa punuar! Ne kemi përmirësuar ndjeshëm mundësitë e analizuesit PVS-Studio. Prandaj mos u shqetësoni, ne vazhdojmë të liderojmë, ashtu si më parë.
Fatkeqësisht, kjo është një përgjigje e keqe. Nuk ka prova. Pikërisht për këtë arsye tani po shkruaj këtë artikull. Pra, projekti LLVM është kontrolluar përsëri dhe janë gjetur një shumëllojshmëri gabimesh. Ato që më dukeshin interesante, do t'i demonstroj tani. Këto gabime nuk mund të gjenden nga Clang Static Analyzer (ose është shumë e vështirë të bëhet me ndihmën e tij). Dhe ne mundemi. Për më tepër, unë i gjethe dhe i shkrova të gjitha këto gabime për një mbrëmje.
Por, shkruarja e artikullit u zgjat për disa javë. Nuk mund të më bëja të organizoj të gjithë këtë si tekst :).
Për më tepër, nëse jeni të interesuar për teknologjitë që përdoren në analizuesin PVS-Studio për identifikimin e gabimeve dhe potencialeve të cenueshmërisë, atëherë ju sugjeroj të njoftoheni me këtë .
Diagnostika të reja dhe të vjetra
Siç është përmendur më parë, rreth dy vjet më parë projekti LLVM u kontrollua përsëri, dhe gabimet e gjetura u korrigjuan. Tani në këtë artikull do të paraqitet një grup i ri gabimesh. Pse u gjetën gabime të reja? Ka tri arsye për këtë:
- Projekti LLVM po zhvillohet, me ndryshimin e kodit të vjetër dhe shfaqjen e atij të ri. Natyrisht, në kodin e modifikuar dhe të shkruar ka gabime të reja. Kjo tregon qartë se analiza statike duhet të aplikohet rregullisht, dhe jo ndonjëherë. Artikujt tanë tregojnë mirë mundësitë e analizuesit PVS-Studio, por kjo nuk ka të bëjë aspak me përmirësimin e cilësisë së kodit dhe uljen e kostove për rregullimin e gabimeve. Përdorni analizuesin statik të kodit rregullisht!
- Ne po përmirësojmë dhe zhvillojmë diagnostikimin e tashëm. Kështu që analizuesi mund të identifikojë gabimet që nuk ishin vërejtur gjatë kontrolleve të mëparshme.
- PVS-Studio ka fituar diagnostika të reja që nuk ishin të disponueshme 2 vjet më parë. Vendosa t'i dalloj ato në një seksion të veçantë për të treguar zhvillimin e PVS-Studio.
Defektet e identifikuara nga diagnostikat që ekzistonin 2 vjet më parë
Fragmenti N1: Copy-Paste
static bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) {
if (Name == "addcarryx.u32" || // Shtuar në 8.0
....
Name == "avx512.mask.cvtps2pd.128" || // Shtuar në 7.0
Name == "avx512.mask.cvtps2pd.256" || // Shtuar në 7.0
Name == "avx512.cvtusi2sd" || // Shtuar në 7.0
Name.startswith("avx512.mask.permvar.") || // Shtuar në 7.0 // <=
Name.startswith("avx512.mask.permvar.") || // Shtuar në 7.0 // <=
Name == "sse2.pmulu.dq" || // Shtuar në 7.0
Name == "sse41.pmuldq" || // Shtuar në 7.0
Name == "avx2.pmulu.dq" || // Shtuar në 7.0
....
}Kujdesi PVS-Studio: [CWE-570] Ka nĂ«n-shprehje identike âName.startswith(«avx512.mask.permvar.»)â nĂ« tĂ« majtĂ« dhe tĂ« djathĂ« tĂ« operatorit â||â. AutoUpgrade.cpp 73
Kontrollohet dy herë nëse emri fillon me nënstring «avx512.mask.permvar.». Në kontrollin e dytë duket se do të shkruhej diçka tjetër, por u harrua të rregullohej teksti i kopjuar.
Fragmenti N2: Gabimi i shtypjes
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;
....
}Kujdesi PVS-Studio: V501 Ka nĂ«n-shprehje identike âCXNameRange_WantQualifierâ nĂ« tĂ« majtĂ« dhe tĂ« djathĂ« tĂ« operatorit â|â. CIndex.cpp 7245
Për shkak të një gabimi të shtypjes, e njëjta konstantë e emruar është përdorur dy herë. CXNameRange_WantQualifier.
Fragmenti N3: Ngatërrimi i prioriteteve të operatorëve
int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
....
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
return 0;
....
}Kujdesi PVS-Studio: [CWE-783] Ndoshta operatori â?:â funksionon ndryshe nga sa pritej. Operatorit â?:â ka njĂ« prioritet mĂ« tĂ« ulĂ«t se ai â==â. PPCTargetTransformInfo.cpp 404
Sipër, mendoj se kjo është një gabim shumë i bukur. Po, e di që kam përfytyrime të çuditshme për bukurinë :) .
Tani, sipas , shprehja llogaritet si në vijim:
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0Nga një këndvështrim praktik, ky kusht nuk ka kuptim, pasi mund të shkurtizohet në:
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())Kjo është një gabim i qartë. Me shumë mundësi, 0/1 do të ishin krahasuar me variablin Index. Për të korrigjuar kodin, është e nevojshme të shtoni hakun përreth operatorit ternar:
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))Për më tepër, operatori ternar është shumë i rrezikshëm dhe provokon gabime logjike. Kini shumë kujdes me të dhe mos u tregoni lakmitarë duke vendosur hakun e rrethuar. Më gjerësisht këtë temë e kam shqyrtuar , në kapitullin 'Kini frikë nga operatori ?: dhe vendosni atë në hakun e rrethuar'.
Fragmenti N4, N5: Pika zero
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("nuk mund të converter '") + LHS->getAsString() +
"' në string");
return nullptr;
}
....
}Kujdesi PVS-Studio: [CWE-476] Mund tĂ« ndodhi dereferencimi i treguesit null âLHSâ. TGParser.cpp 2152
Nëse treguesi LHS nuk është null, duhen lëshuar paralajmërime. Megjithatë, përkundrazi, do të ndodhi dereferencimi i atij treguesi null: LHS->getAsString().
Kjo është një situatë shumë tipike, kur gabimi fshihet në trajtuesin e gabimeve, pasi askush nuk i teston ato. Analizatorët statikë kontrollojnë të gjithë kodin e arritshëm, pavarësisht se sa shpesh përdoret. Ky është një shembull shumë i mirë se si analiza statike plotëson metodologji të tjera të testimit dhe mbrojtjes nga gabimet.
Gabimi i ngjashĂ«m i trajtimit tĂ« treguesit RHS Ă«shtĂ« bĂ«rĂ« nĂ« kodin pak mĂ« poshtĂ«: V522 [CWE-476] Mund tĂ« ndodhi dereferencimi i treguesit null âRHSâ. TGParser.cpp 2186
Fragmenti N6: Përdorimi i treguesit pas zhvendosjes
static Expected
ExtractBlocks(....)
{
....
std::unique_ptr ProgClone = CloneModule(BD.getProgram(), VMap);
....
BD.setNewProgram(std::move(ProgClone)); // getFunction(MisCompFunctions[i].first); // <=
assert(NewF && "Funksioni nuk u gjet??");
MiscompiledFunctions.push_back(NewF);
}
....
}ParalajmĂ«rimi PVS-Studio: V522 [CWE-476] Mund tĂ« ndodhi dereferencimi i treguesit null âProgCloneâ. Miscompilation.cpp 601
Në fillim, treguesi i zgjuar ProgClone në fakt humbet pronësinë mbi objektin:
BD.setNewProgram(std::move(ProgClone));NĂ« tĂ« vĂ«rtetĂ«, tani ProgClone â Ă«shtĂ« njĂ« tregues null. Prandaj, pak mĂ« poshtĂ« duhet tĂ« ndodhĂ« njĂ« dereferencim i treguesit null:
Funksioni *NewF = ProgClone->getFunction(MisCompFunctions[i].first);Por, në të vërtetë, kjo nuk do të ndodhi! Kushtojini vëmendje se cikli në të vërtetë nuk ekzekutohet.
Në fillim të kontejnerit MiscompiledFunctions ka pastruar:
MiscompiledFunctions.clear();Më pas, madhësia e këtij kontejneri përdoret në kushtin e ciklit:
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {ĂshtĂ« e lehtĂ« tĂ« shihni se cikli nuk nis. Mendoj se kjo Ă«shtĂ« gjithashtu njĂ« gabim, dhe kodi duhet tĂ« shkruhet ndryshe.
Duket se kemi hasur në atë famëkeqe parit të gabimeve! Një gabim fsheh një tjetër :).
Fragmenti N7: Përdorimi i treguesit pas lëvizjes
static Expected TestOptimizer(BugDriver &BD, std::unique_ptr Test,
std::unique_ptr Safe) {
outs() << " Optimizimi i funksioneve që janë testuar: ";
std::unique_ptr Optimized =
BD.runPassesOn(Test.get(), BD.getPassesToRun());
if (!Optimized) {
errs() << " Gabim në ekzekutimin e kësaj sekuence kalimesh"
<< " në programin hyrës!n";
BD.setNewProgram(std::move(Test)); // <=
BD.EmitProgressBitcode(*Test, "pass-error", false); // <=
if (Error E = BD.debugOptimizerCrash())
return std::move(E);
return false;
}
....
}Kujdesi PVS-Studio: V522 [CWE-476] Dereferencimi i treguesit null âTestâ mund tĂ« ndodhĂ«. Miscompilation.cpp 709
Sërish situata e njëjtë. Në fillim përmbajtja e objektit zhvendoset, dhe pastaj përdoret si asgjë nuk ka ndodhur. Po e has gjithnjë e më shpesh këtë situatë në kodin e programeve, pasi në C++ u prezantua semantika e zhvendosjes. Për këtë e dua gjuhën C++! Shfaqen gjithnjë e më shumë mënyra për të qëlluar veten në këmbë. Analizatori PVS-Studio gjithmonë do të ketë punë :).
Fragmenti N8: Treguesi null
void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
uint32_t TypeId = Symbol.getTypeId();
auto Type = Symbol.getSession().getSymbolById(TypeId);
if (Type)
Printer << "";
else
Type->dump(*this);
}Kujdesi PVS-Studio: V522 [CWE-476] Dereferencimi i treguesit null âTypeâ mund tĂ« ndodhĂ«. PrettyFunctionDumper.cpp 233
Përveç trajtuesve të gabimeve, zakonisht nuk testohen as funksionet e printimit të të dhënave për qëllime debugimi. Kjo është pikërisht një rast i tillë. Funksioni pret një përdorues, i cili në vend që të zgjidhë problemet e tij, do të detyrohet të merret me rregullimin e tij.
Saktë:
if (Type)
Type->dump(*this);
else
Printer << "";Fragmenti N9: Treguesi null
void SearchableTableEmitter::collectTableEntries(
GenericTable &Table, const std::vector &Items) {
....
RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
if (!Ty) // getAsString() + " vs. " + // getType()->getAsString());
....
}Kujdesi PVS-Studio: V522 [CWE-476] Mund tĂ« ndodhĂ« dereferenca e treguesit null âTyâ. SearchableTableEmitter.cpp 614
Mendoj se gjithçka është e qartë dhe nuk kërkon shpjegime.
Fragment N10: Gabimi i shtypjes
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;
}Kujdesi PVS-Studio: Variabla âIdentifier->Typeâ i Ă«shtĂ« caktuar vetvetes. FormatTokenLexer.cpp 249
Nuk ka kuptim të caktohet variabla vetvetes. Më shumë gjasa do të donit të shkonit:
Identifier->Type = Question->Type;Fragment N11: Bllokim dyshues
void SystemZOperand::print(raw_ostream &OS) const {
switch (Kind) {
break;
case KindToken:
OS << "Token:" << getToken();
break;
case KindReg:
OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
break;
....
}Kujdesi PVS-Studio: [CWE-478] Merrni parasysh tĂ« inspektoni deklaratĂ«n âswitchâ. ĂshtĂ« e mundur qĂ« operatori i parĂ« âcaseâ Ă«shtĂ« i munguar. SystemZAsmParser.cpp 652
Në fillim ka një operator shumë të dyshimtë break. A e kemi harruar të shkruajmë diçka tjetër këtu?
Fragment N12: Kontrollimi i treguesit pas dereferencimit
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("callee i paefektuar");
....
}Kujdesi PVS-Studio: [CWE-476] Treguesi âCalleeâ u shfrytĂ«zua para se tĂ« verifikohej kundĂ«r nullptr. Kontrolloni rreshtat: 172, 174. AMDGPUInline.cpp 172
Treguesi Callee në fillim i dereferencohet në momentin e thirrjes së funksionit getTTI.
Dhe më pas rezulton se ky tregues duhet të kontrollohet për barazi nullptr:
if (!Callee || Callee->isDeclaration())Por tashmë është vonë...
Fragment N13 â NâŠ: Kontrollimi i treguesit pas dereferencimit
Situata e shqyrtuar në fragmentin e mëparshëm të kodit nuk është e veçantë. Ajo ndodhet këtu:
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()) { // <=
....
}Kujdesi PVS-Studio: V595 [CWE-476] Treguesi âCalleeFnâ u shfrytĂ«zua para se tĂ« verifikohej kundĂ«r nullptr. Kontrolloni rreshtat: 1079, 1081. SimplifyLibCalls.cpp 1079
Dhe këtu:
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()); // <=
....
}Kujdesi PVS-Studio: V595 [CWE-476] Pointeri âNDâ u pĂ«rdor pĂ«rpara se tĂ« verifikohet kundrejt nullptr. Kontrolloni linjat: 532, 534. SemaTemplateInstantiateDecl.cpp 532
Dhe këtu:
- V595 [CWE-476] Pointeri âUâ u pĂ«rdor pĂ«rpara se tĂ« verifikohet kundrejt nullptr. Kontrolloni linjat: 404, 407. DWARFFormValue.cpp 404
- V595 [CWE-476] Pointeri âNDâ u pĂ«rdor pĂ«rpara se tĂ« verifikohet kundrejt nullptr. Kontrolloni linjat: 2149, 2151. SemaTemplateInstantiate.cpp 2149
Dhe pastaj nuk më interesoi të studioja paralajmërimet me numër V595. Kështu që nuk e di nëse ka edhe gabime të tjera si këto, përveç atyre të përmendura këtu. Gjithashtu ka për siguri.
Fragment N17, N18: Shkëputje e dyshimtë
static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
uint64_t &Encoding) {
....
unsigned Size = RegSize;
....
uint64_t NImms = ~(Size-1) << 1;
....
}Kujdesi PVS-Studio: [CWE-190] Konsideroni tĂ« kontrolloni shprehjen â~(Size â 1) << 1â. ShkĂ«putja bitore e vlerĂ«s 32-bit me njĂ« zgjerim tĂ« mĂ«vonshĂ«m nĂ« tipin 64-bit. AArch64AddressingModes.h 260
Mund të mos jetë një gabim dhe kodi funksionon ashtu siç ishte parashikuar. Por kjo është padyshim një pikë shumë e dyshimtë dhe duhet të kontrollohet.
Supozoni se variabli Size është 16, dhe atëherë autori i kodit planifikonte të merrte në variablën NImms vlerën:
1111111111111111111111111111111111111111111111111111111111100000
Megjithatë, në të vërtetë, rezultati do të jetë:
0000000000000000000000000000000011111111111111111111111111100000
ĂĂ«shtja Ă«shtĂ« se tĂ« gjithĂ« llogaritjet kryhen duke pĂ«rdorur tipin 32-bit pa shenjĂ«. Dhe vetĂ«m pastaj, ky tip 32-bit pa shenjĂ« do tĂ« zgjerohĂ«t nĂ« mĂ«nyrĂ« implicite nĂ« uint64_t. Me kĂ«tĂ«, bitet mĂ« tĂ« larta do tĂ« jenĂ« zero.
Situatën mund ta zgjidhni kështu:
uint64_t NImms = ~static_cast(Size-1) << 1;SituatĂ« e ngjashme: V629 [CWE-190] Konsideroni tĂ« kontrolloni shprehjen âImmr << 6â. ShkĂ«putja bitore e vlerĂ«s 32-bit me njĂ« zgjerim tĂ« mĂ«vonshĂ«m nĂ« tipin 64-bit. AArch64AddressingModes.h 269
Fragment N19: Mungon fjalëkyçi i rëndësishëm else?
void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
....
if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
// VOP2b (v_add_u32, v_sub_u32 ...) përdorimi i dpp "vcc" token.
// Kaloni atë.
continue;
} if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) { // <=
Op.addRegWithFPInputModsOperands(Inst, 2);
} else if (Op.isDPPCtrl()) {
Op.addImmOperands(Inst, 1);
} else if (Op.isImm()) {
// Trajtoni argumentet opcionale
OptionalIdx[Op.getImmTy()] = I;
} else {
llvm_unreachable("Lloji i operandit është i pavlefshëm");
}
....
}Kujdesi PVS-Studio: [CWE-670] Konsideroni tĂ« kontrolloni logjikĂ«n e aplikacionit. ĂshtĂ« e mundur qĂ« fjalĂ«kyçi âelseâ mungon. AMDGPUAsmParser.cpp 5655
Nuk ka gabime këtu. Sepse blloku then i parë nëse mbaron në continue, atëherë nuk ka rëndësi, nëse ka fjalë kyçe else apo jo. Megjithatë, kodi do të funksionojë në mënyrë të njëjtë. Për më tepër, mungesa e else bën kodin më të paqartë dhe më të rrezikshëm. Nëse më vonë continue zhduket, kodi do të fillojë të funksionojë ndryshe. Sipas mendimit tim, është më mirë të shtosh else.
Fragmenti N20: Katër gabime të ngjashme
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;
}Kërcënime të PVS-Studio:
- V655 [CWE-480] Stringat u bashkuan, por nuk janĂ« pĂ«rdorur. Merrni parasysh inspektimin e shprehjes âResult + Name.str()â. Symbol.cpp 32
- V655 [CWE-480] Stringat u bashkuan, por nuk janĂ« pĂ«rdorur. Merrni parasysh inspektimin e shprehjes âResult + «(ObjC Class) » + Name.str()â. Symbol.cpp 35
- V655 [CWE-480] Stringat u bashkuan, por nuk janĂ« pĂ«rdorur. Merrni parasysh inspektimin e shprehjes âResult + «(ObjC Class EH) » + Name.str()â. Symbol.cpp 38
- V655 [CWE-480] Stringat u bashkuan, por nuk janĂ« pĂ«rdorur. Merrni parasysh inspektimin e shprehjes âResult + «(ObjC IVar) » + Name.str()â. Symbol.cpp 41
Gabimisht, operatori += përdoret në vend të operatorit +. Si rezultat, krijohen konstruksione pa kuptim.
Fragmenti N21: Sjellje e pacaktuar
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 nuk mund të jetë e zbrazët");
for (auto &Op : Ops) {
assert(!Op.empty() && "Operator i zbrazët");
if (FeaturesMap.find(Op) == FeaturesMap.end())
FeaturesMap[Op] = FeaturesMap.size();
}
}
}Provoni të gjeni vetë kodin e rrezikshëm. Kjo është një imazh për të shkëputur vëmendjen, që të mos e shihni menjëherë përgjigjen:

Kujdesi PVS-Studio: [CWE-758] NjĂ« ndĂ«rtim i rrezikshĂ«m po pĂ«rdoret: âFeaturesMap[Op] = FeaturesMap.size()â, ku âFeaturesMapâ Ă«shtĂ« i klasĂ«s âmapâ. Kjo mund tĂ« çojĂ« nĂ« sjellje tĂ« pacaktuar. RISCVCompressInstEmitter.cpp 490
Rreshti problematik:
FeaturesMap[Op] = FeaturesMap.size();Nëse elementi Op nuk gjendet, krijohet një element i ri në hartë dhe atje shkruhet numri i elementeve në këtë hartë. Por nuk dihet se do të thirret funksioni size para apo pas shtimit të elementit të ri.
Fragmentet N22-N24: Përsëritjet e caktimeve
Gabim 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;
}
....
}Kujdesi PVS-Studio: [CWE-563] Variabla âNTypeâ i Ă«shtĂ« caktuar vlera dy herĂ« radhazi. Ndoshta Ă«shtĂ« njĂ« gabim. Kontrolloni rreshtat: 1663, 1664. MachOObjectFile.cpp 1664
Mendoj se nuk ka ndonjë gabim të vërtetë këtu. Thjesht është një caktim i tepërt dhe i përsëritur. Megjithatë, është një lëshim.
Ngjashëm:
- V519 [CWE-563] Variabla âB.NDescâ i Ă«shtĂ« caktuar vlera dy herĂ« radhazi. Ndoshta Ă«shtĂ« njĂ« gabim. Kontrolloni rreshtat: 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] Variabla i është caktuar vlera dy herë radhazi. Ndoshta është një gabim. Kontrolloni rreshtat: 59, 61. coff2yaml.cpp 61
Fragmenti N25-N27: Përsëritje caktimesh
Tani do të shqyrtojmë një variant pak më ndryshe të caktimit të përsëritur.
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;
....
}ParalajmĂ«rim PVS-Studio: V519 [CWE-563] Variabla âAlignmentâ i Ă«shtĂ« caktuar vlera dy herĂ« radhazi. Ndoshta Ă«shtĂ« njĂ« gabim. Kontrolloni rreshtat: 1158, 1160. LoadStoreVectorizer.cpp 1160
Ky është një kod shumë i çuditshëm, i cili duket se përmban një gabim logjik. Fillimisht, variablës Alignment i caktohet një vlerë në varësi të një kushti. Dhe më pas ka një caktim tjetër, por tani pa asnjë kontroll.
Situata të ngjashme mund të shihen këtu:
- V519 [CWE-563] Variabla âEffectsâ i Ă«shtĂ« caktuar vlera dy herĂ« radhazi. Ndoshta Ă«shtĂ« njĂ« gabim. Kontrolloni rreshtat: 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] Variabla âExpectNoDerefChunkâ i Ă«shtĂ« caktuar vlera dy herĂ« radhazi. Ndoshta Ă«shtĂ« njĂ« gabim. Kontrolloni rreshtat: 4970, 4973. SemaType.cpp 4973
Fragmenti N28: Kusht gjithmonë i vërtetë
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) // Mbështetje për instrukcionin PAUSE // <=
break;
}
....
}Kujdesi PVS-Studio: [CWE-571] Shprehja ânextByte != 0x90â Ă«shtĂ« gjithmonĂ« e vĂ«rtetĂ«. X86DisassemblerDecoder.cpp 379
Kontrolli nuk ka kuptim. Variabla nextByte gjithmonë nuk është e barabartë me vlerën 0x90, që rrjedh nga kontrolli i mëparshëm. Kjo është një gabim logjik.
Fragmenti N29 â NâŠ: Kushtet gjithmonĂ« tĂ« vĂ«rteta/falshme
Analizatori jep shumë paralajmërime për atë që e gjithë kushti () ose pjesa e tij () gjithmonë është e vërtetë ose e gabuar. Shpesh këto nuk janë gabime të vërteta, por thjesht kod i pahijshëm, rezultat i vulosjes së makros dhe të ngjashme. Megjithatë, ka kuptim të shikoni të gjitha këto paralajmërime, pasi herë pas here hasen gabime të vërteta logjike. Për shembull, ky segment kodi është shqetësues:
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;
....
}Kujdesi PVS-Studio: [CWE-570] Një pjesë e shprehjes kushtore është gjithmonë e gabuar: RegNo == 0xe. ARMDisassembler.cpp 939
Konstanta 0xE është vlera 14 në sistemin decimal. Kontrolli RegNo == 0xe nuk ka kuptim, pasi nëse RegNo > 13, atëherë funksioni do të përfundojë.
Ishte shumë paralajmërime të tjera me identifikuesit V547 dhe V560, por, ashtu si në rastin e , studimi i këtyre paralajmërimeve nuk më interesonte. Kishte mjaft materiale për të shkruar një artikull :). Prandaj nuk dihet se sa gabime të tilla mund të identifikohen në LLVM me ndihmën e PVS-Studio.
Do të jap një shembull pse studimi i këtyre reagimeve është i mërzitshëm. Analizatori është plotësisht i saktë kur jep paralajmërimin në këtë kod. Por kjo nuk është një gabim.
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
tok::TokenKind ClosingBraceKind) {
bool HasError = false;
....
HasError = true;
if (!ContinueOnSemicolons)
return !HasError;
....
}ParalajmĂ«rimi PVS-Studio: V547 [CWE-570] Shprehja â!HasErrorâ Ă«shtĂ« gjithmonĂ« e gabuar. UnwrappedLineParser.cpp 1635
Fragmenti N30: Kthimi i dyshimtë
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();
}
....
}Kujdesi PVS-Studio: [CWE-670] NjĂ« âkthimâ i kushtuar brenda njĂ« cikli. R600OptimizeVectorRegisters.cpp 63
Kjo Ă«shtĂ« njĂ« gabim ose njĂ« teknikĂ« e veçantĂ«, e cila synon tĂ« shpjegojĂ« diçka pĂ«r programuesit qĂ« lexojnĂ« kodin. MĂ« duket se kjo konstrukcion nuk shpjegon asgjĂ« dhe duket mjaft e dyshimtĂ«. ĂshtĂ« mĂ« mirĂ« tĂ« mos shkruani kĂ«shtu :).
A po lodhesh? Atëherë është koha të përgatisësh çaj ose kafe.

Defektet e zbuluara nga diagnostikët e rinj
Mendoj se 30 reagime të diagnostikëve të vjetër janë të mjaftueshme. Tani le të shikojmë se çfarë interesante mund të gjejmë me diagnostikët e rinj, të cilat janë shtuar në analizator pas kontrollit. Gjatë kësaj kohe, në analizatorin C++ janë shtuar 66 diagnostikë të përgjithshme.
Fragmenti N31: Kodi i padishtë
Gabim CtorDtorRunner::run() {
....
nëse (auto CtorDtorMap =
ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
NoDependenciesToRegister, true))
{
....
kthehu Gabim::sukses();
} përndryshe
kthehu CtorDtorMap.takeError();
CtorDtorsByPriority.clear();
kthehu Gabim::sukses();
}Kujdesi PVS-Studio: [CWE-561] Kodi i paarritshëm është zbuluar. Mund të jetë se ka një gabim të pranishëm. ExecutionUtils.cpp 146
Siç dëgjoni, të dy degët e operatorit nëse mbarojnë me thirrjen e operatorit return. Si rrjedhojë, kontejneri CtorDtorsByPriority kurrë nuk do të pastrohet.
Fragmenti N32: Kodi i paarritshëm
bool LLParser::ParseSummaryEntry() {
....
switch (Lex.getKind()) {
rast lltok::kw_gv:
kthehu ParseGVEntry(SummaryID);
rast lltok::kw_module:
kthehu ParseModuleEntry(SummaryID);
rast lltok::kw_typeid:
kthehu ParseTypeIdEntry(SummaryID); // <=
break; // <=
default:
kthehu Gabim(Lex.getLoc(), "lloj i papritur përmbledhjeje");
}
Lex.setIgnoreColonInIdentifiers(false); // <=
kthehu false;
}Warnimi PVS-Studio: V779 [CWE-561] Kodi i paarritshëm është zbuluar. Mund të jetë se ka një gabim të pranishëm. LLParser.cpp 835
Një situatë interesante. Le të shqyrtojmë fillimisht këtë vend:
kthehu ParseTypeIdEntry(SummaryID);
break;Në shikim të parë duket se nuk ka gabime këtu. Duket se operatori break këtu është i tepërt, dhe mund të fshihet thjesht. Sidoqoftë, nuk është aq e thjeshtë.
Analizuesi lëshon një njoftim në rreshta:
Lex.setIgnoreColonInIdentifiers(false);
kthehu false;Dhe duke qenë me të vërtetë, ky kod është i paarritshëm. Të gjitha rastet në switch mbarojnë me thirrjen nga operatori return. Dhe tani, një i izoluar dhe i paarsyeshëm break nuk duket më kaq i padëmshëm! Ndoshta një nga degët duhet të përfundojë me break, dhe jo me return?
Fragmenti N33: Zero i rastësishëm i bitëve më të lartë
unsigned getStubAlignment() override {
nëse (Arch == Triple::systemz)
kthehu 8;
përndryshe
kthehu 1;
}
Expected
RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
const SectionRef &Section,
bool IsCode) {
....
uint64_t DataSize = Section.getSize();
....
nëse (StubBufSize > 0)
DataSize &= ~(getStubAlignment() - 1);
....
}Kujdesi PVS-Studio: Madhësia e maskës së bitëve është më e vogël se madhësia e operandit të parë. Kjo do të shkaktojë humbjen e bitëve më të lartë. RuntimeDyld.cpp 815
Vini re se funksioni getStubAlignment kthen tipin unsigned. Le të llogarisim vlerën e shprehjes, nëse supozojmë se funksioni do të kthejë vlerën 8:
~(getStubAlignment() - 1)
~(8u-1)
0xFFFFFFF8âŹu
Tani vini re se variabli DataSize ka njĂ« tip tĂ« paqĂ«ndrueshĂ«m 64-bit. KĂ«shtu qĂ«, gjatĂ« ekzekutimit tĂ« operacionit DataSize & 0xFFFFFFF8âŹu, tĂ« gjithĂ« tridhjetĂ« e dy bitĂ«t mĂ« tĂ« lartĂ« do tĂ« zhbĂ«hen. Probabilisht, kjo nuk Ă«shtĂ« ajo qĂ« donte programuesi. Dyshoj se ai dĂ«shiron tĂ« llogarisĂ«: DataSize & 0xFFFFFFFFFFFFFFF8âŹu.
Për të rregulluar gabimin, duhet të shkruhet kështu:
DataSize &= ~(static_cast(getStubAlignment()) - 1);Ose ashtu:
DataSize &= ~(getStubAlignment() - 1ULL);Fragmenti N34: Konvertimi i tipit të qartë dështoi
template <typename T>
void scaleShuffleMask(int Scale, ArrayRef<T> Mask,
SmallVectorImpl<T> &ScaledMask) {
assert(0 < Scale && "Faktori i papritur i shkallës");
int NumElts = Mask.size();
ScaledMask.assign(static_cast<size_t>(NumElts * Scale), -1);
....
}Kujdesi PVS-Studio: [CWE-190] MundĂ«sia e mbipĂ«rshkimit. Merrni nĂ« konsideratĂ« konvertimin e operandĂ«ve tĂ« operatorit âNumElts * Scaleâ nĂ« tipin âsize_tâ, jo rezultatin. X86ISelLowering.h 1577
Konvertimi i qartë të tipit përdoret për të parandaluar mbipërshkimin në shumëzimin e variablave të tipit int. Megjithatë, këtu konvertimi i qartë të tipit nuk mbron nga mbipërshkimi. Në fillim, variablat do të shumëzohen, dhe vetëm më pas rezultati 32-bit i shumëzimit do të zgjerohet në tipin .
Fragmenti N35: Dështimi i 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] Dy fragmente tĂ« ngjashme kodi u gjetĂ«n. Ndoshta, kjo Ă«shtĂ« njĂ« gabim dhe variabli âOp1â duhet tĂ« pĂ«rdoret nĂ« vend tĂ« âOp0â. InstCombineCompares.cpp 5507
Kjo diagnosticë e re interesante identifikon situata kur një fragment kodi u kopjua dhe filluan të ndryshoheshin disa emra, por në një vend nuk e korrigjuan.
Vini re se në bllokun e dytë ndryshuan Op0 në Op1. Por në një vend nuk e korrigjuan. Me shumë gjasa, duhej shkruar kështu:
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
return &I;
}Fragmenti N36: Konfuzion në variabla
struct Status {
unsigned Mask;
unsigned Mode;
Status() : Mask(0), Mode(0){};
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
Mode &= Mask;
};
....
};Kujdesi PVS-Studio: [CWE-563] Variabli âModeâ caktohet por nuk pĂ«rdoret deri nĂ« fund tĂ« funksionit. SIModeRegister.cpp 48
ĂshtĂ« shumĂ« e rrezikshme t'u jepni argumenteve tĂ« funksionit tĂ« njĂ«jtat emra si anĂ«tarĂ«ve tĂ« klasĂ«s. ĂshtĂ« shumĂ« e lehtĂ« tĂ« ngatĂ«rrohesh. Ky Ă«shtĂ« njĂ« rast i tillĂ«. Ky shprehje nuk ka kuptim:
Mode &= Mask;Ndryshohet argumenti i funksionit. Dhe gjithçka. Ky argument nuk përdoret më. Me shumë gjasa, duhej shkruar kështu:
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
this->Mode &= Mask;
};Fragmenti N37: Konfuzion në variabla
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;
}Kujdes PVS-Studio: V1001 [CWE-563] Variabla 'Size' i është caktuar, por nuk përdoret deri në fund të funksionit. Object.cpp 424
Situata është e ngjashme me të mëparshmen. Duhet të shkruhet:
this->Size += this->EntrySize;Fragma N38-N47: Të dhënat e pointer-it janë harruar të kontrollohen
Më parë kemi shqyrtuar shembuj të aktivizimit të diagnostikës . Thelbi i saj është se pointer-i fillimisht shkrinje dhe më pas kontrollohet. Diagnostika e re është e kundërt me të në kuptim, por gjithashtu zbulon shumë gabime. Ajo identifikon situata kur pointer-i fillimisht është kontrolluar, dhe më pas është harruar të bëhet. Le të shqyrtojmë raste të tilla të gjetura brenda 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()); // <=
....
}Kujdes PVS-Studio: V1004 [CWE-476] Pointer-i 'Ptr' është përdorur në mënyrë të pasigurt pas verifikimit kundër nullptr. Kontrolloni rreshtat: 729, 738. TargetTransformInfoImpl.h 738
Variabli Ptr mund të jetë e barabartë me nullptr, që është dëshmi e kontrollit:
if (Ptr != nullptr)Megjithatë, më poshtë ky pointer shkrihet pa kontroll paraprak:
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());Le të shqyrtojmë një rast tjetër të ngjashëm.
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(); // <=
....
}Kujdes PVS-Studio: V1004 [CWE-476] Pointer-i 'FD' është përdorur në mënyrë të pasigurt pas verifikimit kundër nullptr. Kontrolloni rreshtat: 3228, 3231. CGDebugInfo.cpp 3231
Kujdesi mbi pointer-in FD. Jam i sigurt, problemi është i dukshëm, dhe nuk kërkohet shpjegim të veçantë.
Dhe gjithashtu:
static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result,
Value *&BasePtr,
const DataLayout &DL) {
PointerType *PtrTy = dyn_cast(Ptr.getType());
if (!PtrTy) {
Result = Polynomial();
BasePtr = nullptr;
}
unsigned PointerBits =
DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace());
....
}KujtesĂ« PVS-Studio: V1004 [CWE-476] Pika âPtrTyâ u pĂ«rdor nĂ« mĂ«nyrĂ« tĂ« pasigurt pasi u verifikua ndaj nullptr. Kontrolloni linjat: 960, 965. InterleavedLoadCombinePass.cpp 965
Si të mbroheni nga këto gabime? Jini më të kujdesshëm gjatë rishikimit të kodit dhe përdorni një analizues statik si PVS-Studio për kontroll të rregullt.
Nuk ka kuptim të jap përmbajtje të tjera me fragmentet e kodit me këtë lloj gabimi. Do të lë në artikull vetëm listën e kujtesave:
- V1004 [CWE-476] Pika âExprâ u pĂ«rdor nĂ« mĂ«nyrĂ« tĂ« pasigurt pasi u verifikua ndaj nullptr. Kontrolloni linjat: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] Pika âPIâ u pĂ«rdor nĂ« mĂ«nyrĂ« tĂ« pasigurt pasi u verifikua ndaj nullptr. Kontrolloni linjat: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] Pika âStatepointCallâ u pĂ«rdor nĂ« mĂ«nyrĂ« tĂ« pasigurt pasi u verifikua ndaj nullptr. Kontrolloni linjat: 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] Pika âRVâ u pĂ«rdor nĂ« mĂ«nyrĂ« tĂ« pasigurt pasi u verifikua ndaj nullptr. Kontrolloni linjat: 2263, 2268. TGParser.cpp 2268
- V1004 [CWE-476] Pika âCalleeFnâ u pĂ«rdor nĂ« mĂ«nyrĂ« tĂ« pasigurt pasi u verifikua ndaj nullptr. Kontrolloni linjat: 1081, 1096. SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] Pika âTCâ u pĂ«rdor nĂ« mĂ«nyrĂ« tĂ« pasigurt pasi u verifikua ndaj nullptr. Kontrolloni linjat: 1819, 1824. Driver.cpp 1824
Fragmenti N48-N60: Nuk është kritik, por është një defekt (mundësi për rrjedhje memoriesh)
std::unique_ptr createISelMutator() {
....
std::vector<std::unique_ptr> Strategies;
Strategies.emplace_back(
new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
....
}Kujdesi PVS-Studio: [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« kontejnerin âStrategiesâ me metodĂ«n âemplace_backâ. NjĂ« rrjedhje memorjeje do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-isel-fuzzer.cpp 58
Për të shtuar një element në fund të kontejnerit të tipit std::vector<std::unique_ptr> nuk mund të shkruani thjesht xxx.push_back(new X), pasi nuk ka konvertim implicit nga X* në std::unique_ptr.
Një zgjidhje e zakonshme është të shkruani xxx.emplace_back(new X), pasi ajo kompilon: metoda emplace_back ndërton elementin direkt nga argumentet dhe për këtë arsye mund të përdorë konstruktora të qartë.
Kjo nuk është e sigurt. Nëse vektori është i plotë, ndodhi rishpërndarja e memories. Operacioni i rishpërndarjes mund të dështojë, duke rezultuar në gjenerimin e një përjashtimi std::bad_alloc. Në këtë rast, pointeri do të humbasë dhe objekti i krijuar nuk do të fshihet kurrë.
Një zgjidhje e sigurt është krijimi i unique_ptr, e cila do të zotërojë pointerin deri sa vektori të përpiqet të rishpërndajë memorjen:
xxx.push_back(std::unique_ptr(new X))Duke nga C++14, mund tĂ« pĂ«rdorni âstd::make_uniqueâ:
xxx.push_back(std::make_unique())Ky lloj defekti nuk është kritik për LLVM. Nëse nuk arrihet të alokohet memoria, puna e kompilatorit thjesht do të ndalet. Sidoqoftë, për aplikacione me , të cilat nuk mund të përfundojnë lehtë, nëse memorja nuk është alokuar, kjo mund të jetë një gabim i vërtetë.
Pra, megjithëse ky kod nuk paraqet një rrezik praktik për LLVM, e konsiderova të dobishme të flas për këtë model gabimi dhe se si analisti PVS-Studio ka mësuar ta zbulojë atë.
Paralajmërime të tjera të këtij lloji:
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âPassesâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. PassManager.h 546
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âAAsâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. AliasAnalysis.h 324
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âEntriesâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. DWARFDebugFrame.cpp 519
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âAllEdgesâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. CFGMST.h 268
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âVMapsâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âRecordsâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. FDRLogBuilder.h 30
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âPendingSubmodulesâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. ModuleMap.cpp 810
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âObjectsâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. DebugMap.cpp 88
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âStrategiesâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âModifiersâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-stress.cpp 685
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âModifiersâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-stress.cpp 686
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âModifiersâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-stress.cpp 688
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âModifiersâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-stress.cpp 689
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âModifiersâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-stress.cpp 690
- V1023 [CWE-460] NjĂ« pointer pa pronar Ă«shtĂ« shtuar nĂ« konteinerin âModifiersâ nga metoda âemplace_backâ. NjĂ« rrjedhje memorie do tĂ« ndodhĂ« nĂ« rast tĂ« njĂ« pĂ«rjashtimi. llvm-stress.cpp 691
- V1023 [CWE-460] Një tregues pa pronar shtohet në kontejnerin 'Modifiers' nga metoda 'emplace_back'. Një rrjedhje memories do të ndodhë në rast të një përjashtimi. llvm-stress.cpp 692
- V1023 [CWE-460] Një tregues pa pronar shtohet në kontejnerin 'Modifiers' nga metoda 'emplace_back'. Një rrjedhje memories do të ndodhë në rast të një përjashtimi. llvm-stress.cpp 693
- V1023 [CWE-460] Një tregues pa pronar shtohet në kontejnerin 'Modifiers' nga metoda 'emplace_back'. Një rrjedhje memories do të ndodhë në rast të një përjashtimi. llvm-stress.cpp 694
- V1023 [CWE-460] Një tregues pa pronar shtohet në kontejnerin 'Operands' nga metoda 'emplace_back'. Një rrjedhje memories do të ndodhë në rast të një përjashtimi. GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] Një tregues pa pronar shtohet në kontejnerin 'Stash' nga metoda 'emplace_back'. Një rrjedhje memories do të ndodhë në rast të një përjashtimi. GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] Një tregues pa pronar shtohet në kontejnerin 'Matchers' nga metoda 'emplace_back'. Një rrjedhje memories do të ndodhë në rast të një përjashtimi. GlobalISelEmitter.cpp 2702
Përfundim
Shtova gjithsej 60 paralajmërime, pas së cilës ndala. A ka defekte të tjera, që analisti PVS-Studio i zbulon në LLVM? Po, ka. Megjithatë, kur po shkruaja fragmente kodi për artikullin, kishte kaluar vonë, për ndryshe madje natën dhe vendosa se ishte koha të përfundoj.
Shpresoj se ju ka pëlqyer dhe do të dëshironit të provoni analizatorin PVS-Studio.
Mund ta shkarkoni analizatorin dhe të merrni një çelës provues në .
E rëndësishmja, përdorni analizën statike rregullisht. Kontrolle të rastësishme, që ne i kryejmë për të popullarizuar metodologjinë e analizës statike dhe PVS-Studio, nuk janë një skenar normal.
Suksese në përmirësimin e cilësisë dhe besueshmërisë së kodit!
Nëse dëshironi të ndaheni këtë artikull me një audiencë anglishtfolëse, ju lutem përdorni lidhjen për përkthimin: Andrey Karpov. .
Burimi: habr.com
