PVS-স্টুডিও বিশ্লেষক ব্যবহার করে LLVM 8-এ বাগ খোঁজা

PVS-স্টুডিও বিশ্লেষক ব্যবহার করে LLVM 8-এ বাগ খোঁজা
আমাদের পিভিএস-স্টুডিও বিশ্লেষক ব্যবহার করে এলএলভিএম প্রকল্পের শেষ কোড চেক করার পর দুই বছরেরও বেশি সময় কেটে গেছে। আসুন নিশ্চিত করি যে পিভিএস-স্টুডিও বিশ্লেষক এখনও ত্রুটি এবং সম্ভাব্য দুর্বলতা সনাক্ত করার জন্য একটি অগ্রণী হাতিয়ার। এটি করার জন্য, আমরা LLVM 8.0.0 রিলিজে নতুন ত্রুটিগুলি পরীক্ষা করব এবং খুঁজে বের করব।

প্রবন্ধ লিখতে হবে

সত্যি কথা বলতে, আমি এই নিবন্ধটি লিখতে চাইনি। এমন একটি প্রকল্প সম্পর্কে লিখতে আকর্ষণীয় নয় যা আমরা ইতিমধ্যে বেশ কয়েকবার পরীক্ষা করেছি (1, 2, 3) নতুন কিছু নিয়ে লিখলে ভালো হয়, কিন্তু আমার কোনো বিকল্প নেই।

প্রতিবার এলএলভিএম-এর একটি নতুন সংস্করণ প্রকাশিত বা আপডেট করা হয় ঝনঝন স্ট্যাটিক বিশ্লেষক, আমরা আমাদের মেইলে নিম্নলিখিত ধরণের প্রশ্ন পাই:

দেখুন, ক্ল্যাং স্ট্যাটিক অ্যানালাইজারের নতুন সংস্করণ নতুন ত্রুটি খুঁজে বের করতে শিখেছে! আমার কাছে মনে হচ্ছে পিভিএস-স্টুডিও ব্যবহারের প্রাসঙ্গিকতা কমে যাচ্ছে। ক্ল্যাং আগের চেয়ে আরও বেশি ত্রুটি খুঁজে পায় এবং PVS-স্টুডিওর সক্ষমতাগুলি ধরে ফেলে৷ আপনি এ ব্যপারে কী ভাবছেন?

এটির জন্য আমি সর্বদা এমন কিছু উত্তর দিতে চাই:

আমরাও অলস বসে থাকি না! আমরা পিভিএস-স্টুডিও বিশ্লেষকের ক্ষমতা উল্লেখযোগ্যভাবে উন্নত করেছি। তাই চিন্তা করবেন না, আমরা আগের মতই নেতৃত্ব দিয়ে যাচ্ছি।

দুর্ভাগ্যবশত, এটি একটি খারাপ উত্তর। এতে কোনো প্রমাণ নেই। আর সেই কারণেই আমি এখন এই নিবন্ধটি লিখছি। সুতরাং, এলএলভিএম প্রকল্পটি আবারও পরীক্ষা করা হয়েছে এবং এতে বিভিন্ন ত্রুটি পাওয়া গেছে। আমি এখন সেগুলি প্রদর্শন করব যা আমার কাছে আকর্ষণীয় বলে মনে হয়েছিল। ক্ল্যাং স্ট্যাটিক অ্যানালাইজার এই ত্রুটিগুলি খুঁজে পায় না (বা এটির সাহায্যে এটি করা অত্যন্ত অসুবিধাজনক)। কিন্তু আমরা পারি. তাছাড়া, আমি এক সন্ধ্যায় এই সমস্ত ত্রুটি খুঁজে পেয়েছি এবং লিখেছিলাম।

কিন্তু নিবন্ধটি লিখতে কয়েক সপ্তাহ লেগেছে। আমি শুধু এই সব টেক্সটে রাখার জন্য নিজেকে আনতে পারিনি :)।

যাইহোক, আপনি যদি পিভিএস-স্টুডিও বিশ্লেষক ত্রুটিগুলি এবং সম্ভাব্য দুর্বলতাগুলি সনাক্ত করতে কোন প্রযুক্তিগুলি ব্যবহার করা হয় সে সম্পর্কে আগ্রহী হন, তবে আমি এটির সাথে পরিচিত হওয়ার পরামর্শ দিই বিঃদ্রঃ.

নতুন এবং পুরানো ডায়াগনস্টিকস

ইতিমধ্যে উল্লিখিত হিসাবে, প্রায় দুই বছর আগে এলএলভিএম প্রকল্পটি আবার পরীক্ষা করা হয়েছিল, এবং পাওয়া ত্রুটিগুলি সংশোধন করা হয়েছিল। এখন এই নিবন্ধটি ত্রুটির একটি নতুন ব্যাচ উপস্থাপন করবে। কেন নতুন বাগ পাওয়া গেছে? এর জন্য 3টি কারণ রয়েছে:

  1. LLVM প্রকল্পটি বিকশিত হচ্ছে, পুরানো কোড পরিবর্তন করছে এবং নতুন কোড যোগ করছে। স্বাভাবিকভাবেই, পরিবর্তিত এবং লিখিত কোডে নতুন ত্রুটি রয়েছে। এটি স্পষ্টভাবে প্রমাণ করে যে স্ট্যাটিক বিশ্লেষণ নিয়মিত ব্যবহার করা উচিত, এবং মাঝে মাঝে নয়। আমাদের নিবন্ধগুলি পিভিএস-স্টুডিও বিশ্লেষকের ক্ষমতাগুলি ভালভাবে দেখায়, তবে কোডের গুণমান উন্নত করা এবং ত্রুটিগুলি ঠিক করার খরচ কমানোর সাথে এর কোনও সম্পর্ক নেই৷ নিয়মিত একটি স্ট্যাটিক কোড বিশ্লেষক ব্যবহার করুন!
  2. আমরা বিদ্যমান ডায়াগনস্টিকসকে চূড়ান্ত করছি এবং উন্নত করছি। অতএব, বিশ্লেষক ত্রুটিগুলি সনাক্ত করতে পারে যা এটি পূর্ববর্তী স্ক্যানের সময় লক্ষ্য করেনি।
  3. পিভিএস-স্টুডিওতে নতুন ডায়াগনস্টিকস উপস্থিত হয়েছে যা 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-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())

এটি একটি স্পষ্ট ভুল। খুব সম্ভবত, তারা 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 শূন্য, একটি সতর্কতা জারি করা উচিত। যাইহোক, পরিবর্তে, এই একই নাল পয়েন্টার ডিরেফারেন্স করা হবে: 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

শুরুতে একটি স্মার্ট পয়েন্টার প্রোগক্লোন বস্তুর মালিকানা বন্ধ করে দেয়:

BD.setNewProgram(std::move(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

পয়েন্টার যাকে কল করছি ফাংশন কল করার সময় শুরুতে ডিরেফারেন্স করা হয় টিটিআই পান.

এবং তারপর দেখা যাচ্ছে যে এই পয়েন্টারটি সমতার জন্য পরীক্ষা করা উচিত nullptr:

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

কিন্তু অনেক দেরি হয়ে গেছে...

ফ্র্যাগমেন্ট N13 - N...: ডিরেফারেন্স করার পরে একটি পয়েন্টার পরীক্ষা করা হচ্ছে

পূর্ববর্তী কোড খণ্ডে আলোচনা করা পরিস্থিতি অনন্য নয়। এটি এখানে প্রদর্শিত হয়:

static Value *optimizeDoubleFP(CallInst *CI, IRBuilder<> &B,
                               bool isBinary, bool isPrecise = false) {
  ....
  Function *CalleeFn = CI->getCalledFunction();
  StringRef CalleeNm = CalleeFn->getName();                 // <=
  AttributeList CalleeAt = CalleeFn->getAttributes();
  if (CalleeFn && !CalleeFn->isIntrinsic()) {               // <=
  ....
}

PVS-Studio সতর্কতা: V595 [CWE-476] 'CalleeFn' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার আগে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 1079, 1081. SimplifyLibCalls.cpp 1079

এবং এখানে:

void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
                            const Decl *Tmpl, Decl *New,
                            LateInstantiatedAttrVec *LateAttrs,
                            LocalInstantiationScope *OuterMostScope) {
  ....
  NamedDecl *ND = dyn_cast<NamedDecl>(New);
  CXXRecordDecl *ThisContext =
    dyn_cast_or_null<CXXRecordDecl>(ND->getDeclContext());         // <=
  CXXThisScopeRAII ThisScope(*this, ThisContext, Qualifiers(),
                             ND && ND->isCXXInstanceMember());     // <=
  ....
}

PVS-Studio সতর্কতা: V595 [CWE-476] 'ND' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার আগে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 532, 534. SemaTemplateInstantiateDecl.cpp 532

এবং এখানে:

  • V595 [CWE-476] 'U' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার আগে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 404, 407. DWARFormValue.cpp 404
  • V595 [CWE-476] 'ND' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার আগে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 2149, 2151. SemaTemplateInstantiate.cpp 2149

এবং তারপর আমি V595 নম্বর সহ সতর্কতা অধ্যয়ন করতে আগ্রহী হয়ে উঠি না। তাই আমি জানি না এখানে তালিকাভুক্ত ছাড়াও আরও অনুরূপ ত্রুটি আছে কিনা। সম্ভবত আছে.

ফ্র্যাগমেন্ট N17, N18: সন্দেহজনক স্থানান্তর

static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
                                           uint64_t &Encoding) {
  ....
  unsigned Size = RegSize;
  ....
  uint64_t NImms = ~(Size-1) << 1;
  ....
}

PVS-স্টুডিও সতর্কতা: V629 [CWE-190] '~(আকার - 1) << 1' অভিব্যক্তি পরিদর্শন বিবেচনা করুন। 32-বিট টাইপের পরবর্তী সম্প্রসারণের সাথে 64-বিট মানের বিট স্থানান্তর। AArch64AddressingModes.h 260

এটি একটি বাগ নাও হতে পারে এবং কোডটি ঠিক যেমনটি কাজ করে। তবে এটি স্পষ্টতই একটি খুব সন্দেহজনক জায়গা এবং এটি পরীক্ষা করা দরকার।

চলক বলা যাক আয়তন 16 এর সমান, এবং তারপর কোডের লেখক এটি একটি পরিবর্তনশীল করার পরিকল্পনা করেছিলেন NImms মান:

1111111111111111111111111111111111111111111111111111111111100000

যাইহোক, বাস্তবে ফলাফল হবে:

0000000000000000000000000000000011111111111111111111111111100000

আসল বিষয়টি হল যে সমস্ত গণনা 32-বিট স্বাক্ষরবিহীন টাইপ ব্যবহার করে ঘটে। এবং শুধুমাত্র তারপর, এই 32-বিট স্বাক্ষরবিহীন টাইপটি নিহিতভাবে প্রসারিত হবে uint64_t. এই ক্ষেত্রে, সবচেয়ে উল্লেখযোগ্য বিট শূন্য হবে।

আপনি এই মত পরিস্থিতি ঠিক করতে পারেন:

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

অনুরূপ পরিস্থিতি: V629 [CWE-190] 'Immr << 6' অভিব্যক্তি পরিদর্শন বিবেচনা করুন। 32-বিট টাইপের পরবর্তী সম্প্রসারণের সাথে 64-বিট মানের বিট স্থানান্তর। AArch64AddressingModes.h 269

ফ্র্যাগমেন্ট N19: অনুপস্থিত কীওয়ার্ড আর?

void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
  ....
  if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
    // VOP2b (v_add_u32, v_sub_u32 ...) dpp use "vcc" token.
    // Skip it.
    continue;
  } if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) {    // <=
    Op.addRegWithFPInputModsOperands(Inst, 2);
  } else if (Op.isDPPCtrl()) {
    Op.addImmOperands(Inst, 1);
  } else if (Op.isImm()) {
    // Handle optional arguments
    OptionalIdx[Op.getImmTy()] = I;
  } else {
    llvm_unreachable("Invalid operand type");
  }
  ....
}

PVS-স্টুডিও সতর্কতা: V646 [CWE-670] অ্যাপ্লিকেশনের যুক্তি পরিদর্শন বিবেচনা করুন. এটা সম্ভব যে 'অন্য' কীওয়ার্ড অনুপস্থিত। AMDGPUAsmParser.cpp 5655

এখানে কোনো ভুল নেই। তারপর থেকে প্রথম ব্লক if দিয়ে শেষ হয় অবিরত, তাহলে এটা কোন ব্যাপার না, একটি কীওয়ার্ড আছে আর অথবা না. যেভাবেই হোক কোড একই কাজ করবে। এখনও মিস করছি আর কোডটিকে আরও অস্পষ্ট এবং বিপজ্জনক করে তোলে। যদি ভবিষ্যতে অবিরত অদৃশ্য হয়ে যায়, কোডটি সম্পূর্ণ ভিন্নভাবে কাজ শুরু করবে। আমার মতে যোগ করা ভালো আর.

ফ্র্যাগমেন্ট N20: একই ধরনের চারটি টাইপো

LLVM_DUMP_METHOD void Symbol::dump(raw_ostream &OS) const {
  std::string Result;
  if (isUndefined())
    Result += "(undef) ";
  if (isWeakDefined())
    Result += "(weak-def) ";
  if (isWeakReferenced())
    Result += "(weak-ref) ";
  if (isThreadLocalValue())
    Result += "(tlv) ";
  switch (Kind) {
  case SymbolKind::GlobalSymbol:
    Result + Name.str();                        // <=
    break;
  case SymbolKind::ObjectiveCClass:
    Result + "(ObjC Class) " + Name.str();      // <=
    break;
  case SymbolKind::ObjectiveCClassEHType:
    Result + "(ObjC Class EH) " + Name.str();   // <=
    break;
  case SymbolKind::ObjectiveCInstanceVariable:
    Result + "(ObjC IVar) " + Name.str();       // <=
    break;
  }
  OS << Result;
}

PVS-স্টুডিও সতর্কতা:

  • 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

চেক করা মানে না. পরিবর্তনশীল নেক্সটবাইট সবসময় মানের সমান নয় 0x90, যা পূর্ববর্তী চেক থেকে অনুসরণ করে। এটি একধরনের যৌক্তিক ত্রুটি।

ফ্র্যাগমেন্ট N29 - N...: সর্বদা সত্য/মিথ্যা শর্ত

বিশ্লেষক অনেক সতর্কতা জারি করে যে সমগ্র অবস্থা (V547) বা এর অংশ (V560) সর্বদা সত্য বা মিথ্যা। প্রায়শই এগুলি আসল ত্রুটি নয়, তবে কেবল স্লোপি কোড, ম্যাক্রো সম্প্রসারণের ফলাফল এবং এর মতো। যাইহোক, এই সমস্ত সতর্কবাণীর দিকে তাকানো বোধগম্য, কারণ প্রকৃত যৌক্তিক ত্রুটি সময়ে সময়ে ঘটে। উদাহরণস্বরূপ, কোডের এই বিভাগটি সন্দেহজনক:

static DecodeStatus DecodeGPRPairRegisterClass(MCInst &Inst, unsigned RegNo,
                                   uint64_t Address, const void *Decoder) {
  DecodeStatus S = MCDisassembler::Success;

  if (RegNo > 13)
    return MCDisassembler::Fail;

  if ((RegNo & 1) || RegNo == 0xe)
     S = MCDisassembler::SoftFail;
  ....
}

PVS-স্টুডিও সতর্কতা: V560 [CWE-570] শর্তসাপেক্ষ অভিব্যক্তির একটি অংশ সর্বদা মিথ্যা: RegNo == 0xe। ARMDisassembler.cpp 939

ধ্রুবক 0xE হল দশমিকের মান 14। পরীক্ষা RegNo == 0xe মানে না কারণ যদি RegNo > 13, তাহলে ফাংশনটি তার কার্য সম্পাদন সম্পন্ন করবে।

আইডি V547 এবং V560 এর সাথে আরও অনেক সতর্কতা ছিল, তবে এর সাথে V595, আমি এই সতর্কতা অধ্যয়ন করতে আগ্রহী ছিল না. এটি ইতিমধ্যে স্পষ্ট ছিল যে আমার কাছে একটি নিবন্ধ লেখার জন্য যথেষ্ট উপাদান ছিল :)। অতএব, পিভিএস-স্টুডিও ব্যবহার করে এলএলভিএম-এ এই ধরণের কতগুলি ত্রুটি সনাক্ত করা যায় তা অজানা।

আমি আপনাকে একটি উদাহরণ দেব কেন এই ট্রিগারগুলি অধ্যয়ন করা বিরক্তিকর। বিশ্লেষক নিম্নলিখিত কোডের জন্য একটি সতর্কতা জারি করার ক্ষেত্রে একেবারে সঠিক। তবে এটি একটি ভুল নয়।

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 অপারেটরকে একটি কল দিয়ে শেষ হয় প্রত্যাবর্তন. সেই অনুযায়ী ধারক CtorDtorsBy Priority কখনও পরিষ্কার করা হবে না।

ফ্র্যাগমেন্ট 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 অপারেশন সম্পাদন করার সময়, সমস্ত বত্রিশটি হাই-অর্ডার বিট শূন্যে রিসেট করা হবে। সম্ভবত, এটি প্রোগ্রামার যা চেয়েছিলেন তা নয়। আমি সন্দেহ করি যে তিনি গণনা করতে চেয়েছিলেন: ডেটা সাইজ এবং 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

এই নতুন আকর্ষণীয় ডায়াগনস্টিকটি এমন পরিস্থিতিতে সনাক্ত করে যেখানে কোডের একটি অংশ অনুলিপি করা হয়েছে এবং এতে কিছু নাম পরিবর্তন করা শুরু হয়েছে, কিন্তু এক জায়গায় তারা এটি সংশোধন করেনি।

দয়া করে মনে রাখবেন যে দ্বিতীয় ব্লকে তারা পরিবর্তিত হয়েছে অপ৫ উপর অপ৫. কিন্তু এক জায়গায় তারা ঠিক করেনি। সম্ভবত এটি এই মত লেখা উচিত ছিল:

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. এর সারমর্ম হল যে পয়েন্টারটি শুরুতে ডিরেফারেন্স করা হয়, এবং শুধুমাত্র তারপর চেক করা হয়। তরুণ ডায়াগনস্টিকস 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-Studio স্ট্যাটিক বিশ্লেষক ব্যবহার করুন।

এই ধরণের ত্রুটি সহ অন্যান্য কোডের খণ্ডগুলি উদ্ধৃত করার কোনও অর্থ নেই। আমি নিবন্ধে শুধুমাত্র সতর্কতার একটি তালিকা ছেড়ে দেব:

  • V1004 [CWE-476] 'Expr' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার পরে অনিরাপদভাবে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] 'PI' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার পরে অনিরাপদভাবে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] 'StatepointCall' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার পরে অনিরাপদভাবে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 4371, 4379। Verifier.cpp 4379
  • V1004 [CWE-476] 'RV' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার পরে অনিরাপদভাবে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] 'CalleeFn' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার পরে অনিরাপদভাবে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] 'TC' পয়েন্টারটি nullptr-এর বিরুদ্ধে যাচাই করার পরে অনিরাপদভাবে ব্যবহার করা হয়েছিল। লাইন চেক করুন: 1819, 1824। Driver.cpp 1824

ফ্র্যাগমেন্ট N48-N60: সমালোচনামূলক নয়, কিন্তু একটি ত্রুটি (সম্ভাব্য মেমরি লিক)

std::unique_ptr<IRMutator> createISelMutator() {
  ....
  std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
  Strategies.emplace_back(
      new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
  ....
}

PVS-স্টুডিও সতর্কতা: V1023 [CWE-460] 'এমপ্লেস_ব্যাক' পদ্ধতিতে 'কৌশল' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. llvm-isel-fuzzer.cpp 58

একটি ধারক শেষে একটি উপাদান যোগ করার মত std:: ভেক্টর > আপনি শুধু লিখতে পারবেন না xxx.push_back(নতুন এক্স), যেহেতু থেকে কোন অন্তর্নিহিত রূপান্তর নেই X* в std::unique_ptr.

একটি সাধারণ সমাধান লিখতে হয় xxx.emplace_back(নতুন এক্স)যেহেতু এটি সংকলন করে: পদ্ধতি emplace_back সরাসরি তার আর্গুমেন্ট থেকে একটি উপাদান গঠন করে এবং তাই স্পষ্ট কনস্ট্রাক্টর ব্যবহার করতে পারে।

এটা নিরাপদ নয়. ভেক্টর পূর্ণ হলে, মেমরি পুনরায় বরাদ্দ করা হয়। মেমরি রিলোকেশন অপারেশন ব্যর্থ হতে পারে, যার ফলে একটি ব্যতিক্রম নিক্ষিপ্ত হয় std::bad_alloc. এই ক্ষেত্রে, পয়েন্টারটি হারিয়ে যাবে এবং তৈরি করা বস্তুটি কখনই মুছে যাবে না।

একটি নিরাপদ সমাধান তৈরি করা হয় অনন্য_পিটিআরভেক্টর মেমরি পুনরায় বরাদ্দ করার চেষ্টা করার আগে যা পয়েন্টারের মালিক হবে:

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] মালিক ছাড়া একটি পয়েন্টার 'এএএস' কন্টেইনারে 'এমপ্লেস_ব্যাক' পদ্ধতিতে যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. উপনাম বিশ্লেষণ.h 324
  • V1023 [CWE-460] 'এমপ্লেস_ব্যাক' পদ্ধতিতে 'এন্ট্রি' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] 'emplace_back' পদ্ধতিতে 'AllEdges' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. CFGMST.h 268
  • V1023 [CWE-460] 'emplace_back' পদ্ধতিতে 'VMaps' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] 'এমপ্লেস_ব্যাক' পদ্ধতিতে 'রেকর্ড' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. FDRLogBuilder.h 30
  • V1023 [CWE-460] 'emplace_back' পদ্ধতিতে 'PendingSubmodules' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. ModuleMap.cpp 810
  • V1023 [CWE-460] 'emplace_back' পদ্ধতিতে 'অবজেক্ট' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. DebugMap.cpp 88
  • V1023 [CWE-460] 'এমপ্লেস_ব্যাক' পদ্ধতিতে 'কৌশল' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. llvm-isel-fuzzer.cpp 60
  • 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' পদ্ধতিতে 'অপারেন্ড' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] 'emplace_back' পদ্ধতিতে 'Stash' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] 'এমপ্লেস_ব্যাক' পদ্ধতিতে 'ম্যাচার্স' কন্টেইনারে মালিক ছাড়া একটি পয়েন্টার যোগ করা হয়। একটি ব্যতিক্রম ক্ষেত্রে একটি মেমরি লিক ঘটবে. GlobalISelEmitter.cpp 2702

উপসংহার

আমি মোট 60টি সতর্কতা জারি করেছি এবং তারপর বন্ধ করেছি। পিভিএস-স্টুডিও বিশ্লেষক এলএলভিএম-এ শনাক্ত করে এমন অন্যান্য ত্রুটি আছে কি? হ্যাঁ আমার আছে. যাইহোক, যখন আমি নিবন্ধটির জন্য কোডের টুকরোগুলি লিখছিলাম, তখন গভীর সন্ধ্যা, বা এমনকি রাত ছিল এবং আমি সিদ্ধান্ত নিয়েছিলাম যে এটিকে একটি দিন বলার সময়।

আমি আশা করি আপনি এটি আকর্ষণীয় পেয়েছেন এবং PVS-স্টুডিও বিশ্লেষক চেষ্টা করতে চাইবেন।

আপনি বিশ্লেষক ডাউনলোড করতে পারেন এবং মাইনসুইপার কী পেতে পারেন এই পৃষ্ঠাটি.

সবচেয়ে গুরুত্বপূর্ণ, নিয়মিত স্ট্যাটিক বিশ্লেষণ ব্যবহার করুন। এককালীন চেক, স্ট্যাটিক বিশ্লেষণ পদ্ধতি জনপ্রিয় করার জন্য আমাদের দ্বারা বাহিত এবং PVS-স্টুডিও একটি স্বাভাবিক দৃশ্যকল্প নয়.

আপনার কোডের গুণমান এবং নির্ভরযোগ্যতা উন্নত করার জন্য সৌভাগ্য!

PVS-স্টুডিও বিশ্লেষক ব্যবহার করে LLVM 8-এ বাগ খোঁজা

আপনি যদি এই নিবন্ধটি ইংরেজিভাষী দর্শকদের সাথে ভাগ করতে চান তবে অনুগ্রহ করে অনুবাদ লিঙ্কটি ব্যবহার করুন: আন্দ্রে কার্পভ। PVS-Studio-এর সাথে LLVM 8-এ বাগ খোঁজা৷.

উত্স: www.habr.com

একটি মন্তব্য জুড়ুন