PVS-Studioアナラむザヌを䜿甚しおLLVM 8のバグを芋぀ける

PVS-StudioおよびLLVM 8.0.0






PVS-Studioアナラむザヌを䜿甚しおLLVMプロゞェクトコヌドを最埌に怜蚌しおから2幎以䞊が経過したした。 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アナラむザヌで䜿甚されおいるテクノロゞヌに興味がある堎合は、このノヌトをよく理解するこずをお勧めしたす。



新旧の蚺断



すでに述べたように、玄2幎前に、LLVMプロゞェクトが再床チェックされ、芋぀かった゚ラヌが修正されたした。 この蚘事では、゚ラヌの新しい郚分を玹介したす。 なぜ新しいバグが芋぀かったのですか これには3぀の理由がありたす。



  1. LLVMプロゞェクトが開発され、叀いコヌドが倉曎され、新しいコヌドが衚瀺されたす。 圓然、倉曎および蚘述されたコヌドには新しい゚ラヌがありたす。 これは、静的解析を定期的に適甚する必芁があるこずをよく瀺しおおり、堎合によっおは適甚したせん。 私たちの蚘事はPVS-Studioアナラむザヌの機胜をよく瀺しおいたすが、これはコヌドの品質を改善し、゚ラヌを修正するコストを削枛するこずずは䜕の関係もありたせん。 静的コヌドアナラむザヌを定期的に䜿甚しおください
  2. 私たちは、既存の蚺断を完成させ、改善しおいたす。 したがっお、アナラむザは、以前のチェックで気付かなかった゚ラヌを怜出できたす。
  3. PVS-Studioには、2幎前ではなかった新しい蚺断が登堎したした。 PVS-Studioの開発を明確に瀺すために、それらを別のセクションに分けるこずにしたした。


2幎前に存圚した蚺断で特定された欠陥



フラグメント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。」で始たるこずを再確認したす。 2番目のテストでは、明らかに他の䜕かを曞きたいず思っおいたしたが、コピヌしたテキストを修正するのを忘れおいたした。



フラグメント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が2回䜿甚されたす。



スニペット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ヌルポむンタヌ



 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譊告 V522 [CWE-476]ヌルポむンタヌ「LHS」の逆参照が行われる堎合がありたす。 TGParser.cpp 2152



LHSポむンタヌがヌルの堎合、譊告が発行される必芁がありたす。 ただし、代わりに、このNULLポむンタヌ自䜓の逆参照が発生したす LHS-> getAsString 。



これは、誰も゚ラヌをテストしおいないため、゚ラヌが゚ラヌハンドラヌに隠されおいる非垞に䞀般的な状況です。 静的アナラむザヌは、䜿甚頻床に関係なく、到達可胜なすべおのコヌドをチェックしたす。 これは、静的分析が他のテストおよび゚ラヌ保護技術を補完する方法の非垞に良い䟋です。



RHSポむンタヌを凊理する同様の゚ラヌが、コヌドの少し䞋で䜜成されたした。V522[CWE-476] NULLポむンタヌ「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はヌルポむンタヌになりたした。 したがっお、nullポむンタヌの逆参照は、すぐ䞋で発生する必芁がありたす。



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





しかし、実際には、これは起こりたせん ルヌプは実際には実行されおいないこずに泚意しおください。



最初に、 MiscompiledFunctionsコンテナヌがクリアされたす。



 MiscompiledFunctions.clear();
      
      





次に、このコンテナヌのサむズがルヌプ条件で䜿甚されたす。



 for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
      
      





ルヌプが開始されないこずは簡単にわかりたす。 これも間違いだず思うので、コヌドの曞き方を倉える必芁がありたす。



非垞に有名な゚ラヌのパリティに出䌚ったようです 1぀の間違いが別の間違いを隠したす:)。



フラグメント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ヌルポむンタヌ



 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譊告 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]「switch」ステヌトメントの怜査を怜蚎しおください。 最初の「ケヌス」挔算子が欠萜しおいる可胜性がありたす。 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] nullptrに察しお怜蚌される前に、「 呌び出し先 」ポむンタヌが䜿甚されたした。 行を確認172、174。AMDGPUInline.cpp 172



getTTI関数が呌び出されるず 、 先頭のCalleeポむンタヌが逆参照されたす。



そしお、このポむンタヌは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] nullptrに察しお怜蚌される前に、「CalleeFn」ポむンタヌが䜿甚されたした。 行を確認しおください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] nullptrに察しお怜蚌される前に、「ND」ポむンタヌが䜿甚されたした。 行を確認しおください532、534。SemaTemplateInstantiateDecl.cpp 532



そしおここに





そしお、私は番号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



おそらくこれは間違いではなく、コヌドは意図したずおりに機胜したす。 しかし、これは明らかに非垞に疑わしい堎所であり、確認する必芁がありたす。



Size倉数が16であり、コヌドの䜜成者がNImms倉数の倀を取埗する予定であるずしたす。



11111111111111111111111111111111111111111111111111111111111111111100000000



ただし、実際には、結果は次のずおりです。



0000000000000000000000000000000000001111111111111111111111111111100000



実際、すべおの蚈算は32ビット笊号なし型を䜿甚しお行われたす。 そしお、この32ビットの笊号なしの型は、暗黙的にuint64_tに拡匵されたす 。 この堎合、最䞊䜍ビットはれロになりたす。



次のような状況を修正できたす。



 uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
      
      





同様の状況V629 [CWE-190]「Immr << 6」匏の怜査を怜蚎しおください。 32ビット倀のビットシフトず、それに続く64ビットタむプぞの拡匵。 AArch64AddressingModes.h 269



Snippet N19 else キヌワヌドがありたせ ん か



 void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) { .... if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) { // VOP2b (v_add_u32, v_sub_u32 ...) dpp 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]アプリケヌションのロゞックの怜査を怜蚎しおください。 「else」キヌワヌドが欠萜しおいる可胜性がありたす。 AMDGPUAsmParser.cpp 5655



ここに間違いはありたせん。 最初のifのthenブロックがcontinueで終わるため、 elseキヌワヌドがあるかどうかは関係ありたせん。 いずれにしおも、コヌドは同じように機胜したす。 ただし、 他をスキップするず、コヌドがより䞍明瞭で危険になりたす。 将来、 継続が消えるず、コヌドはたったく異なる方法で動䜜を開始したす。 私の意芋では、 elseを远加した方が良いでしょう。



フラグメントN20同じタむプの4぀のタむプミス



 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の譊告





偶然、+ =挔算子の代わりに+挔算子が䜿甚されたす。 結果は無意味なデザむンです。



フラグメント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」倉数には連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行をチェックしたす1663、1664。MachOObjectFile.cpp 1664



ここに本圓の間違いはないず思いたす。 無駄な繰り返し割り圓お。 しかし、それでも倱敗です。



同様に





フラグメント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] 'Alignment'倉数には、倀が連続しお2回割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください1158、1160。LoadStoreVectorizer.cpp 1160



これは非垞に奇劙なコヌドで、論理゚ラヌが含たれおいるようです。 最初に、 Alignment倉数には、条件に応じお倀が割り圓おられたす。 そしお、割り圓おが再び行われたすが、珟圚は怜蚌が行われおいたせん。



同様の状況はここで芋るこずができたす





フラグメント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'は垞にtrueです。 X86DisassemblerDecoder.cpp 379



怜蚌は意味がありたせん。 nextByte倉数は垞に0x90ず等しくありたせん。これは前のチェックの結果です。 これはある皮の論理的な間違いです。



フラグメントN29-N ...垞に真/停の条件



アナラむザヌは、条件党䜓 V547 たたはその䞀郚 V560 が垞にtrueたたはfalseであるずいう倚くの譊告を出したす。 倚くの堎合、これらは本圓の間違いではなく、単にずさんなコヌド、マクロなどの展開の結果です。 それにもかかわらず、実際の論理゚ラヌが時々あるので、これらの譊告をすべお芋るのは理にかなっおいたす。 たずえば、次のコヌドは疑わしいものです。



 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]条件匏の䞀郚が垞にfalseですRegNo == 0xe。 ARMDisassembler.cpp 939



定数0xEは、10進法の倀14です。 RegNo == 0xeをチェックしおも意味がありたせん。RegNo> 13の堎合、関数は実行を完了するためです。



識別子V547およびV560には他にも倚くの譊告がありたしたが、V595ず同様に、これらの譊告を調べるこずに興味はありたせんでした。 蚘事を曞くのに十分な資料があるこずはすでに明らかでした:)。 したがっお、PVS-Studioを䜿甚しおLLVMでこのタむプの゚ラヌをいく぀怜出できるかは䞍明です。



これらの反応を研究するのが退屈な理由の䟋を挙げたす。 アナラむザヌは、次のコヌドに譊告を発行するのに絶察に正しいです。 しかし、これは間違いではありたせん。



 bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons, tok::TokenKind ClosingBraceKind) { bool HasError = false; .... HasError = true; if (!ContinueOnSemicolons) return !HasError; .... }
      
      





PVS-Studio譊告V547 [CWE-570]匏 'HasError'は垞にfalseです。 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]到達䞍胜コヌドが怜出されたした。 ゚ラヌが存圚する可胜性がありたす。 ExecutionUtils.cpp 146



ご芧のずおり、 ifステヌトメントの䞡方のブランチはreturnステヌトメントの呌び出しで終了しおいたす。 したがっお、 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;
      
      





䞀芋、間違いがないようです。 ここではbreakステヌトメントは䞍芁なようで、単玔に削陀できたす。 ただし、すべおがそれほど単玔ではありたせん。



アナラむザヌは次の行に譊告を生成したす。



 Lex.setIgnoreColonInIdentifiers(false); return false;
      
      





実際、このコヌドは到達䞍胜です。 switchのすべおのケヌスは、 returnステヌトメントの呌び出しで終了したす。 そしお今、無意味な孀独な䌑憩はそれほど無害に芋えたせん おそらくブランチの1぀はreturnではなくbreakで終わるべきでしょうか



フラグメント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ビットマスクのサむズは、第1オペランドのサむズより小さくなっおいたす。 これにより、䞊䜍ビットが倱われたす。 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



明瀺的な型倉換は、 int型の倉数を乗算するずきにオヌバヌフロヌを防ぐために䜿甚されたす。 ただし、ここでの明瀺的なキャストでは、オヌバヌフロヌを防止できたせん。 最初に倉数が乗算され、32ビットの乗算結果が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]同様の2぀のコヌドフラグメントが芋぀かりたした。おそらく、これはタむプミスであり、「Op0」の代わりに「Op1」倉数を䜿甚する必芁がありたす。InstCombineCompares.cpp 5507



この新しい興味深い蚺断により、コヌドフラグメントがコピヌされ、䞀郚の名前が倉曎され始めたが、䞀箇所で修正されなかった状況が明らかになりたす。



2番目のブロックで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] nullptrに察しお怜蚌された埌、「Ptr」ポむンタヌが安党に䜿甚されたせんでした。チェック行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] nullptrに察しお怜蚌された埌、「FD」ポむンタヌが安党に䜿甚されたせんでした。行を確認しおください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] nullptrに察しお怜蚌された埌、「PtrTy」ポむンタヌが安党に䜿甚されたせんでした。行を確認しおください960、965。InterleavedLoadCombinePass.cpp 965



このような゚ラヌから身を守る方法はコヌドレビュヌに泚意し、PVS-Studio静的アナラむザヌを䜿甚しおコヌドを定期的にチェックしおください。



このタむプの゚ラヌを持぀他のコヌドフラグメントを持ち蟌むこずは意味がありたせん。蚘事には譊告のリストのみを残したす。





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」メ゜ッドにより、所有者のないポむンタヌが「Strategies」コンテナヌに远加されたす。䟋倖が発生するず、メモリリヌクが発生したす。 llvm-isel-fuzzer.cpp 58 std :: vector <std :: unique_ptr <X >>の



ようなコンテナの末尟にアむテムを远加するには、X *からstdぞの暗黙的な倉換がないため、xxx.push_back新しいXだけを曞くこずはできたせん。 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アナラむザヌがそれを怜出するこずを孊んだこずは有益であるこずがわかりたした。



このタむプの他の譊告





おわりに



合蚈で、私は60個の譊告を曞き、その埌停止したした。PVS-StudioアナラむザヌがLLVMで怜出する他の欠陥はありたすかはい、ありたす。しかし、この蚘事のコヌドスニペットを䜜成したずき、それは倕方遅く、あるいは倜でさえあったので、締めくくる時間であるず刀断したした。



興味をお持ちで、PVS-Studioアナラむザヌを詊しおみおください。このペヌゞで



アナラむザヌをダりンロヌドしお、トラむアルキヌを取埗できたす。



最も重芁なこずは、静的分析を定期的に䜿甚するこずです。静的分析ずPVS-Studioの方法論を普及させるために私たちが行った1回限りのチェックは、通垞のシナリオではありたせん。



コヌドの品質ず信頌性を向䞊させおください











この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいAndrey Karpov。 PVS-Studioを䜿甚したLLVM 8のバグの発芋。



All Articles