ابحث عن الأخطاء في LLVM 8 باستخدام محلل PVS-Studio

PVS-Studio و LLVM 8.0.0

لقد مر أكثر من عامين على آخر التحقق من رمز مشروع LLVM باستخدام محلل PVS-Studio الخاص بنا. دعنا نتأكد من أن محلل PVS-Studio لا يزال الأداة الرائدة للكشف عن الأخطاء ونقاط الضعف المحتملة. للقيام بذلك ، تحقق من وجود أخطاء جديدة في الإصدار LLVM 8.0.0 وابحث عنها.

المادة المراد كتابتها


أن نكون صادقين ، لم أكن أريد أن أكتب هذا المقال. ليس من المثير للاهتمام أن نكتب عن مشروع قمنا باختباره بالفعل بشكل متكرر ( 1 ، 2 ، 3 ). من الأفضل الكتابة عن شيء جديد ، لكن ليس لدي خيار آخر.

في كل مرة يتم إصدار نسخة جديدة من LLVM أو تحديث Clang Static Analyzer ، تظهر الأسئلة من النوع التالي في بريدنا:

انظر ، لقد تعلمت النسخة الجديدة من Clang Static Analyzer العثور على أخطاء جديدة! يبدو لي أن أهمية استخدام PVS-Studio آخذة في التناقص. يعثر Clang على أخطاء أكثر من ذي قبل ويلحق بقدرات PVS-Studio. ما رأيك في هذا؟

لذلك ، أريد دائمًا أن أجيب على شيء بروح:

نحن لا نجلس خاملاً! لقد قمنا بتحسين كبير في قدرات محلل PVS-Studio. لذلك لا تقلق ، نحن مستمرون في القيادة ، كما كان من قبل.

لسوء الحظ ، هذه إجابة سيئة. لا توجد أدلة على ذلك. وهذا هو السبب في أنني أكتب هذا المقال الآن. لذلك ، تم اختبار LLVM مرة أخرى وتم العثور على مجموعة متنوعة من الأخطاء فيه. تلك التي بدت مثيرة للاهتمام بالنسبة لي ، وسوف تظهر الآن. لا يمكن العثور على هذه الأخطاء من قِبل Clang Static Analyzer (أو من غير الملائم للغاية القيام بها). ونحن نستطيع. لقد وجدت وكتبت كل هذه الأخطاء في إحدى الليالي.

لكن كتابة المقال استمرت لعدة أسابيع. لم أستطع إجبار نفسي على ترتيب كل هذا في شكل نص :).

بالمناسبة ، إذا كنت مهتمًا بالتقنيات المستخدمة في محلل PVS-Studio للكشف عن الأخطاء ونقاط الضعف المحتملة ، أقترح عليك أن تتعرف على هذه الملاحظة .

التشخيصات الجديدة والقديمة


كما لوحظ بالفعل ، منذ حوالي عامين ، تم التحقق من مشروع LLVM مرة أخرى ، وتم تصحيح الأخطاء التي تم العثور عليها. الآن في هذه المقالة سيتم تقديم جزء جديد من الأخطاء. لماذا تم العثور على أخطاء جديدة؟ هناك 3 أسباب لهذا:

  1. تطوير مشروع LLVM ، يتغير الرمز القديم فيه ، ويظهر واحد جديد. بطبيعة الحال ، هناك أخطاء جديدة في التعليمات البرمجية المعدلة والمكتوبة. هذا يدل بشكل جيد على أنه ينبغي تطبيق التحليل الثابت بانتظام ، وليس من حالة إلى أخرى. توضح مقالاتنا قدرات محلل PVS-Studio جيدًا ، ولكن هذا لا علاقة له بتحسين جودة الشفرة وتقليل تكلفة إصلاح الأخطاء. استخدام محلل كود ثابت بانتظام!
  2. نحن بصدد الانتهاء من التشخيصات الحالية وتحسينها. لذلك ، يمكن للمحلل اكتشاف الأخطاء التي لم يتم ملاحظتها أثناء الاختبارات السابقة.
  3. قدم PVS-Studio تشخيصات جديدة ، والتي لم تكن منذ عامين. قررت فصلها إلى قسم منفصل لإظهار تطور PVS-Studio بوضوح.

العيوب التي حددتها التشخيصات التي كانت موجودة قبل عامين


جزء N1: نسخ لصق

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: V501 [CWE-570] هناك Name.startswith تعبيرات فرعية متطابقة ("avx512.mask.permvar.") "إلى اليسار وإلى يمين" || " المشغل. AutoUpgrade.cpp 73

تم التحقق من أن الاسم يبدأ بـ "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: V502 [CWE-783] ربما يعمل المشغل '؟: بطريقة مختلفة عما كان متوقعًا. لدى المشغل '؟:' أولوية أقل من المشغل '=='. PPCTargetTransformInfo.cpp 404

في رأيي ، هذا خطأ جميل جدا. نعم ، أنا أعلم أن لديّ أفكار غريبة عن الجمال :).

الآن ، وفقًا لأولويات المشغلين ، يتم حساب التعبير على النحو التالي:

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

من وجهة نظر عملية ، مثل هذا الشرط لا معنى له ، حيث يمكن اختزاله إلى:

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

هذا خطأ واضح. على الأرجح ، أراد 0/1 المقارنة مع متغير Index . لإصلاح الكود ، أضف أقواس حول المشغل الثلاثي:

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

بالمناسبة ، المشغل الثلاثي خطير للغاية ويستفز الأخطاء المنطقية. كن حذرا للغاية مع ذلك ، ولا تكن جشعًا لوضع أقواس. لقد نظرت في هذا الموضوع بمزيد من التفصيل هنا في الفصل "خوف المشغل؟: وأرفقه بين قوسين".

جزء N4 ، N5: مؤشر Null

 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- الاستوديو: V522 [CWE-476] قد يتم إلغاء تنظيم المؤشر "LHS". TGParser.cpp 2152

إذا كان مؤشر 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" الخاص بالمؤشر الفارغ. Miscompilation.cpp 601

في البداية ، يتوقف المؤشر الذكي ProgClone لامتلاك الكائن:

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

في الواقع ، الآن ProgClone هو مؤشر فارغ. لذلك ، يجب أن يحدث dereference مؤشر فارغة أدناه:

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

ولكن ، في الواقع ، هذا لن يحدث! لاحظ أن الحلقة ليست قيد التشغيل بالفعل.

في البداية ، يتم مسح الحاوية MiscompiledFunctions :

 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] قد يتم إلغاء تحديد مؤشر "اختبار" المؤشر الخالي. Miscompilation.cpp 709

مرة أخرى نفس الوضع. في البداية ، يتم نقل محتويات الكائن ، ثم يتم استخدامه كما لو لم يحدث شيء. أرى هذا الموقف بشكل متزايد في رمز البرنامج ، بعد ظهور دلالات الحركة في C ++. لهذا أنا أحب لغة C ++! هناك طرق أكثر وأكثر لإطلاق النار على ساقك. سوف محلل PVS-Studio تعمل دائما :).

جزء N8: Null Pointer

 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: Null Pointer

 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- الاستوديو: 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: V570 يتم تعيين متغير " المعرف- > النوع" لنفسه. FormatTokenLexer.cpp 249

لا معنى لتعيين متغير لنفسه. على الأرجح أرادوا الكتابة:

 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: V622 [CWE-478] النظر في فحص بيان "التبديل". من المحتمل أن يكون مشغل "الحالة" الأول مفقودًا. SystemZAsmParser.cpp 652

في البداية هناك بيان استراحة مشبوه للغاية. هل نسيت كتابة شيء آخر هنا؟

الجزء 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: V595 [CWE-476] تم استخدام مؤشر "Callee" قبل أن يتم التحقق منه ضد nullptr. خطوط التحقق: 172 ، 174. AMDGPUInline.cpp 172

يتم إلغاء تحديد مؤشر Callee في البداية عند استدعاء دالة getTTI .

ثم اتضح أن هذا المؤشر يجب أن يتم التحقق من عدم المساواة 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. DWARFFormValue.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: V629 [CWE-190] فكر في فحص تعبير '~ (الحجم - 1) << 1'. تحويل البت لقيمة 32 بت مع توسع لاحق إلى نوع 64 بت. AArch64AddressingModes.h 260

ربما هذا ليس خطأ ، والرمز يعمل بالضبط كما هو مقصود. ولكن من الواضح أن هذا مكان مشبوه للغاية ، ويجب التحقق منه.

افترض أن متغير الحجم هو 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: V646 [CWE-670] النظر في فحص منطق التطبيق. من المحتمل أن الكلمة الرئيسية "أخرى" مفقودة. AMDGPUAsmParser.cpp 5655

لا يوجد خطأ هنا. نظرًا لاستمرار الكتل الأولى إذا انتهى بـ ، لا يهم ما إذا كانت هناك كلمة رئيسية أخرى أم لا. في أي حال ، فإن رمز العمل نفسه. ومع ذلك ، فإن فقد شخص آخر يجعل الشفرة أكثر غموضًا وخطورة. إذا استمر الاختفاء في المستقبل ، فسوف تبدأ الشفرة في العمل بطريقة مختلفة تمامًا. في رأيي ، من الأفضل إضافة شيء آخر .

جزء 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) "+ 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: V708 [CWE-758] يتم استخدام الإنشاءات الخطرة: "FeaturesMap [Op] = FeaturesMap.size ()" ، حيث تكون "FeaturesMap" من فئة "map". هذا قد يؤدي إلى سلوك غير محدد. RISCVCompressInstEmitter.cpp 490

خط المشكلة:

 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] تم تعيين متغير "NType" القيم مرتين على التوالي. ربما هذا خطأ. خطوط التحقق: 1663 ، 1664. MachOObjectFile.cpp 1664

أعتقد أنه لا يوجد خطأ حقيقي هنا. مجرد مهمة متكررة لا لزوم لها. ولكن لا يزال خطأ.

وبالمثل:

  • 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. LoadStoreVectorizer.cpp 1160

هذا رمز غريب جدًا يبدو أنه يحتوي على خطأ منطقي. في البداية ، يتم تعيين متغير المحاذاة قيمة حسب الشرط. وبعد ذلك تحدث المهمة مرة أخرى ، ولكن الآن دون أي تحقق.

يمكن رؤية حالات مماثلة هنا:

  • V519 [CWE-563] تم تعيين متغير "Effects" لقيم مرتين متتاليتين. ربما هذا خطأ. خطوط التحقق: 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: V547 [CWE-571] التعبير 'nextByte! = 0x90' صحيح دائمًا. X86DisassemblerDecoder.cpp 379

التحقق لا معنى له. متغير nextByte دائمًا لا يساوي 0x90 ، والذي يتبع من الفحص السابق. هذا هو نوع من الخطأ المنطقي.

جزء N29 - N ...: دائمًا صواب / خطأ

يعطي المحلل الكثير من التحذيرات من أن الحالة بأكملها ( V547 ) أو جزء منها ( V560 ) دائمًا ما يكون صحيحًا أو خاطئًا. غالبًا ما لا تكون هذه أخطاء حقيقية ، ولكنها ببساطة رمز غير دقيق ، نتيجة نشر وحدات الماكرو وما شابه. ومع ذلك ، فمن المنطقي أن ننظر إلى كل هذه التحذيرات ، لأن هناك أخطاء منطقية حقيقية من وقت لآخر. على سبيل المثال ، هذه الشفرة مشبوهة:

 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: V560 [CWE-570] جزء من التعبير الشرطي خطأ دائمًا: RegNo == 0xe. ARMDisassembler.cpp 939

الثابت 0xE هو القيمة 14 في النظام العشري. التحقق من RegNo == 0xe لا معنى له ، لأنه إذا كانت RegNo> 13 ، فستكمل الوظيفة تنفيذها.

كان هناك العديد من التحذيرات الأخرى باستخدام المعرفات V547 و V560 ، لكن ، كما في حالة V595 ، لم أكن مهتمًا بدراسة هذه التحذيرات. كان من الواضح بالفعل أنه كان لدي ما يكفي من المواد لكتابة مقال :). لذلك ، لا يُعرف عدد الأخطاء من هذا النوع التي يمكن اكتشافها في LLVM باستخدام PVS-Studio.

سأقدم مثالا على سبب ممل دراسة هذه الردود. المحلل على حق تماما في إصدار تحذير للرمز التالي. لكن هذا ليس خطأ.

 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: V612 [CWE-670] "عودة" غير مشروطة داخل حلقة. R600OptimizeVectorRegisters.cpp 63

هذا إما خطأ أو خدعة محددة الغرض منها شرح شيء للمبرمجين الذين يقرؤون الكود. هذا التصميم لا يفسر أي شيء لي ، ويبدو مشبوه للغاية. من الأفضل عدم الكتابة بهذا الشكل :).

هل انت متعب ثم حان الوقت لصنع الشاي أو القهوة.

قهوة


العيوب المكتشفة بواسطة التشخيصات الجديدة


أعتقد أن 30 إطلاقًا للتشخيصات القديمة كافية. دعونا الآن نرى ما هو مثير للاهتمام يمكن العثور عليه مع التشخيصات الجديدة التي ظهرت في محلل بعد فحص سابق . في المجموع ، تم إضافة 66 تشخيص للأغراض العامة إلى محلل C ++ خلال هذا الوقت.

جزء 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: تم اكتشاف رمز غير قابل للوصول V779 [CWE-561]. من الممكن وجود خطأ. ExecuteUtils.cpp 146

كما ترون ، ينتهي كلا فرعي العبارة 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: V784 حجم قناع البت أقل من حجم المعامل الأول. هذا سوف يسبب فقدان أعلى بت. RuntimeDyld.cpp 815

لاحظ أن دالة getStubAlignment تُرجع نوعًا غير موقَّع . نحسب قيمة التعبير إذا افترضنا أن الدالة تُرجع القيمة 8:

~ (getStubAlignment () - 1)

~ (8u-1)

0xFFFFFFF8u

الآن لاحظ أن متغير DataSize يحتوي على نوع غير موقعة 64 بت. اتضح أنه أثناء عملية DataSize & 0xFFFFFFF88 ، سيتم إعادة تعيين كل 32 بت عالي الترتيب. على الأرجح ، هذا ليس ما أراده المبرمج. أظن أنه أراد حساب: DataSize & 0xFFFFFFFFFFFFFFFFF8u.

لإصلاح الخطأ ، يجب أن تكتب مثل هذا:

 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: V1028 [CWE-190] احتمال تجاوز السعة. النظر في صب المعاملات من عامل التشغيل "NumElts * Scale" إلى نوع "size_t" ، وليس النتيجة. X86ISelLowering.h 1577

يتم استخدام تحويل النوع الصريح لمنع تجاوز السعة عند ضرب متغيرات type int . ومع ذلك ، الصب الصريح هنا لا يحمي من الفيض.في البداية ، سيتم ضرب المتغيرات ، وعندها فقط سيتم توسيع نتيجة الضرب 32 بت إلى type size_t .

جزء N35: نسخ لصق غير ناجحة

 Instruction *InstCombiner::visitFCmpInst(FCmpInst &I) { .... if (!match(Op0, m_PosZeroFP()) && isKnownNeverNaN(Op0, &TLI)) { I.setOperand(0, ConstantFP::getNullValue(Op0->getType())); return &I; } if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) { I.setOperand(1, ConstantFP::getNullValue(Op0->getType())); // <= return &I; } .... } 

V778 [CWE-682] تم العثور على شظايا رمز مماثلة. ربما ، هذا خطأ مطبعي ويجب استخدام متغير "Op1" بدلاً من "Op0". InstCombineCompares.cpp 5507 يكشف

هذا التشخيص الجديد المثير للاهتمام عن مواقف عندما تم نسخ جزء من الكود وبدأت بعض الأسماء تتغير فيه ، لكن لم يتم تصحيحه في مكان واحد.

يرجى ملاحظة أنه في الكتلة الثانية قاموا بتغيير 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: V1001 [CWE-563] تم تعيين متغير "الوضع" ولكن لا يتم استخدامه بحلول نهاية الوظيفة. SIModeRegister.cpp 48 من

الخطورة جدًا إعطاء وسيطات الوظيفة نفس أسماء أعضاء الفصل. من السهل جدا الحصول على الخلط. أمامنا هو مجرد مثل هذه الحالة. هذا التعبير لا معنى له:

 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: لقد نسيت المؤشر التحقق

، وفي وقت سابق ، درسنا أمثلة على تشغيل تشخيص V595 . جوهرها هو أن المؤشر تم إلغاء ترجمته في البداية ، ثم يتم التحقق منه فقط. إن التشخيص الشاب لـ V1004 هو عكس معناه ، ولكنه يكتشف أيضًا الكثير من الأخطاء. يحدد المواقف التي تم فيها فحص المؤشر في البداية ، ثم نسي القيام بذلك. النظر في مثل هذه الحالات وجدت داخل LLVM.

 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 يمكن أن يكون

متغير Ptr 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

كيف تحمي نفسك من مثل هذه الأخطاء؟ كن حذرًا في مراجعة الشفرة واستخدم محلل ثابت 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] The 'StatepointCall' pointer was used unsafely after it was verified against nullptr. Check lines: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] The 'RV' pointer was used unsafely after it was verified against nullptr. Check lines: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] The 'CalleeFn' pointer was used unsafely after it was verified against nullptr. Check lines: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] The 'TC' pointer was used unsafely after it was verified against nullptr. Check lines: 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: V1023 [CWE-460] يضاف مؤشر بدون مالك إلى حاوية "الاستراتيجيات" بواسطة طريقة "emplace_back". سوف يحدث تسرب للذاكرة في حالة حدوث استثناء. llvm-isel-fuzzer.cpp 58

لإضافة عنصر إلى نهاية الحاوية مثل std :: vector <std :: unique_ptr <X>> لا يمكنك فقط كتابة xxx.push_back (X جديدة) ، لأنه لا يوجد تحويل ضمني من X * إلى std: : unique_ptr <X> .

الحل الشائع هو كتابة 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] A pointer without owner is added to the 'AAs' container by the 'emplace_back' method. A memory leak will occur in case of an exception. AliasAnalysis.h 324
  • V1023 [CWE-460] A pointer without owner is added to the 'Entries' container by the 'emplace_back' method. A memory leak will occur in case of an exception. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] A pointer without owner is added to the 'AllEdges' container by the 'emplace_back' method. A memory leak will occur in case of an exception. CFGMST.h 268
  • V1023 [CWE-460] A pointer without owner is added to the 'VMaps' container by the 'emplace_back' method. A memory leak will occur in case of an exception. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] A pointer without owner is added to the 'Records' container by the 'emplace_back' method. A memory leak will occur in case of an exception. FDRLogBuilder.h 30
  • V1023 [CWE-460] A pointer without owner is added to the 'PendingSubmodules' container by the 'emplace_back' method. A memory leak will occur in case of an exception. ModuleMap.cpp 810
  • V1023 [CWE-460] A pointer without owner is added to the 'Objects' container by the 'emplace_back' method. A memory leak will occur in case of an exception. DebugMap.cpp 88
  • V1023 [CWE-460] A pointer without owner is added to the 'Strategies' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 685
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 686
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 688
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 689
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 690
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 691
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 692
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 693
  • V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 694
  • V1023 [CWE-460] A pointer without owner is added to the 'Operands' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] A pointer without owner is added to the 'Stash' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] A pointer without owner is added to the 'Matchers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 2702

استنتاج


في المجموع ، كتبت 60 تحذيرًا ، توقفت بعدها. هل هناك أي عيوب أخرى يكتشفها محلل PVS-Studio في LLVM؟ نعم هناك. ومع ذلك ، عندما كتبت مقتطفات الشفرة للمقال ، كان ذلك في وقت متأخر من المساء ، أو بالأحرى ، حتى في الليل ، وقررت أن الوقت قد حان للدوران.

أتمنى أن تكون مهتمًا ، وسوف ترغب في تجربة محلل PVS-Studio.

يمكنك تنزيل المحلل والحصول على مفتاح تجريبي في هذه الصفحة .

الأهم من ذلك ، استخدم التحليل الثابت بانتظام. الشيكات لمرة واحدة التي أجريناها من أجل تعميم منهجية التحليل الثابت و PVS-Studio ليست سيناريو عاديًا.

حظا سعيدا في تحسين جودة وموثوقية الكود!



إذا كنت ترغب في مشاركة هذا المقال مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Andrey Karpov. البحث عن الأخطاء في LLVM 8 باستخدام PVS-Studio .

Source: https://habr.com/ru/post/ar450008/


All Articles