Ավելի քան երկու տարի է անցել LLVM նախագծի վերջին ծածկագրի ստուգումից՝ օգտագործելով մեր PVS-Studio անալիզատորը: Եկեք համոզվենք, որ PVS-Studio անալիզատորը դեռևս առաջատար գործիք է սխալների և հնարավոր խոցելիության հայտնաբերման համար: Դա անելու համար մենք կստուգենք և կգտնենք նոր սխալներ LLVM 8.0.0 թողարկման մեջ:
Հոդվածը պետք է գրվի
Ճիշտն ասած, ես չէի ուզում գրել այս հոդվածը: Հետաքրքիր չէ գրել մի նախագծի մասին, որը մենք արդեն մի քանի անգամ ստուգել ենք (
Ամեն անգամ, երբ թողարկվում կամ թարմացվում է LLVM-ի նոր տարբերակը
Տեսեք, Clang Static Analyzer-ի նոր տարբերակը սովորել է գտնել նոր սխալներ: Ինձ թվում է, որ PVS-Studio-ի օգտագործման արդիականությունը նվազում է։ Clang-ը գտնում է ավելի շատ սխալներ, քան նախկինում և հասնում է PVS-Studio-ի հնարավորություններին: Ի՞նչ կարծիքի եք այս մասին։
Սրան ես միշտ ուզում եմ պատասխանել նման բան.
Մենք էլ պարապ չենք նստում։ Մենք զգալիորեն բարելավել ենք PVS-Studio անալիզատորի հնարավորությունները: Այնպես որ, մի անհանգստացեք, մենք նախկինի պես շարունակում ենք առաջատար լինել։
Ցավոք սրտի, սա վատ պատասխան է։ Դրանում ապացույցներ չկան։ Եվ դրա համար ես հիմա գրում եմ այս հոդվածը։ Այսպիսով, LLVM նախագիծը ևս մեկ անգամ ստուգվել է և նրանում հայտնաբերվել են մի շարք սխալներ։ Ես հիմա ցույց կտամ նրանց, որոնք ինձ հետաքրքիր էին թվում: Clang Static Analyzer-ը չի կարող գտնել այս սխալները (կամ չափազանց անհարմար է դա անել իր օգնությամբ): Բայց մենք կարող ենք։ Ավելին, ես գտա և գրի առա այս բոլոր սխալները մեկ երեկոյան։
Բայց հոդվածը գրելը տևեց մի քանի շաբաթ: Ես պարզապես չկարողացա ինձ ստիպել այս ամենը տեքստի մեջ դնել :):
Ի դեպ, եթե ձեզ հետաքրքրում է, թե ինչ տեխնոլոգիաներ են օգտագործվում PVS-Studio անալիզատորում սխալներն ու հնարավոր խոցելիությունները բացահայտելու համար, ապա առաջարկում եմ ծանոթանալ սրա հետ.
Նոր և հին ախտորոշում
Ինչպես արդեն նշվեց, մոտ երկու տարի առաջ LLVM նախագիծը հերթական անգամ ստուգվեց, և հայտնաբերված սխալները ուղղվեցին: Այժմ այս հոդվածը կներկայացնի սխալների նոր խմբաքանակ: Ինչու են հայտնաբերվել նոր սխալներ: Դրա համար կա 3 պատճառ.
- LLVM նախագիծը զարգանում է՝ փոխելով հին ծածկագիրը և ավելացնելով նոր ծածկագիրը: Բնականաբար, փոփոխված և գրված կոդում նոր սխալներ կան։ Սա հստակ ցույց է տալիս, որ ստատիկ վերլուծությունը պետք է պարբերաբար օգտագործվի, և ոչ երբեմն: Մեր հոդվածները լավ ցույց են տալիս PVS-Studio անալիզատորի հնարավորությունները, բայց դա ոչ մի կապ չունի կոդի որակի բարելավման և սխալների շտկման ծախսերի նվազեցման հետ: Պարբերաբար օգտագործեք ստատիկ կոդի անալիզատոր:
- Մենք վերջնական տեսքի ենք բերում և բարելավում առկա ախտորոշումը: Հետևաբար, անալիզատորը կարող է բացահայտել այն սխալները, որոնք նա չի նկատել նախորդ սկանավորման ժամանակ:
- PVS-Studio-ում հայտնվել են նոր դիագնոստիկա, որոնք գոյություն չունեին 2 տարի առաջ։ Ես որոշեցի առանձնացնել դրանք առանձին բաժնում՝ հստակ ցույց տալու PVS-Studio-ի զարգացումը:
2 տարի առաջ առկա ախտորոշմամբ հայտնաբերված թերությունները
Հատված N1՝ Copy-Paste
static bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) {
if (Name == "addcarryx.u32" || // Added in 8.0
....
Name == "avx512.mask.cvtps2pd.128" || // Added in 7.0
Name == "avx512.mask.cvtps2pd.256" || // Added in 7.0
Name == "avx512.cvtusi2sd" || // Added in 7.0
Name.startswith("avx512.mask.permvar.") || // Added in 7.0 // <=
Name.startswith("avx512.mask.permvar.") || // Added in 7.0 // <=
Name == "sse2.pmulu.dq" || // Added in 7.0
Name == "sse41.pmuldq" || // Added in 7.0
Name == "avx2.pmulu.dq" || // Added in 7.0
....
}
PVS-Studio նախազգուշացում.
Կրկնակի ստուգվում է, որ անունը սկսվում է «avx512.mask.permvar» ենթատողով: Երկրորդ ստուգման ժամանակ ակնհայտորեն ուզում էին այլ բան գրել, բայց մոռացել էին ուղղել պատճենված տեքստը։
Հատված N2. Տառասխալ
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;
....
}
Զգուշացում PVS-Studio. V501 «|»-ի ձախ և աջ «CXNameRange_WantQualifier» ենթաարտահայտությունները նույնական են: օպերատոր. CIndex.cpp 7245
Տառասխալի պատճառով նույն անունով հաստատունն օգտագործվում է երկու անգամ CXNameRange_WantQualifier.
Հատված N3. շփոթություն օպերատորի գերակայության հետ
int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
....
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
return 0;
....
}
PVS-Studio նախազգուշացում.
Իմ կարծիքով, սա շատ գեղեցիկ սխալ է։ Այո, ես գիտեմ, որ տարօրինակ պատկերացումներ ունեմ գեղեցկության մասին :):
Այժմ, ըստ
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0
Գործնական տեսանկյունից նման պայմանը իմաստ չունի, քանի որ այն կարող է կրճատվել.
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())
Սա ակնհայտ սխալ է։ Ամենայն հավանականությամբ, նրանք ցանկացել են համեմատել 0/1-ը փոփոխականի հետ ինդեքս. Կոդը շտկելու համար անհրաժեշտ է եռակի օպերատորի շուրջ փակագծեր ավելացնել.
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))
Ի դեպ, եռակի օպերատորը շատ վտանգավոր է և տրամաբանական սխալներ է առաջացնում։ Շատ զգույշ եղեք դրա հետ և մի ագահ մի եղեք փակագծերով։ Ես ավելի մանրամասն նայեցի այս թեմային
Հատված N4, N5. Զուր ցուցիչ
Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
....
TypedInit *LHS = dyn_cast<TypedInit>(Result);
....
LHS = dyn_cast<TypedInit>(
UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get())
->Fold(CurRec));
if (!LHS) {
Error(PasteLoc, Twine("can't cast '") + LHS->getAsString() +
"' to string");
return nullptr;
}
....
}
PVS-Studio նախազգուշացում.
Եթե ցուցիչը LHS զրոյական է, նախազգուշացում պետք է արվի. Այնուամենայնիվ, փոխարենը, այս նույն զրոյական ցուցիչը կհեռացվի՝ LHS->getAsString().
Սա շատ բնորոշ իրավիճակ է, երբ սխալը թաքնված է սխալ մշակողի մեջ, քանի որ ոչ ոք չի ստուգում դրանք: Ստատիկ անալիզատորները ստուգում են բոլոր հասանելի ծածկագրերը, անկախ նրանից, թե որքան հաճախ է այն օգտագործվում: Սա շատ լավ օրինակ է, թե ինչպես է ստատիկ վերլուծությունը լրացնում փորձարկման և սխալներից պաշտպանվելու այլ մեթոդներ:
Նմանատիպ ցուցիչի հետ աշխատելու սխալ RHS թույլատրվում է հենց ներքևում գտնվող ծածկագրում. V522 [CWE-476] «RHS» զրոյական ցուցիչի անջատումը կարող է տեղի ունենալ: TGParser.cpp 2186
Հատված N6. ցուցիչի օգտագործումը շարժվելուց հետո
static Expected<bool>
ExtractBlocks(....)
{
....
std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap);
....
BD.setNewProgram(std::move(ProgClone)); // <=
MiscompiledFunctions.clear();
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first); // <=
assert(NewF && "Function not found??");
MiscompiledFunctions.push_back(NewF);
}
....
}
PVS-Studio Զգուշացում. V522 [CWE-476] «ProgClone» զրոյական ցուցիչի անջատում կարող է տեղի ունենալ: Սխալ կազմավորում.cpp 601
Սկզբում խելացի ցուցիչ ProgClone դադարում է տիրապետել օբյեկտին.
BD.setNewProgram(std::move(ProgClone));
Փաստորեն, հիմա ProgClone զրոյական ցուցիչ է: Հետևաբար, զրոյական ցուցիչի հղումը պետք է տեղի ունենա հենց ստորև.
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
Բայց իրականում դա տեղի չի ունենա։ Նկատի ունեցեք, որ օղակն իրականում չի կատարվում:
Տարայի սկզբում Սխալ կազմված գործառույթներ մաքրված:
MiscompiledFunctions.clear();
Հաջորդը, այս կոնտեյների չափը օգտագործվում է հանգույցի վիճակում.
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
Հեշտ է տեսնել, որ հանգույցը չի սկսվում: Կարծում եմ, որ սա նույնպես սխալ է, և կոդը պետք է այլ կերպ գրվի:
Թվում է, թե մենք բախվել ենք սխալների այդ հայտնի հավասարությանը։ Մի սխալը քողարկում է մյուսը :):
Հատված N7. ցուցիչի օգտագործումը շարժվելուց հետո
static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
std::unique_ptr<Module> Safe) {
outs() << " Optimizing functions being tested: ";
std::unique_ptr<Module> Optimized =
BD.runPassesOn(Test.get(), BD.getPassesToRun());
if (!Optimized) {
errs() << " Error running this sequence of passes"
<< " on the input program!n";
BD.setNewProgram(std::move(Test)); // <=
BD.EmitProgressBitcode(*Test, "pass-error", false); // <=
if (Error E = BD.debugOptimizerCrash())
return std::move(E);
return false;
}
....
}
PVS-Studio-ի նախազգուշացում. V522 [CWE-476] «Թեստ» զրոյական ցուցիչի անջատում կարող է տեղի ունենալ: Սխալ կազմավորում.cpp 709
Կրկին նույն իրավիճակը. Սկզբում օբյեկտի պարունակությունը տեղափոխվում է, իսկ հետո այն օգտագործվում է այնպես, կարծես ոչինչ չի եղել։ Ես այս իրավիճակը ավելի ու ավելի հաճախ եմ տեսնում ծրագրային կոդում այն բանից հետո, երբ շարժման իմաստաբանությունը հայտնվեց C++-ում: Ահա թե ինչու ես սիրում եմ C++ լեզուն: Գոյություն ունեն ավելի ու ավելի շատ նոր եղանակներ՝ սեփական ոտքը կրակելու համար: PVS-Studio անալիզատորը միշտ գործ կունենա :):
Հատված N8. Զուր ցուցիչ
void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
uint32_t TypeId = Symbol.getTypeId();
auto Type = Symbol.getSession().getSymbolById(TypeId);
if (Type)
Printer << "<unknown-type>";
else
Type->dump(*this);
}
PVS-Studio-ի նախազգուշացում. V522 [CWE-476] «Տիպ» զրոյական ցուցիչի անջատում կարող է տեղի ունենալ: PrettyFunctionDumper.cpp 233
Սխալների մշակիչներից բացի, վրիպազերծման տպման գործառույթները սովորաբար չեն փորձարկվում: Մեզ մոտ հենց այդպիսի դեպք կա. Ֆունկցիան սպասում է օգտատիրոջը, ով իր խնդիրները լուծելու փոխարեն ստիպված կլինի ուղղել այն։
Ճիշտ:
if (Type)
Type->dump(*this);
else
Printer << "<unknown-type>";
Հատված N9. Զուր ցուցիչ
void SearchableTableEmitter::collectTableEntries(
GenericTable &Table, const std::vector<Record *> &Items) {
....
RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
if (!Ty) // <=
PrintFatalError(Twine("Field '") + Field.Name + "' of table '" +
Table.Name + "' has incompatible type: " +
Ty->getAsString() + " vs. " + // <=
TI->getType()->getAsString());
....
}
PVS-Studio-ի նախազգուշացում. V522 [CWE-476] «Ty» զրոյական ցուցիչի անջատում կարող է տեղի ունենալ: SearchableTableEmitter.cpp 614
Կարծում եմ՝ ամեն ինչ պարզ է և բացատրություն չի պահանջում։
Հատված N10. Տառասխալ
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;
}
PVS-Studio նախազգուշացում.
Անիմաստ է իրեն փոփոխական վերագրելը: Ամենայն հավանականությամբ նրանք ցանկանում էին գրել.
Identifier->Type = Question->Type;
Հատված N11. Կասկածելի ընդմիջում
void SystemZOperand::print(raw_ostream &OS) const {
switch (Kind) {
break;
case KindToken:
OS << "Token:" << getToken();
break;
case KindReg:
OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
break;
....
}
PVS-Studio նախազգուշացում.
Սկզբում շատ կասկածելի օպերատոր կա կոտրել. Մոռացե՞լ եք այստեղ ուրիշ բան գրել։
Հատված N12. ցուցիչի ստուգում հղումից հեռացնելուց հետո
InlineCost AMDGPUInliner::getInlineCost(CallSite CS) {
Function *Callee = CS.getCalledFunction();
Function *Caller = CS.getCaller();
TargetTransformInfo &TTI = TTIWP->getTTI(*Callee);
if (!Callee || Callee->isDeclaration())
return llvm::InlineCost::getNever("undefined callee");
....
}
PVS-Studio նախազգուշացում.
Ինդեքսը Կալլի սկզբում անջատված է ֆունկցիան կանչելու պահին ստանալTTI.
Եվ հետո պարզվում է, որ այս ցուցիչը պետք է ստուգվի հավասարության համար nullptr:
if (!Callee || Callee->isDeclaration())
Բայց արդեն ուշ է…
Հատված N13 - N...՝ ցուցիչի ստուգում հղումից հեռացնելուց հետո
Նախորդ կոդի հատվածում քննարկված իրավիճակը եզակի չէ։ Այստեղ հայտնվում է.
static Value *optimizeDoubleFP(CallInst *CI, IRBuilder<> &B,
bool isBinary, bool isPrecise = false) {
....
Function *CalleeFn = CI->getCalledFunction();
StringRef CalleeNm = CalleeFn->getName(); // <=
AttributeList CalleeAt = CalleeFn->getAttributes();
if (CalleeFn && !CalleeFn->isIntrinsic()) { // <=
....
}
PVS-Studio նախազգուշացում. V595 [CWE-476] «CalleeFn» ցուցիչը օգտագործվել է նախքան այն ստուգվելը nullptr-ի հետ: Ստուգեք տողերը՝ 1079, 1081: SimplifyLibCalls.cpp 1079
Եվ ահա.
void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
const Decl *Tmpl, Decl *New,
LateInstantiatedAttrVec *LateAttrs,
LocalInstantiationScope *OuterMostScope) {
....
NamedDecl *ND = dyn_cast<NamedDecl>(New);
CXXRecordDecl *ThisContext =
dyn_cast_or_null<CXXRecordDecl>(ND->getDeclContext()); // <=
CXXThisScopeRAII ThisScope(*this, ThisContext, Qualifiers(),
ND && ND->isCXXInstanceMember()); // <=
....
}
PVS-Studio նախազգուշացում. V595 [CWE-476] «ND» ցուցիչը օգտագործվել է նախքան այն ստուգվելը nullptr-ի հետ: Ստուգեք տողերը՝ 532, 534: SemaTemplateInstantiateDecl.cpp 532
Եվ ահա.
- V595 [CWE-476] «U» ցուցիչը օգտագործվել է նախքան nullptr-ի հետ ստուգումը: Ստուգեք տողերը՝ 404, 407: DWARFormValue.cpp 404
- V595 [CWE-476] «ND» ցուցիչը օգտագործվել է նախքան nullptr-ի հետ ստուգումը: Ստուգեք տողերը՝ 2149, 2151: SemaTemplateInstantiate.cpp 2149
Եվ հետո ես չհետաքրքրվեցի ուսումնասիրել V595 համարով նախազգուշացումները։ Այսպիսով, ես չգիտեմ, արդյոք կան ավելի շատ նմանատիպ սխալներ, բացի այստեղ թվարկվածներից: Ամենայն հավանականությամբ կա։
Հատված N17, N18՝ Կասկածելի տեղաշարժ
static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
uint64_t &Encoding) {
....
unsigned Size = RegSize;
....
uint64_t NImms = ~(Size-1) << 1;
....
}
PVS-Studio նախազգուշացում.
Հնարավոր է, որ դա վրիպակ չէ, և կոդը աշխատում է ճիշտ այնպես, ինչպես նախատեսված է: Բայց սա ակնհայտորեն շատ կասկածելի վայր է և պետք է ստուգվի:
Ասենք փոփոխականը չափ հավասար է 16-ի, այնուհետև կոդի հեղինակը նախատեսել է այն ստանալ փոփոխականում NImms իմաստը:
1111111111111111111111111111111111111111111111111111111111100000
Այնուամենայնիվ, իրականում արդյունքը կլինի.
0000000000000000000000000000000011111111111111111111111111100000
Փաստն այն է, որ բոլոր հաշվարկները կատարվում են 32-բիթանոց չստորագրված տիպի միջոցով: Եվ միայն այն ժամանակ, այս 32-բիթանոց անստորագիր տեսակը անուղղակիորեն կընդլայնվի uint64_t. Այս դեպքում ամենակարևոր բիթերը կլինեն զրո:
Դուք կարող եք շտկել իրավիճակը հետևյալ կերպ.
uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
Նմանատիպ իրավիճակ. V629 [CWE-190] Մտածեք ստուգելու «Immr << 6» արտահայտությունը: 32-բիթանոց արժեքի բիթային տեղաշարժ՝ 64-բիթանոց տիպի հետագա ընդլայնմամբ: AArch64AddressingModes.h 269
Հատված N19. Բացակայում է հիմնաբառը ուրիշ?
void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
....
if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
// VOP2b (v_add_u32, v_sub_u32 ...) dpp use "vcc" token.
// Skip it.
continue;
} if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) { // <=
Op.addRegWithFPInputModsOperands(Inst, 2);
} else if (Op.isDPPCtrl()) {
Op.addImmOperands(Inst, 1);
} else if (Op.isImm()) {
// Handle optional arguments
OptionalIdx[Op.getImmTy()] = I;
} else {
llvm_unreachable("Invalid operand type");
}
....
}
PVS-Studio նախազգուշացում.
Այստեղ ոչ մի սխալ չկա։ Առաջինի այն ժամանակվա բլոկից ի վեր if ավարտվում է շարունակել, ուրեմն կապ չունի, բանալի բառ կա ուրիշ կամ ոչ. Ամեն դեպքում, կոդը կաշխատի նույն կերպ: Դեռ բաց թողնված ուրիշ ծածկագիրը դարձնում է ավելի անհասկանալի և վտանգավոր: Եթե ապագայում շարունակել անհետանում է, կոդը կսկսի գործել բոլորովին այլ կերպ: Իմ կարծիքով ավելի լավ է ավելացնել ուրիշ.
Հատված N20. Նույն տիպի չորս տառասխալներ
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;
}
PVS-Studio նախազգուշացումներ.
- V655 [CWE-480] Տողերը միացված էին, բայց չեն օգտագործվում: Մտածեք ստուգելու «Արդյունք + Name.str()» արտահայտությունը: Symbol.cpp 32
- V655 [CWE-480] Տողերը միացված էին, բայց չեն օգտագործվում: Ստուգեք «Արդյունք + «(ObjC Class)» + Name.str()» արտահայտությունը: Symbol.cpp 35
- V655 [CWE-480] Տողերը միացված էին, բայց չեն օգտագործվում: Ստուգեք «Արդյունք + «(ObjC Class EH)» + Name.str()» արտահայտությունը: Symbol.cpp 38
- V655 [CWE-480] Տողերը միացված էին, բայց չեն օգտագործվում: Ստուգեք «Արդյունք + «(ObjC IVar)» + Name.str()» արտահայտությունը: Symbol.cpp 41
Պատահաբար += օպերատորի փոխարեն օգտագործվում է + օպերատորը: Արդյունքը ձևավորումներ են, որոնք զուրկ են իմաստից:
Հատված N21. Չսահմանված վարքագիծ
static void getReqFeatures(std::map<StringRef, int> &FeaturesMap,
const std::vector<Record *> &ReqFeatures) {
for (auto &R : ReqFeatures) {
StringRef AsmCondString = R->getValueAsString("AssemblerCondString");
SmallVector<StringRef, 4> Ops;
SplitString(AsmCondString, Ops, ",");
assert(!Ops.empty() && "AssemblerCondString cannot be empty");
for (auto &Op : Ops) {
assert(!Op.empty() && "Empty operator");
if (FeaturesMap.find(Op) == FeaturesMap.end())
FeaturesMap[Op] = FeaturesMap.size();
}
}
}
Փորձեք ինքներդ գտնել վտանգավոր ծածկագիրը: Եվ սա նկար է ուշադրությունը շեղելու համար, որպեսզի անմիջապես չնայես պատասխանին.
PVS-Studio նախազգուշացում.
Խնդրի գիծ.
FeaturesMap[Op] = FeaturesMap.size();
Եթե տարր Op չի գտնվել, այնուհետև քարտեզում ստեղծվում է նոր տարր և այնտեղ գրվում է այս քարտեզի տարրերի քանակը: Պարզապես անհայտ է, թե արդյոք գործառույթը կկանչվի Չափս նոր տարր ավելացնելուց առաջ կամ հետո:
Հատված N22-N24. Կրկնվող առաջադրանքներ
Error MachOObjectFile::checkSymbolTable() const {
....
} else {
MachO::nlist STE = getSymbolTableEntry(SymDRI);
NType = STE.n_type; // <=
NType = STE.n_type; // <=
NSect = STE.n_sect;
NDesc = STE.n_desc;
NStrx = STE.n_strx;
NValue = STE.n_value;
}
....
}
PVS-Studio նախազգուշացում.
Չեմ կարծում, որ այստեղ իրական սխալ կա: Պարզապես անհարկի կրկնվող հանձնարարություն։ Բայց դեռ կոպիտ սխալ.
Նմանապես:
- V519 [CWE-563] «B.NDesc» փոփոխականին տրվում են արժեքներ երկու անգամ հաջորդաբար: Միգուցե սա սխալմունք է։ Ստուգեք տողերը՝ 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] Փոփոխականին տրվում են արժեքներ երկու անգամ հաջորդաբար: Միգուցե սա սխալմունք է։ Ստուգեք տողերը՝ 59, 61. coff2yaml.cpp 61
Հատված N25-N27. Ավելի շատ վերահանձնումներ
Հիմա եկեք տեսնենք վերահանձնման մի փոքր այլ տարբերակ:
bool Vectorizer::vectorizeLoadChain(
ArrayRef<Instruction *> Chain,
SmallPtrSet<Instruction *, 16> *InstructionsProcessed) {
....
unsigned Alignment = getAlignment(L0);
....
unsigned NewAlign = getOrEnforceKnownAlignment(L0->getPointerOperand(),
StackAdjustedAlignment,
DL, L0, nullptr, &DT);
if (NewAlign != 0)
Alignment = NewAlign;
Alignment = NewAlign;
....
}
PVS-Studio-ի նախազգուշացում՝ V519 [CWE-563] «Հավասարեցում» փոփոխականին նշանակվում են արժեքներ երկու անգամ հաջորդաբար: Միգուցե սա սխալմունք է։ Ստուգեք տողերը՝ 1158, 1160: LoadStoreVetorizer.cpp 1160
Սա շատ տարօրինակ կոդ է, որը, ըստ երևույթին, պարունակում է տրամաբանական սխալ: Սկզբում փոփոխական Հավասարեցում արժեք է նշանակվում՝ կախված պայմանից: Եվ հետո հանձնարարությունը կրկին տեղի է ունենում, բայց հիմա առանց ստուգման:
Նմանատիպ իրավիճակներ կարելի է տեսնել այստեղ.
- V519 [CWE-563] «Էֆեկտներ» փոփոխականին տրվում են արժեքներ երկու անգամ հաջորդաբար: Միգուցե սա սխալմունք է։ Ստուգեք տողերը՝ 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] «ExpectNoDerefChunk» փոփոխականին տրվում են արժեքներ երկու անգամ հաջորդաբար: Միգուցե սա սխալմունք է։ Ստուգեք տողերը՝ 4970, 4973. SemaType.cpp 4973
Հատված N28. Միշտ ճիշտ վիճակ
static int readPrefixes(struct InternalInstruction* insn) {
....
uint8_t byte = 0;
uint8_t nextByte;
....
if (byte == 0xf3 && (nextByte == 0x88 || nextByte == 0x89 ||
nextByte == 0xc6 || nextByte == 0xc7)) {
insn->xAcquireRelease = true;
if (nextByte != 0x90) // PAUSE instruction support // <=
break;
}
....
}
PVS-Studio նախազգուշացում.
Ստուգելն իմաստ չունի։ Փոփոխական հաջորդԲայթ միշտ չէ, որ հավասար է արժեքին 0x90, որը բխում է նախորդ ստուգումից։ Սա ինչ-որ տրամաբանական սխալ է:
Հատված N29 - N...՝ Միշտ ճիշտ/կեղծ պայմաններ
Անալիզատորը տալիս է բազմաթիվ նախազգուշացումներ, որ ամբողջ վիճակը (
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;
....
}
PVS-Studio նախազգուշացում.
0xE հաստատունը տասնորդական 14 արժեքն է: Փորձաքննություն RegNo == 0xe իմաստ չունի, քանի որ եթե RegNo > 13, ապա ֆունկցիան կավարտի իր կատարումը։
Կային բազմաթիվ այլ նախազգուշացումներ V547 և V560 ID-ներով, բայց ինչպես և
Ես ձեզ օրինակ կտամ, թե ինչու է այս հրահրող գործոնների ուսումնասիրությունը ձանձրալի: Անալիզատորը միանգամայն իրավացի է հետևյալ կոդի համար նախազգուշացում տալով. Բայց սա սխալ չէ։
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
tok::TokenKind ClosingBraceKind) {
bool HasError = false;
....
HasError = true;
if (!ContinueOnSemicolons)
return !HasError;
....
}
PVS-Studio Զգուշացում. V547 [CWE-570] «!HasError» արտահայտությունը միշտ կեղծ է: UnwrappedLineParser.cpp 1635
Հատված N30. Կասկածելի վերադարձ
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();
}
....
}
PVS-Studio նախազգուշացում.
Սա կա՛մ սխալ է, կա՛մ հատուկ տեխնիկա, որը կոչված է ինչ-որ բան բացատրելու ծածկագիրը կարդացող ծրագրավորողներին: Այս դիզայնը ինձ ոչինչ չի բացատրում և շատ կասկածելի է թվում: Ավելի լավ է այդպես չգրես :)։
Հոգնե՞լ եք: Հետո թեյ կամ սուրճ պատրաստելու ժամանակն է։
Նոր ախտորոշմամբ հայտնաբերված թերությունները
Հին դիագնոստիկայի 30 ակտիվացումը կարծում եմ բավական է։ Հիմա տեսնենք, թե ինչ հետաքրքիր բաներ կարելի է գտնել անալիզատորում հետո հայտնված նոր ախտորոշմամբ
Հատված N31. անհասանելի ծածկագիր
Error CtorDtorRunner::run() {
....
if (auto CtorDtorMap =
ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
NoDependenciesToRegister, true))
{
....
return Error::success();
} else
return CtorDtorMap.takeError();
CtorDtorsByPriority.clear();
return Error::success();
}
PVS-Studio նախազգուշացում.
Ինչպես տեսնում եք, օպերատորի երկու մասնաճյուղերը if ավարտվում է օպերատորին զանգով վերադարձնել. Համապատասխանաբար, բեռնարկղը CtorDtorsByPriority երբեք չի մաքրվի:
Հատված N32. անհասանելի ծածկագիր
bool LLParser::ParseSummaryEntry() {
....
switch (Lex.getKind()) {
case lltok::kw_gv:
return ParseGVEntry(SummaryID);
case lltok::kw_module:
return ParseModuleEntry(SummaryID);
case lltok::kw_typeid:
return ParseTypeIdEntry(SummaryID); // <=
break; // <=
default:
return Error(Lex.getLoc(), "unexpected summary kind");
}
Lex.setIgnoreColonInIdentifiers(false); // <=
return false;
}
PVS-Studio նախազգուշացում՝ V779 [CWE-561] Հայտնաբերվել է անհասանելի կոդ: Հնարավոր է, որ սխալ կա: LLParser.cpp 835
Հետաքրքիր իրավիճակ. Եկեք նախ նայենք այս վայրին.
return ParseTypeIdEntry(SummaryID);
break;
Առաջին հայացքից թվում է, թե այստեղ սխալ չկա։ Կարծես օպերատորին կոտրել այստեղ կա լրացուցիչ մեկը, և դուք կարող եք պարզապես ջնջել այն: Այնուամենայնիվ, ամեն ինչ այնքան էլ պարզ չէ:
Անալիզատորը նախազգուշացում է տալիս տողերի վրա.
Lex.setIgnoreColonInIdentifiers(false);
return false;
Եվ իսկապես, այս կոդը անհասանելի է: Բոլոր դեպքերը անցնել ավարտվում է օպերատորի զանգով վերադարձնել. Իսկ հիմա մենակ անիմաստ կոտրել այնքան էլ անվնաս չի թվում: Թերևս ճյուղերից մեկը պետք է ավարտվի դրանով կոտրելբայց ոչ վերադարձնել?
Հատված N33. Բարձր բիթերի պատահական վերակայում
unsigned getStubAlignment() override {
if (Arch == Triple::systemz)
return 8;
else
return 1;
}
Expected<unsigned>
RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
const SectionRef &Section,
bool IsCode) {
....
uint64_t DataSize = Section.getSize();
....
if (StubBufSize > 0)
DataSize &= ~(getStubAlignment() - 1);
....
}
PVS-Studio նախազգուշացում.
Խնդրում ենք նկատի ունենալ, որ գործառույթը getStubAlignment վերադարձնում է տեսակը չստորագրված. Եկեք հաշվարկենք արտահայտության արժեքը՝ ենթադրելով, որ ֆունկցիան վերադարձնում է 8 արժեքը.
~(getStubAlignment() - 1)
~ (8u-1)
0xFFFFFFFF8u
Այժմ ուշադրություն դարձրեք, որ փոփոխականը Տվյալների չափը ունի 64-բիթանոց անստորագիր տեսակ: Ստացվում է, որ DataSize & 0xFFFFFFF8u գործողությունը կատարելիս բոլոր երեսուներկու բարձր կարգի բիթերը կզրոյացվեն: Ամենայն հավանականությամբ, սա այն չէ, ինչ ցանկանում էր ծրագրավորողը։ Ես կասկածում եմ, որ նա ցանկացել է հաշվարկել՝ DataSize & 0xFFFFFFFFFFFFFFFFFF8u։
Սխալը շտկելու համար դուք պետք է գրեք սա.
DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);
Կամ այսպես.
DataSize &= ~(getStubAlignment() - 1ULL);
Հատված N34. Չհաջողվեց բացահայտ տիպի քաղվածք
template <typename T>
void scaleShuffleMask(int Scale, ArrayRef<T> Mask,
SmallVectorImpl<T> &ScaledMask) {
assert(0 < Scale && "Unexpected scaling factor");
int NumElts = Mask.size();
ScaledMask.assign(static_cast<size_t>(NumElts * Scale), -1);
....
}
PVS-Studio նախազգուշացում.
Բացահայտ տիպի ձուլումը օգտագործվում է տիպի փոփոխականները բազմապատկելիս վարարումից խուսափելու համար int. Այնուամենայնիվ, բացահայտ տիպի ձուլումն այստեղ չի պաշտպանում արտահոսքից: Նախ, փոփոխականները կբազմապատկվեն, և միայն դրանից հետո բազմապատկման 32-բիթանոց արդյունքը կընդլայնվի մինչև տեսակի.
Հատված N35. Չհաջողվեց 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;
}
....
}
Այս նոր հետաքրքիր ախտորոշումը բացահայտում է իրավիճակներ, երբ կոդի մի կտոր պատճենվել է, և դրա մեջ որոշ անուններ սկսել են փոխվել, բայց մի տեղ չեն ուղղել այն:
Խնդրում ենք նկատի ունենալ, որ երկրորդ բլոկում դրանք փոխվել են Op0 մասին Op1. Բայց մի տեղ չեն ուղղել. Ամենայն հավանականությամբ այն պետք է գրվեր այսպես.
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
return &I;
}
Հատված N36. Փոփոխական շփոթություն
struct Status {
unsigned Mask;
unsigned Mode;
Status() : Mask(0), Mode(0){};
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
Mode &= Mask;
};
....
};
PVS-Studio նախազգուշացում.
Շատ վտանգավոր է ֆունկցիայի արգումենտներին տալ դասի անդամների նույն անունները: Շատ հեշտ է շփոթվել։ Մեզ մոտ հենց այդպիսի դեպք կա. Այս արտահայտությունը իմաստ չունի.
Mode &= Mask;
Ֆունկցիայի փաստարկը փոխվում է: Այսքանը: Այս փաստարկն այլևս չի օգտագործվում: Ամենայն հավանականությամբ, դուք պետք է գրեիք այսպես.
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
this->Mode &= Mask;
};
Հատված N37. Փոփոխական շփոթություն
class SectionBase {
....
uint64_t Size = 0;
....
};
class SymbolTableSection : public SectionBase {
....
};
void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
SectionBase *DefinedIn, uint64_t Value,
uint8_t Visibility, uint16_t Shndx,
uint64_t Size) {
....
Sym.Value = Value;
Sym.Visibility = Visibility;
Sym.Size = Size;
Sym.Index = Symbols.size();
Symbols.emplace_back(llvm::make_unique<Symbol>(Sym));
Size += this->EntrySize;
}
Զգուշացում PVS-Studio՝ V1001 [CWE-563] «Չափ» փոփոխականը նշանակված է, բայց գործառույթի ավարտին չի օգտագործվում: Object.cpp 424
Իրավիճակը նման է նախորդին. Պետք է գրել.
this->Size += this->EntrySize;
Հատված N38-N47. Մոռացել են ստուգել ցուցանիշը
Նախկինում մենք դիտարկել ենք ախտորոշիչ հրահրման օրինակներ
int getGEPCost(Type *PointeeType, const Value *Ptr,
ArrayRef<const Value *> Operands) {
....
if (Ptr != nullptr) { // <=
assert(....);
BaseGV = dyn_cast<GlobalValue>(Ptr->stripPointerCasts());
}
bool HasBaseReg = (BaseGV == nullptr);
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType()); // <=
....
}
PVS-Studio-ի նախազգուշացում. V1004 [CWE-476] «Ptr» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 729, 738. TargetTransformInfoImpl.h 738
Փոփոխական Պտր կարող է հավասար լինել nullptr, ինչպես վկայում է ստուգումը.
if (Ptr != nullptr)
Այնուամենայնիվ, այս ցուցիչից ներքևում նշված է առանց նախնական ստուգման.
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());
Դիտարկենք նմանատիպ մեկ այլ դեպք.
llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
bool Stub) {
....
auto *FD = dyn_cast<FunctionDecl>(GD.getDecl());
SmallVector<QualType, 16> ArgTypes;
if (FD) // <=
for (const ParmVarDecl *Parm : FD->parameters())
ArgTypes.push_back(Parm->getType());
CallingConv CC = FD->getType()->castAs<FunctionType>()->getCallConv(); // <=
....
}
PVS-Studio-ի նախազգուշացում. V1004 [CWE-476] «FD» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 3228, 3231: CGDebugInfo.cpp 3231
Ուշադրություն դարձրեք նշանին FD. Համոզված եմ, որ խնդիրը հստակ տեսանելի է, և հատուկ բացատրություն չի պահանջվում։
Եվ հետագայում.
static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result,
Value *&BasePtr,
const DataLayout &DL) {
PointerType *PtrTy = dyn_cast<PointerType>(Ptr.getType());
if (!PtrTy) { // <=
Result = Polynomial();
BasePtr = nullptr;
}
unsigned PointerBits =
DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace()); // <=
....
}
PVS-Studio-ի նախազգուշացում. V1004 [CWE-476] «PtrTy» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 960, 965. InterleavedLoadCombinePass.cpp 965
Ինչպե՞ս պաշտպանվել ձեզ նման սխալներից: Ավելի ուշադիր եղեք Code-Review-ում և օգտագործեք PVS-Studio ստատիկ անալիզատորը՝ կանոնավոր կերպով ստուգելու ձեր կոդը:
Այս տեսակի սխալներով այլ կոդերի հատվածներ մեջբերելու իմաստ չկա: Հոդվածում ես կթողնեմ միայն նախազգուշացումների ցանկը.
- V1004 [CWE-476] «Expr» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 1049, 1078: DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] «PI» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 733, 753: LegacyPassManager.cpp 753
- V1004 [CWE-476] «StatepointCall» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] «RV» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 2263, 2268։ TGParser.cpp 2268
- V1004 [CWE-476] «CalleeFn» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 1081, 1096: SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] «TC» ցուցիչը օգտագործվել է անապահով այն բանից հետո, երբ այն հաստատվել է nullptr-ի հետ: Ստուգեք տողերը՝ 1819, 1824: Driver.cpp 1824
Հատված N48-N60. Ոչ թե կրիտիկական, այլ թերություն (հնարավոր հիշողության արտահոսք)
std::unique_ptr<IRMutator> createISelMutator() {
....
std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
Strategies.emplace_back(
new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
....
}
PVS-Studio նախազգուշացում.
Նման տարայի վերջում տարր ավելացնելու համար std::վեկտոր > չես կարող ուղղակի գրել xxx.push_back (նոր X), քանի որ անուղղակի փոխակերպում չկա X* в std::unique_ptr.
Ընդհանուր լուծում գրելն է xxx.emplace_back (նոր X)քանի որ այն կազմում է մեթոդ emplace_back կառուցում է տարր ուղղակիորեն իր փաստարկներից և, հետևաբար, կարող է օգտագործել բացահայտ կոնստրուկտորներ:
Դա անվտանգ չէ։ Եթե վեկտորը լի է, ապա հիշողությունը վերաբաշխվում է: Հիշողության վերաբաշխման գործողությունը կարող է ձախողվել, ինչի արդյունքում բացառություն է բացվում std::bad_alloc. Այս դեպքում ցուցիչը կկորչի, և ստեղծված օբյեկտը երբեք չի ջնջվի:
Անվտանգ լուծում ստեղծելն է unique_ptrորը կունենա ցուցիչ, նախքան վեկտորը կփորձի վերաբաշխել հիշողությունը.
xxx.push_back(std::unique_ptr<X>(new X))
Քանի որ C++14, դուք կարող եք օգտագործել 'std::make_unique':
xxx.push_back(std::make_unique<X>())
Այս տեսակի թերությունը կարևոր չէ LLVM-ի համար: Եթե հիշողությունը հնարավոր չէ հատկացնել, կոմպիլյատորը պարզապես կդադարի: Այնուամենայնիվ, երկարատև հավելվածների համար
Այսպիսով, չնայած այս կոդը գործնական վտանգ չի ներկայացնում LLVM-ի համար, ես օգտակար գտա խոսել այս սխալի օրինակի մասին, և որ PVS-Studio անալիզատորը սովորել է նույնականացնել այն:
Այս տեսակի այլ նախազգուշացումներ.
- V1023 [CWE-460] «Անցումներ» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ PassManager.h 546
- V1023 [CWE-460] «AAs» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ AliasAnalysis.h 324
- V1023 [CWE-460] «Entries» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ DWARFDebugFrame.cpp 519
- V1023 [CWE-460] «AllEdges» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ CFGMST.h 268
- V1023 [CWE-460] «VMaps» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ SimpleLoopUnswitch.cpp 2012 թ
- V1023 [CWE-460] «Records» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ FDRLogBuilder.h 30
- V1023 [CWE-460] «PendingSubmodules» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ ModuleMap.cpp 810
- V1023 [CWE-460] «Օբյեկտներ» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ DebugMap.cpp 88
- V1023 [CWE-460] «Ստրատեգիաներ» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 685
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 686
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 688
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 689
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 690
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 691
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 692
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 693
- V1023 [CWE-460] «Modifiers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ llvm-stress.cpp 694
- V1023 [CWE-460] «Operands» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ GlobalISelEmitter.cpp 1911 թ
- V1023 [CWE-460] «Stash» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] «Matchers» կոնտեյների մեջ առանց սեփականատիրոջ ցուցիչ է ավելացվել «emplace_back» մեթոդով: Բացառության դեպքում հիշողության արտահոսք տեղի կունենա։ GlobalISelEmitter.cpp 2702
Ամփոփում
Ընդհանուր առմամբ 60 նախազգուշացում եմ արել, հետո դադարեցրել: Կա՞ն այլ թերություններ, որոնք PVS-Studio անալիզատորը հայտնաբերում է LLVM-ում: Այո, ունեմ. Այնուամենայնիվ, երբ ես հոդվածի համար ծածկագրեր էի գրում, ուշ երեկո էր, ավելի ճիշտ՝ նույնիսկ գիշեր, և ես որոշեցի, որ ժամանակն է այն անվանել օր։
Հուսով եմ, որ ձեզ հետաքրքիր է թվում և կցանկանաք փորձել PVS-Studio անալիզատորը:
Դուք կարող եք ներբեռնել անալիզատորը և ստանալ ականակիր ստեղնը այստեղից
Ամենակարևորը, պարբերաբար օգտագործեք ստատիկ վերլուծություն: Միանգամյա ստուգումներ, մեր կողմից իրականացված ստատիկ վերլուծության մեթոդաբանությունը հանրահռչակելու նպատակով և PVS-Studio-ն նորմալ սցենար չեն։
Հաջողություն ձեր կոդի որակի և հուսալիության բարելավման գործում:
Եթե ցանկանում եք կիսվել այս հոդվածով անգլիախոս լսարանի հետ, խնդրում ենք օգտագործել թարգմանության հղումը՝ Անդրեյ Կարպով:
Source: www.habr.com