PVS-स्टुडिओ विश्लेषक वापरून LLVM 8 मध्ये बग शोधणे

PVS-स्टुडिओ विश्लेषक वापरून LLVM 8 मध्ये बग शोधणे
आमच्या PVS-स्टुडिओ विश्लेषक वापरून LLVM प्रकल्पाची शेवटची कोड तपासणी करून दोन वर्षांहून अधिक काळ लोटला आहे. PVS-स्टुडिओ विश्लेषक अजूनही त्रुटी आणि संभाव्य भेद्यता ओळखण्यासाठी एक प्रमुख साधन आहे याची खात्री करूया. हे करण्यासाठी, आम्ही LLVM 8.0.0 प्रकाशन मध्ये नवीन त्रुटी तपासू आणि शोधू.

लेख लिहायचा आहे

खरे सांगायचे तर मला हा लेख लिहायचा नव्हता. आम्ही आधीच अनेक वेळा तपासलेल्या प्रकल्पाबद्दल लिहिणे मनोरंजक नाही (1, 2, 3). काहीतरी नवीन लिहिणे चांगले आहे, परंतु मला पर्याय नाही.

प्रत्येक वेळी LLVM ची नवीन आवृत्ती प्रकाशित किंवा अद्यतनित केली जाते क्लॅंग स्टॅटिक विश्लेषक, आम्हाला आमच्या मेलमध्ये खालील प्रकारचे प्रश्न प्राप्त होतात:

पहा, क्लॅंग स्टॅटिक अॅनालायझरची नवीन आवृत्ती नवीन त्रुटी शोधण्यास शिकली आहे! मला असे वाटते की PVS-Studio वापरण्याची प्रासंगिकता कमी होत आहे. Clang पूर्वीपेक्षा अधिक त्रुटी शोधतो आणि PVS-Studio च्या क्षमतांसह पकडतो. तुम्ही याबद्दल काय विचार करता?

यावर मी नेहमी असे काहीतरी उत्तर देऊ इच्छितो:

आम्हीही निष्क्रिय बसत नाही! आम्ही PVS-Studio विश्लेषकाच्या क्षमतांमध्ये लक्षणीय सुधारणा केली आहे. त्यामुळे काळजी करू नका, आम्ही पूर्वीप्रमाणेच नेतृत्व करत आहोत.

दुर्दैवाने, हे एक वाईट उत्तर आहे. त्यात कोणतेही पुरावे नाहीत. आणि म्हणूनच मी आता हा लेख लिहित आहे. त्यामुळे एलएलव्हीएम प्रकल्पाची पुन्हा एकदा तपासणी करण्यात आली असून त्यात अनेक प्रकारच्या त्रुटी आढळून आल्या आहेत. जे मला मनोरंजक वाटले ते मी आता दाखवून देईन. क्लॅंग स्टॅटिक अॅनालायझर या त्रुटी शोधू शकत नाही (किंवा त्याच्या मदतीने असे करणे अत्यंत गैरसोयीचे आहे). पण आपण करू शकतो. शिवाय, मी एका संध्याकाळी या सर्व त्रुटी शोधून काढल्या आणि लिहून ठेवल्या.

पण लेख लिहायला काही आठवडे लागले. हे सर्व मजकूरात ठेवण्यासाठी मी स्वतःला आणू शकलो नाही :).

तसे, त्रुटी आणि संभाव्य भेद्यता ओळखण्यासाठी पीव्हीएस-स्टुडिओ विश्लेषकमध्ये कोणते तंत्रज्ञान वापरले जाते याबद्दल आपल्याला स्वारस्य असल्यास, मी यासह परिचित होण्याचे सुचवितो. टीप.

नवीन आणि जुने निदान

आधीच नमूद केल्याप्रमाणे, सुमारे दोन वर्षांपूर्वी एलएलव्हीएम प्रकल्पाची पुन्हा एकदा तपासणी करण्यात आली आणि आढळलेल्या त्रुटी सुधारण्यात आल्या. आता हा लेख त्रुटींचा एक नवीन बॅच सादर करेल. नवीन बग का सापडले? याची 3 कारणे आहेत:

  1. LLVM प्रकल्प विकसित होत आहे, जुना कोड बदलत आहे आणि नवीन कोड जोडत आहे. स्वाभाविकच, सुधारित आणि लिखित कोडमध्ये नवीन त्रुटी आहेत. हे स्पष्टपणे दर्शवते की स्थिर विश्लेषण नियमितपणे वापरले पाहिजे, आणि अधूनमधून नाही. आमचे लेख पीव्हीएस-स्टुडिओ विश्लेषकाच्या क्षमता चांगल्या प्रकारे दर्शवतात, परंतु याचा कोड गुणवत्ता सुधारण्याशी आणि त्रुटी दूर करण्याचा खर्च कमी करण्याशी काहीही संबंध नाही. स्थिर कोड विश्लेषक नियमितपणे वापरा!
  2. आम्ही विद्यमान निदानांना अंतिम रूप देत आहोत आणि त्यात सुधारणा करत आहोत. म्हणून, विश्लेषक मागील स्कॅन दरम्यान लक्षात न आलेल्या त्रुटी ओळखू शकतो.
  3. PVS-Studio मध्ये नवीन डायग्नोस्टिक्स दिसू लागले आहेत जे 2 वर्षांपूर्वी अस्तित्वात नव्हते. पीव्हीएस-स्टुडिओचा विकास स्पष्टपणे दर्शविण्यासाठी मी त्यांना वेगळ्या विभागात हायलाइट करण्याचा निर्णय घेतला.

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-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-स्टुडिओ चेतावणी: V502 [CWE-783] कदाचित '?:' ऑपरेटर अपेक्षेपेक्षा वेगळ्या पद्धतीने काम करत असेल. '?:' ऑपरेटरला '==' ऑपरेटरपेक्षा कमी प्राधान्य आहे. PPCTargetTransformInfo.cpp 404

माझ्या मते, ही एक अतिशय सुंदर चूक आहे. होय, मला माहित आहे की मला सौंदर्याबद्दल विचित्र कल्पना आहेत :).

आता, त्यानुसार ऑपरेटर प्राधान्ये, अभिव्यक्तीचे खालीलप्रमाणे मूल्यमापन केले जाते:

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

व्यावहारिक दृष्टिकोनातून, अशा स्थितीचा अर्थ नाही, कारण ते कमी केले जाऊ शकते:

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

ही एक स्पष्ट चूक आहे. बहुधा, त्यांना ०/१ ची व्हेरिएबलशी तुलना करायची होती निर्देशांक. कोडचे निराकरण करण्यासाठी तुम्हाला ट्रॅनरी ऑपरेटरभोवती कंस जोडणे आवश्यक आहे:

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->getAsString().

एरर हँडलरमध्ये एरर लपलेली असते तेव्हा ही एक अतिशय सामान्य परिस्थिती असते, कारण कोणीही त्यांची चाचणी करत नाही. स्थिर विश्लेषक सर्व पोहोचण्यायोग्य कोड तपासतात, ते कितीही वेळा वापरले तरीही. स्थिर विश्लेषण इतर चाचणी आणि त्रुटी संरक्षण तंत्रांना कसे पूरक ठरते याचे हे एक उत्तम उदाहरण आहे.

समान पॉइंटर हाताळणी त्रुटी एचआरएच फक्त खालील कोडमध्ये परवानगी आहे: 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 एक शून्य पॉइंटर आहे. म्हणून, एक शून्य पॉइंटर डिरेफरन्स खाली आला पाहिजे:

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++ भाषा आवडते! आपला स्वतःचा पाय काढून टाकण्याचे अधिकाधिक नवीन मार्ग आहेत. पीव्हीएस-स्टुडिओ विश्लेषकाकडे नेहमी काम असेल :).

फ्रॅगमेंट 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

सुरुवातीला एक अतिशय संशयास्पद ऑपरेटर आहे ब्रेक. इथे अजून काही लिहायला विसरलात का?

फ्रॅगमेंट 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-स्टुडिओ चेतावणी: V595 [CWE-476] 'कॅली' पॉइंटर nullptr विरुद्ध सत्यापित करण्यापूर्वी वापरला गेला. ओळी तपासा: 172, 174. AMDGPUInline.cpp 172

पॉइंटर कॉली फंक्शन कॉल केल्यावर सुरुवातीला dereferenced केले जाते टीटीआय मिळवा.

आणि मग असे दिसून आले की हा पॉइंटर समानतेसाठी तपासला पाहिजे 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-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-स्टुडिओ चेतावणी: 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' अभिव्यक्तीचे निरीक्षण करण्याचा विचार करा. 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

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' 'map' वर्गाचा आहे. यामुळे अपरिभाषित वर्तन होऊ शकते. RISCVCompressInstEmitter.cpp 490

समस्या ओळ:

FeaturesMap[Op] = FeaturesMap.size();

जर घटक Op आढळले नाही, तर नकाशामध्ये एक नवीन घटक तयार केला जातो आणि या नकाशातील घटकांची संख्या तेथे लिहिली जाते. फंक्शन कॉल केले जाईल की नाही हे फक्त अज्ञात आहे आकार नवीन घटक जोडण्यापूर्वी किंवा नंतर.

Fragment 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

Fragment 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] 'इफेक्ट्स' व्हेरिएबलला सलग दोनदा मूल्ये नियुक्त केली जातात. कदाचित ही चूक आहे. ओळी तपासा: 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] 'ExpectNoDerefChunk' व्हेरिएबलची मूल्ये सलग दोनदा नियुक्त केली जातात. कदाचित ही चूक आहे. ओळी तपासा: ४९७०, ४९७३. SemaType.cpp ४९७३

Fragment 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 सक्रियता पुरेसे आहे. आता विश्‍लेषकामध्ये दिसलेल्या नवीन डायग्नोस्टिक्ससह कोणत्या मनोरंजक गोष्टी शोधल्या जाऊ शकतात ते पाहूया मागील चेक यावेळी, C++ विश्लेषकामध्ये एकूण 66 सामान्य-उद्देश निदान जोडण्यात आले.

फ्रॅगमेंट 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 ऑपरेटरला कॉल करून समाप्त होते परत. त्यानुसार कंटेनर 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-स्टुडिओ चेतावणी: 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-बिट स्वाक्षरी नसलेला प्रकार आहे. असे दिसून आले की DataSize आणि 0xFFFFFFF8u ऑपरेशन करत असताना, सर्व बत्तीस उच्च-ऑर्डर बिट शून्य वर रीसेट केले जातील. बहुधा, प्रोग्रामरला हेच हवे होते. मला शंका आहे की त्याला गणना करायची आहे: DataSize & 0xFFFFFFFFFFFFFF8u.

त्रुटी दूर करण्यासाठी, आपण हे लिहावे:

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

टाइप व्हेरिएबल्सचा गुणाकार करताना ओव्हरफ्लो टाळण्यासाठी स्पष्ट प्रकार कास्टिंग वापरले जाते int. तथापि, येथे स्पष्ट प्रकार कास्टिंग ओव्हरफ्लोपासून संरक्षण करत नाही. प्रथम, व्हेरिएबल्सचा गुणाकार केला जाईल आणि त्यानंतरच गुणाकाराचा 32-बिट परिणाम प्रकारात वाढविला जाईल. आकार_टी.

फ्रॅगमेंट 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-Studio: 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

चल पं समान असू शकते 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 विरुद्ध सत्यापित केल्यानंतर तो असुरक्षितपणे वापरला गेला. ओळी तपासा: ३२२८, ३२३१. CGDebugInfo.cpp ३२३१

चिन्हाकडे लक्ष द्या 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-Studio स्टॅटिक अॅनालायझर वापरा.

या प्रकारच्या त्रुटींसह इतर कोडचे तुकडे उद्धृत करण्यात काही अर्थ नाही. मी लेखात फक्त चेतावणींची एक सूची सोडेन:

  • V1004 [CWE-476] 'Expr' पॉइंटर nullptr विरुद्ध सत्यापित केल्यानंतर तो असुरक्षितपणे वापरला गेला. ओळी तपासा: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] 'PI' पॉइंटर nullptr विरुद्ध सत्यापित केल्यानंतर तो असुरक्षितपणे वापरला गेला. ओळी तपासा: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] 'स्टेटपॉईंटकॉल' पॉइंटर 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] मालक नसलेला पॉइंटर 'एएएस' कंटेनरमध्ये 'एमप्लेस_बॅक' पद्धतीने जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. AliasAnalysis.h 324
  • V1023 [CWE-460] 'emplace_back' पद्धतीने 'एंट्रीज' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. 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 60
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 685
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 686
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 688
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 689
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 690
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 691
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 692
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. llvm-stress.cpp 693
  • V1023 [CWE-460] 'एमप्लेस_बॅक' पद्धतीने 'मॉडिफायर्स' कंटेनरमध्ये मालक नसलेला पॉइंटर जोडला जातो. अपवादाच्या बाबतीत मेमरी लीक होईल. 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

निष्कर्ष

मी एकूण 60 चेतावणी जारी केल्या आणि नंतर थांबलो. PVS-स्टुडिओ विश्लेषक LLVM मध्ये शोधणारे इतर दोष आहेत का? होय माझ्याकडे आहे. तथापि, जेव्हा मी लेखासाठी कोडचे तुकडे लिहित होतो, तेव्हा संध्याकाळ झाली होती, किंवा अगदी रात्र झाली होती आणि मी ठरवले की त्याला एक दिवस म्हणण्याची वेळ आली आहे.

मला आशा आहे की तुम्हाला ते मनोरंजक वाटले आणि PVS-स्टुडिओ विश्लेषक वापरून पहावेसे वाटेल.

तुम्ही विश्लेषक डाउनलोड करू शकता आणि येथे माइनस्वीपर की मिळवू शकता हे पृष्ठ.

सर्वात महत्त्वाचे म्हणजे, नियमितपणे स्थिर विश्लेषण वापरा. एकवेळ चेक, आमच्याद्वारे स्थिर विश्लेषणाची कार्यपद्धती लोकप्रिय करण्यासाठी केली जाते आणि PVS-Studio ही सामान्य परिस्थिती नाही.

तुमच्या कोडची गुणवत्ता आणि विश्वासार्हता सुधारण्यात शुभेच्छा!

PVS-स्टुडिओ विश्लेषक वापरून LLVM 8 मध्ये बग शोधणे

आपण हा लेख इंग्रजी भाषिक प्रेक्षकांसह सामायिक करू इच्छित असल्यास, कृपया भाषांतर दुवा वापरा: आंद्रे कार्पोव्ह. PVS-Studio सह LLVM 8 मध्ये बग शोधत आहे.

स्त्रोत: www.habr.com

एक टिप्पणी जोडा