PVS-स्टुडियो विश्लेषक प्रयोग गरेर LLVM 8 मा बगहरू खोज्दै

PVS-स्टुडियो विश्लेषक प्रयोग गरेर LLVM 8 मा बगहरू खोज्दै
हाम्रो PVS-स्टुडियो विश्लेषक प्रयोग गरेर LLVM परियोजनाको अन्तिम कोड जाँच भएको दुई वर्ष भन्दा बढी बितिसकेको छ। PVS-स्टुडियो विश्लेषक अझै पनि त्रुटिहरू र सम्भावित कमजोरीहरू पहिचान गर्नको लागि एक प्रमुख उपकरण हो भनेर सुनिश्चित गरौं। यो गर्नको लागि, हामी LLVM 8.0.0 रिलीजमा नयाँ त्रुटिहरू जाँच गर्नेछौं र फेला पार्नेछौं।

लेख लेख्नु पर्छ

इमानदार हुन, म यो लेख लेख्न चाहन्न। हामीले पहिले नै धेरै पटक जाँच गरिसकेका परियोजनाको बारेमा लेख्न यो रोचक छैन (1, 2, 3)। नयाँ कुरा लेख्नु राम्रो हो, तर मसँग विकल्प छैन।

हरेक पटक LLVM को नयाँ संस्करण जारी वा अद्यावधिक हुन्छ क्ल्यांग स्टेटिक विश्लेषक, हामीले हाम्रो मेलमा निम्न प्रकारका प्रश्नहरू प्राप्त गर्छौं:

हेर्नुहोस्, Clang Static Analyzer को नयाँ संस्करणले नयाँ त्रुटिहरू फेला पार्न सिकेको छ! यो PVS-स्टुडियो प्रयोगको प्रासंगिकता घट्दै गएको जस्तो देखिन्छ। Clang ले पहिले भन्दा धेरै त्रुटिहरू फेला पार्छ र PVS-Studio को क्षमताहरूसँग समात्छ। तपाईलाई यस बारे के सोच्नुहुन्छ?

यसको लागि म सधैं केहि जवाफ दिन चाहन्छु:

हामी पनि निष्क्रिय बस्दैनौं! हामीले PVS-स्टुडियो विश्लेषकको क्षमताहरूमा उल्लेखनीय सुधार गरेका छौं। त्यसोभए चिन्ता नगर्नुहोस्, हामी पहिले जस्तै नेतृत्व जारी राख्छौं।

दुर्भाग्यवश, यो एक खराब जवाफ हो। यसमा कुनै प्रमाण छैन। र त्यसैले म अहिले यो लेख लेख्दैछु। त्यसैले एलएलभीएम आयोजनाको पुनः जाँच गरी यसमा विभिन्न त्रुटिहरू भेटिएका छन् । म अब ती देखाउनेछु जुन मलाई रोचक लाग्थ्यो। क्ल्याङ्ग स्टेटिक एनालाइजरले यी त्रुटिहरू फेला पार्न सक्दैन (वा यसको मद्दतले त्यसो गर्न अत्यन्तै असुविधाजनक छ)। तर हामी सक्छौं। यसबाहेक, मैले एक साँझमा यी सबै त्रुटिहरू फेला पारे र लेखे।

तर लेख लेख्न धेरै हप्ता लाग्यो। मैले यो सबै पाठमा राख्न आफैलाई ल्याउन सकिन :)।

खैर, यदि तपाइँ त्रुटिहरू र सम्भावित कमजोरीहरू पहिचान गर्न PVS-स्टुडियो विश्लेषकमा कुन प्रविधिहरू प्रयोग गरिन्छ भन्नेमा रुचि राख्नुहुन्छ भने, म यससँग परिचित हुन सुझाव दिन्छु। नोट.

नयाँ र पुरानो निदान

पहिले नै उल्लेख गरिए अनुसार, लगभग दुई वर्ष पहिले LLVM परियोजना एक पटक फेरि जाँच गरिएको थियो, र फेला परेका त्रुटिहरू सच्याइयो। अब यस लेखले त्रुटिहरूको नयाँ ब्याच प्रस्तुत गर्नेछ। किन नयाँ बगहरू फेला पर्यो? यसका 3 कारणहरू छन्:

  1. LLVM परियोजना विकसित हुँदैछ, पुरानो कोड परिवर्तन गर्दै र नयाँ कोड थप्दैछ। स्वाभाविक रूपमा, परिमार्जित र लिखित कोडमा नयाँ त्रुटिहरू छन्। यसले स्पष्ट रूपमा देखाउँछ कि स्थिर विश्लेषण नियमित रूपमा प्रयोग गरिनुपर्छ, कहिलेकाहीं होइन। हाम्रा लेखहरूले PVS-स्टुडियो विश्लेषकको क्षमताहरू राम्ररी देखाउँछन्, तर यसले कोड गुणस्तर सुधार गर्न र त्रुटिहरू समाधान गर्ने लागत घटाउनेसँग कुनै सरोकार राख्दैन। एक स्थिर कोड विश्लेषक नियमित रूपमा प्रयोग गर्नुहोस्!
  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-स्टुडियो चेतावनी: 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-स्टुडियो: 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-स्टुडियो चेतावनी: 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 लाई चरसँग तुलना गर्न चाहन्थे सूचकांक। कोड फिक्स गर्नको लागि तपाईंले टर्नरी अपरेटरको वरिपरि कोष्ठकहरू थप्नु पर्छ:

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-स्टुडियो चेतावनी: V522 [CWE-476] शून्य सूचक 'LHS' को सन्दर्भ हुन सक्छ। TGParser.cpp 2152

यदि सूचक LHS शून्य छ, चेतावनी जारी गर्नुपर्छ। यद्यपि, यसको सट्टा, यो समान शून्य सूचक dereferenced हुनेछ: 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-स्टुडियो चेतावनी: V522 [CWE-476] नल पोइन्टर 'ProgClone' को सन्दर्भ हुन सक्छ। Miscompilation.cpp 601

सुरुमा एक स्मार्ट सूचक ProgClone वस्तुको स्वामित्व छोड्छ:

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

वास्तवमा, अहिले ProgClone एक शून्य सूचक हो। तसर्थ, एक शून्य सूचक dereference तल मात्र हुनुपर्दछ:

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-स्टुडियो चेतावनी: V522 [CWE-476] नल पोइन्टर 'परीक्षण' को सन्दर्भ हुन सक्छ। Miscompilation.cpp 709

फेरी उस्तै अवस्था । सुरुमा, वस्तुको सामग्रीहरू सारियो, र त्यसपछि यो प्रयोग गरिन्छ मानौं केहि भएको थिएन। C++ मा आन्दोलन सिमान्टिक्स देखा परे पछि म यो अवस्था प्रोग्राम कोडमा अधिक र अधिक देख्छु। यसैले मलाई C++ भाषा मन पर्छ! त्यहाँ आफ्नो खुट्टा बन्द गर्न थप र थप नयाँ तरिकाहरू छन्। PVS-स्टुडियो विश्लेषकसँग सधैं काम हुनेछ :)।

टुक्रा 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: V622 [CWE-478] 'स्विच' कथन निरीक्षण गर्न विचार गर्नुहोस्। यो सम्भव छ कि पहिलो 'केस' अपरेटर हराइरहेको छ। SystemZAsmParser.cpp 652

सुरुमा एक धेरै शंकास्पद अपरेटर छ ब्रेक। यहाँ अरु केहि लेख्न बिर्सनुभयो कि ?

Fragment N12: dereferencing पछि एक सूचक जाँच गर्दै

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-स्टुडियो चेतावनी: V595 [CWE-476] nullptr विरुद्ध प्रमाणित हुनु अघि 'Callee' सूचक प्रयोग गरिएको थियो। लाइनहरू जाँच गर्नुहोस्: 172, 174। AMDGPUInline.cpp 172

सूचक कल्ले सुरुमा फंक्शन बोलाइएको समयमा dereferenced हुन्छ TTI प्राप्त गर्नुहोस्.

र त्यसपछि यो बाहिर जान्छ कि यो सूचक समानता लागि जाँच गर्नुपर्छ nullptr:

if (!Callee || Callee->isDeclaration())

तर धेरै ढिलो…

Fragment N13 - N...: dereferencing पछि एक सूचक जाँच गर्दै

अघिल्लो कोड खण्डमा छलफल गरिएको अवस्था अद्वितीय छैन। यो यहाँ देखिन्छ:

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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: V629 [CWE-190] '~(आकार - 1) << 1' अभिव्यक्तिको निरीक्षण गर्न विचार गर्नुहोस्। ६४-बिट प्रकारमा पछिल्लो विस्तारको साथ ३२-बिट मानको बिट सिफ्टिङ। AArch32AddressingModes.h 64

यो बग नहुन सक्छ र कोडले ठ्याक्कै उद्देश्य अनुसार काम गर्दछ। तर यो स्पष्ट रूपमा एक धेरै संदिग्ध स्थान हो र जाँच गर्न आवश्यक छ।

चर भनौं आकार 16 को बराबर छ, र त्यसपछि कोड को लेखक एक चर मा प्राप्त गर्न योजना बनायो NImms मूल्य:

1111111111111111111111111111111111111111111111111111111111100000

यद्यपि, वास्तविकतामा परिणाम हुनेछ:

0000000000000000000000000000000011111111111111111111111111100000

तथ्य यो हो कि सबै गणनाहरू 32-बिट अहस्ताक्षरित प्रकार प्रयोग गरेर हुन्छन्। र त्यसपछि मात्र, यो 32-बिट अहस्ताक्षरित प्रकार स्पष्ट रूपमा विस्तार गरिनेछ uint64_t। यस अवस्थामा, सबैभन्दा महत्त्वपूर्ण बिट्स शून्य हुनेछ।

तपाईंले यस प्रकारको अवस्थालाई ठीक गर्न सक्नुहुन्छ:

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

समान स्थिति: V629 [CWE-190] 'Immr << 6' अभिव्यक्तिको निरीक्षण गर्ने बारे विचार गर्नुहोस्। ६४-बिट प्रकारमा पछिल्लो विस्तारको साथ ३२-बिट मानको बिट सिफ्टिङ। AArch32AddressingModes.h 64

Fragment 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-स्टुडियो चेतावनी: V646 [CWE-670] एप्लिकेसनको तर्कको निरीक्षण गर्ने बारे विचार गर्नुहोस्। यो सम्भव छ कि 'अरू' किवर्ड हराइरहेको छ। AMDGPUAsmParser.cpp 5655

यहाँ कुनै गल्ती छैन। त्यसबेलादेखि पहिलोको ब्लक हो if संग समाप्त हुन्छ जारी, त्यसो भए फरक पर्दैन, त्यहाँ एउटा कुञ्जी शब्द छ अरू कि हैन। कुनै पनि तरिका कोडले समान काम गर्नेछ। अझै छुटेको छ अरू कोड थप अस्पष्ट र खतरनाक बनाउँछ। यदि भविष्यमा जारी गायब हुन्छ, कोड पूर्ण रूपमा फरक काम गर्न सुरु हुनेछ। मेरो विचारमा यो थप्न राम्रो छ अरू.

Fragment 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-स्टुडियो चेतावनी:

  • V655 [CWE-480] तारहरू जोडिएका थिए तर प्रयोग गरिएन। 'Result + Name.str()' अभिव्यक्तिको निरीक्षण गर्ने बारे विचार गर्नुहोस्। Symbol.cpp 32
  • V655 [CWE-480] तारहरू जोडिएका थिए तर प्रयोग गरिएन। 'परिणाम + "(ObjC वर्ग)" + Name.str()' अभिव्यक्तिको निरीक्षण गर्ने विचार गर्नुहोस्। Symbol.cpp 35
  • V655 [CWE-480] तारहरू जोडिएका थिए तर प्रयोग गरिएन। 'परिणाम + "(ObjC कक्षा 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-स्टुडियो विश्लेषक प्रयोग गरेर LLVM 8 मा बगहरू खोज्दै

PVS-स्टुडियो चेतावनी: V708 [CWE-758] खतरनाक निर्माण प्रयोग गरिन्छ: 'FeaturesMap[Op] = FeaturesMap.size()', जहाँ 'FeaturesMap' 'नक्सा' वर्गको हो। यसले अपरिभाषित व्यवहार निम्त्याउन सक्छ। 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: V519 [CWE-563] 'पङ्क्तिबद्ध' चरलाई क्रमशः दुई पटक मान तोकिएको छ। सायद यो गल्ती हो। लाइनहरू जाँच गर्नुहोस्: 1158, 1160। LoadStoreVectorizer.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-स्टुडियो चेतावनी: V547 [CWE-571] अभिव्यक्ति 'nextByte != 0x90' सधैँ सत्य हुन्छ। X86DisassemblerDecoder.cpp 379

जाँच गर्नुको कुनै अर्थ छैन। चर NextByte सधैं मूल्य बराबर हुँदैन 0x90, जुन अघिल्लो चेकबाट पछ्याउँछ। यो एक प्रकारको तार्किक त्रुटि हो।

Fragment 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-स्टुडियो चेतावनी: V560 [CWE-570] सशर्त अभिव्यक्तिको एक भाग सधैं गलत हुन्छ: RegNo == 0xe। ARMDisassembler.cpp 939

स्थिर 0xE दशमलव मा 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: V612 [CWE-670] एक लूप भित्र एक बिना शर्त 'रिटर्न'। R600OptimizeVectorRegisters.cpp 63

यो या त त्रुटि हो वा कोड पढिरहेका प्रोग्रामरहरूलाई केही व्याख्या गर्ने उद्देश्यले विशेष प्रविधि हो। यो डिजाइनले मलाई केहि पनि व्याख्या गर्दैन र धेरै शंकास्पद देखिन्छ। यसरी नलेख्नु राम्रो :)

थकित? त्यसपछि यो चिया वा कफी बनाउने समय हो।

PVS-स्टुडियो विश्लेषक प्रयोग गरेर LLVM 8 मा बगहरू खोज्दै

नयाँ निदानद्वारा पहिचान गरिएका त्रुटिहरू

मलाई लाग्छ पुरानो निदान को 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-स्टुडियो चेतावनी: V779 [CWE-561] पहुँचयोग्य कोड पत्ता लाग्यो। यो सम्भव छ कि एक त्रुटि उपस्थित छ। ExecutionUtils.cpp 146

तपाईले देख्न सक्नुहुन्छ, अपरेटरको दुबै शाखाहरू if अपरेटरलाई कल गरेर समाप्त हुन्छ फिर्ती। तदनुसार, कन्टेनर CtorDtors द्वारा प्राथमिकता कहिल्यै सफा हुनेछैन।

टुक्रा 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: V784 बिट मास्कको साइज पहिलो अपरेन्डको साइजभन्दा कम छ। यसले उच्च बिटहरू गुमाउनेछ। RuntimeDyld.cpp 815

कृपया ध्यान दिनुहोस् कि कार्य GetStubAlignment रिटर्न प्रकार हस्ताक्षर नगरिएको। अभिव्यक्तिको मान गणना गरौं, मानौं कि प्रकार्यले मान 8 फर्काउँछ:

~(getStubAlignment() - 1)

~(8u-1)

0xFFFFFFFF8u

अब ध्यान दिनुहोस् कि चर डाटासाइज 64-बिट अहस्ताक्षरित प्रकार छ। डेटासाइज र 0xFFFFFFF8u अपरेशन प्रदर्शन गर्दा, सबै बत्तीस उच्च-अर्डर बिटहरू शून्यमा रिसेट हुनेछन्। सम्भवतः, यो प्रोग्रामरले चाहेको होइन। मलाई शंका छ कि उनी गणना गर्न चाहन्थे: DataSize र 0xFFFFFFFFFFFFFF8u।

त्रुटि ठीक गर्न, तपाईंले यो लेख्नुपर्छ:

DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);

वा यस्तै:

DataSize &= ~(getStubAlignment() - 1ULL);

Fragment 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-स्टुडियो चेतावनी: V1028 [CWE-190] सम्भावित ओभरफ्लो। 'size_t' प्रकारमा 'NumElts * Scale' अपरेटरको कास्टिङ अपरेन्डहरू विचार गर्नुहोस्, नतिजा होइन। X86ISelLowering.h 1577

प्रकार चरहरू गुणा गर्दा ओभरफ्लोबाट बच्न स्पष्ट प्रकार कास्टिङ प्रयोग गरिन्छ int। यद्यपि, यहाँ स्पष्ट प्रकार कास्टिङले ओभरफ्लो विरुद्ध सुरक्षा गर्दैन। पहिले, चरहरू गुणन गरिनेछ, र त्यसपछि मात्र गुणनको 32-बिट परिणाम प्रकारमा विस्तार गरिनेछ। आकार_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-स्टुडियो चेतावनी: 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-स्टुडियो: V1001 [CWE-563] 'साइज' चल तोकिएको छ तर प्रकार्यको अन्त्यमा प्रयोग गरिएको छैन। Object.cpp 424

अवस्था पहिलेको जस्तै छ। यो लेखिएको हुनुपर्छ:

this->Size += this->EntrySize;

टुक्रा N38-N47: तिनीहरूले सूचकांक जाँच गर्न बिर्सनुभयो

पहिले, हामीले डायग्नोस्टिक ट्रिगरिङका उदाहरणहरू हेर्यौं V595। यसको सार यो हो कि सूचक सुरुमा dereferenced छ, र त्यसपछि मात्र जाँच। युवा निदान 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: 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-स्टुडियो चेतावनी: V1004 [CWE-476] 'PtrTy' पोइन्टर nullptr विरुद्ध प्रमाणित भएपछि असुरक्षित रूपमा प्रयोग गरियो। लाइनहरू जाँच गर्नुहोस्: 960, 965। InterleavedLoadCombinePass.cpp 965

यस्ता गल्तीहरूबाट आफूलाई कसरी बचाउने? कोड-समीक्षामा बढी ध्यान दिनुहोस् र नियमित रूपमा आफ्नो कोड जाँच गर्न PVS-स्टुडियो स्थिर विश्लेषक प्रयोग गर्नुहोस्।

यस प्रकारका त्रुटिहरू भएका अन्य कोड टुक्राहरू उद्धृत गर्नुको कुनै मतलब छैन। म लेखमा चेतावनीहरूको सूची मात्र छोड्नेछु:

  • 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-स्टुडियो चेतावनी: V1023 [CWE-460] मालिक बिनाको सूचकलाई 'emplace_back' विधिद्वारा 'रणनीतिहरू' कन्टेनरमा थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-isel-fuzzer.cpp 58

जस्तै कन्टेनरको अन्त्यमा एउटा तत्व थप्न std::vector > तपाईले मात्र लेख्न सक्नुहुन्न xxx.push_back(नयाँ X), बाट कुनै निहित रूपान्तरण छैन X* в std::unique_ptr.

एक साझा समाधान लेख्न छ xxx.emplace_back(नयाँ X)किनकि यसले कम्पाइल गर्छ: विधि emplace_back यसको आर्गुमेन्टबाट सीधै तत्व निर्माण गर्दछ र त्यसैले स्पष्ट कन्स्ट्रक्टरहरू प्रयोग गर्न सक्छ।

यो सुरक्षित छैन। यदि भेक्टर भरिएको छ भने, मेमोरी पुन: आवंटित हुन्छ। मेमोरी पुन: स्थानान्तरण अपरेशन असफल हुन सक्छ, परिणाममा अपवाद फ्याँकिएको छ std::bad_alloc। यस अवस्थामा, सूचक हराउनेछ र सिर्जना गरिएको वस्तु कहिल्यै मेटिने छैन।

एक सुरक्षित समाधान सिर्जना गर्न हो अद्वितीय_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-स्टुडियो विश्लेषकले यसलाई पहिचान गर्न सिकेको छ।

यस प्रकारका अन्य चेतावनीहरू:

  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'Passes' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। PassManager.h 546
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'AAs' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। AliasAnalysis.h 324
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'Entry' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'AllEdges' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। CFGMST.h 268
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'VMaps' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'रेकर्ड' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। FDRLogBuilder.h 30
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'PendingSubmodules' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। ModuleMap.cpp 810
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'वस्तुहरू' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। DebugMap.cpp 88
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'रणनीतिहरू' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-isel-fuzzer.cpp ६०
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 685
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 686
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 688
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 689
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 690
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 691
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 692
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 693
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'परिमार्जक' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। llvm-stress.cpp 694
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'Operands' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'Stash' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] 'emplace_back' विधिद्वारा 'Matchers' कन्टेनरमा मालिक बिनाको सूचक थपिएको छ। एक अपवाद को मामला मा मेमोरी लीक हुनेछ। GlobalISelEmitter.cpp 2702

निष्कर्षमा

मैले जम्मा ६० वटा चेतावनी जारी गरेँ र रोकें। त्यहाँ PVS-स्टुडियो विश्लेषकले LLVM मा पत्ता लगाउने अन्य दोषहरू छन्? हो, मसँग छ। जे होस्, जब मैले लेखको लागि कोड टुक्राहरू लेखिरहेको थिएँ, यो अबेर साँझ थियो, वा राती पनि, र मैले निर्णय गरें कि यो एक दिन कल गर्ने समय हो।

मलाई आशा छ कि तपाईंले यो रोचक पाउनुभयो र PVS-स्टुडियो विश्लेषक प्रयास गर्न चाहानुहुन्छ।

तपाईं विश्लेषक डाउनलोड गर्न सक्नुहुन्छ र माइनस्वीपर कुञ्जी प्राप्त गर्न सक्नुहुन्छ यो पृष्ठ.

सबैभन्दा महत्त्वपूर्ण कुरा, स्थिर विश्लेषण नियमित रूपमा प्रयोग गर्नुहोस्। एक पटक जाँच, स्थिर विश्लेषण र PVS-स्टुडियो को विधि को लोकप्रिय बनाउन को लागी हामी द्वारा गरिएको सामान्य परिदृश्य होइन।

तपाईंको कोडको गुणस्तर र विश्वसनीयता सुधार गर्नमा शुभकामना!

PVS-स्टुडियो विश्लेषक प्रयोग गरेर LLVM 8 मा बगहरू खोज्दै

यदि तपाइँ यो लेख अंग्रेजी बोल्ने दर्शकहरूसँग साझा गर्न चाहनुहुन्छ भने, कृपया अनुवाद लिङ्क प्रयोग गर्नुहोस्: Andrey Karpov। PVS-Studio सँग LLVM 8 मा बगहरू खोज्दै.

स्रोत: www.habr.com

एक टिप्पणी थप्न