د PVS-Studio تحلیل کونکي په کارولو سره په LLVM 8 کې د بګونو موندل

د PVS-Studio تحلیل کونکي په کارولو سره په LLVM 8 کې د بګونو موندل
زموږ د PVS-Studio تحلیل کونکي په کارولو سره د LLVM پروژې وروستي کوډ چیک څخه دوه کاله ډیر تیر شوي. راځئ ډاډ ترلاسه کړو چې د PVS-Studio شنونکی لاهم د غلطیو او احتمالي زیانونو پیژندلو لپاره مخکښ وسیله ده. د دې کولو لپاره، موږ به د LLVM 8.0.0 خوشې کولو کې نوې تېروتنې وګورو او ومومئ.

مقاله باید لیکل شي

د ریښتیني کیدو لپاره ، ما نه غوښتل دا مقاله ولیکم. دا په زړه پورې نده چې د یوې پروژې په اړه ولیکئ چې موږ دمخه څو ځله چیک کړی دی (1, 2, 3). دا غوره ده چې د نوي څه په اړه ولیکئ، مګر زه هیڅ انتخاب نه لرم.

هرکله چې د LLVM نوې نسخه خوشې یا تازه کیږي کلینګ جامد تحلیل کونکی، موږ زموږ په بریښنالیک کې د لاندې ډول پوښتنې ترلاسه کوو:

وګورئ، د کلینګ جامد تحلیل کونکي نوې نسخه د نوي غلطیو موندلو زده کړل! ماته داسې ښکاري چې د PVS-Studio کارولو تړاو کمیږي. کلینګ د پخوا په پرتله ډیرې خطاګانې لټوي او د PVS-Studio وړتیاو سره مخ کیږي. تاسو په دې اړه څه فکر کوئ؟

دې ته زه تل غواړم یو څه ځواب ورکړم لکه:

موږ هم ناست نه یو! موږ د PVS-Studio تحلیل کونکي وړتیاوې د پام وړ وده کړې. نو اندیښنه مه کوئ، موږ د پخوا په څیر رهبري ته دوام ورکوو.

له بده مرغه، دا یو بد ځواب دی. په دې کې هیڅ ثبوت نشته. او له همدې امله زه اوس دا مقاله لیکم. نو، د LLVM پروژه یو ځل بیا چک شوې او یو لړ غلطۍ په کې موندل شوي. زه به اوس هغه وښیم چې ماته په زړه پوري ښکاري. د کلینګ جامد تحلیل کونکی دا غلطۍ نشي موندلی (یا د دې په مرسته دا کار کول خورا ګران دي). مګر موږ کولی شو. سربیره پردې، ما په یوه ماښام کې دا ټولې غلطۍ وموندلې او لیکلې.

مګر د مقالې لیکل څو اونۍ وخت نیسي. زه نشم کولی ځان ته راوړم چې دا ټول په متن کې واچوم :).

په هرصورت، که تاسو لیوالتیا لرئ چې د PVS-Studio شنونکي کې د غلطیتونو او احتمالي زیانونو پیژندلو لپاره کوم ټیکنالوژي کارول کیږي، نو زه وړاندیز کوم چې د دې سره آشنا شئ. یادونه.

نوي او زاړه تشخیص

لکه څنګه چې مخکې یادونه وشوه، شاوخوا دوه کاله وړاندې د LLVM پروژه یو ځل بیا وڅیړل شوه، او موندل شوې غلطۍ سمې شوې. اوس دا مقاله به د غلطیو نوې ډله وړاندې کړي. ولې نوې کیچونه وموندل شول؟ د دې لپاره 3 لاملونه شتون لري:

  1. د LLVM پروژه وده کوي، زوړ کوډ بدلوي او نوی کوډ اضافه کوي. په طبيعي توګه، په تعديل شوي او ليکلي کوډ کې نوې تېروتنې شتون لري. دا په واضح ډول ښیي چې جامد تحلیل باید په منظمه توګه وکارول شي، نه کله ناکله. زموږ مقالې د PVS-Studio شنونکي وړتیاوې ښیې، مګر دا د کوډ کیفیت ښه کولو او د غلطیو اصلاح کولو لګښت کمولو سره هیڅ تړاو نلري. په منظم ډول د جامد کوډ تحلیل کونکي وکاروئ!
  2. موږ موجوده تشخیصونه نهایی او ښه کوو. له همدې امله، شنونکی کولی شي هغه تېروتنې وپیژني چې د تیرو سکینونو په جریان کې یې ندي لیدلي.
  3. نوي تشخیصات په PVS-Studio کې څرګند شوي چې 2 کاله دمخه شتون نه درلود. ما پریکړه وکړه چې دوی په جلا برخه کې روښانه کړم ترڅو په روښانه توګه د PVS-Studio پراختیا وښیې.

د تشخیص لخوا پیژندل شوي نیمګړتیاوې چې دوه کاله دمخه شتون درلود

ټوټه N1: کاپي پیسټ

static bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) {
  if (Name == "addcarryx.u32" || // Added in 8.0
    ....
    Name == "avx512.mask.cvtps2pd.128" || // Added in 7.0
    Name == "avx512.mask.cvtps2pd.256" || // Added in 7.0
    Name == "avx512.cvtusi2sd" || // Added in 7.0
    Name.startswith("avx512.mask.permvar.") || // Added in 7.0     // <=
    Name.startswith("avx512.mask.permvar.") || // Added in 7.0     // <=
    Name == "sse2.pmulu.dq" || // Added in 7.0
    Name == "sse41.pmuldq" || // Added in 7.0
    Name == "avx2.pmulu.dq" || // Added in 7.0
  ....
}

PVS-Studio خبرداری: V501 [CWE-570] د نوم.startswith("avx512.mask.permvar.")' کیڼ اړخ ته او د '||' ښي خوا ته ورته فرعي څرګندونې شتون لري. چلوونکی AutoUpgrade.cpp 73

دا دوه ځله چک شوی چې نوم د "avx512.mask.permvar" سبسټرینګ سره پیل کیږي. په دوهم چک کې، دوی په ښکاره ډول غوښتل چې بل څه ولیکي، مګر د کاپي شوي متن سم کول یې هیر کړل.

ټوټه N2: ټایپ

enum CXNameRefFlags {
  CXNameRange_WantQualifier = 0x1,
  CXNameRange_WantTemplateArgs = 0x2,
  CXNameRange_WantSinglePiece = 0x4
};

void AnnotateTokensWorker::HandlePostPonedChildCursor(
    CXCursor Cursor, unsigned StartTokenIndex) {
  const auto flags = CXNameRange_WantQualifier | CXNameRange_WantQualifier;
  ....
}

خبرداری PVS-Studio: V501 د CXNameRange_WantQualifier ښي خوا ته او د '|' ښي خوا ته ورته فرعي څرګندونې شتون لري. چلوونکی CIndex.cpp 7245

د ټایپو له امله، ورته نوم ثابت دوه ځله کارول کیږي CXNameRange_WantQualifier.

ټوټه N3: د آپریټر لومړیتوب سره ګډوډي

int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
  ....
  if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
    return 0;
  ....
}

PVS-Studio خبرداری: V502 [CWE-783] شاید '؟:' آپریټر په بل ډول کار کوي تر هغه چې تمه کیده. '؟:' آپریټر د '==' آپریټر په پرتله ټیټ لومړیتوب لري. PPCTargetTransformInfo.cpp 404

زما په اند، دا یوه ډېره ښکلې تېروتنه ده. هو، زه پوهیږم چې زه د ښکلا په اړه عجیب نظرونه لرم :).

اوس، په وینا د چلونکي لومړیتوبونهبیان په لاندې ډول ارزول کیږي:

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

د عملي نظر څخه، دا ډول حالت معنی نلري، ځکه چې دا کولی شي کم شي:

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

دا یوه څرګنده تېروتنه ده. ډیری احتمال، دوی غوښتل چې 0/1 د متغیر سره پرتله کړي ډېرځليزې. د کوډ د سمولو لپاره تاسو اړتیا لرئ د ترنیري آپریټر شاوخوا قوسونه اضافه کړئ:

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

د لارې په توګه، ترنیري آپریټر خورا خطرناک دی او منطقي تېروتنې هڅوي. د دې سره ډیر محتاط اوسئ او د قوسونو سره لالچ مه کوئ. ما دا موضوع په ډیر تفصیل سره وڅیړله دلتهپه څپرکي کې "له دې څخه خبر اوسئ؟: چلونکي او په قوسونو کې یې وتړئ."

ټوټه N4، N5: نول پوائنټر

Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
  ....
  TypedInit *LHS = dyn_cast<TypedInit>(Result);
  ....
  LHS = dyn_cast<TypedInit>(
    UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get())
      ->Fold(CurRec));
  if (!LHS) {
    Error(PasteLoc, Twine("can't cast '") + LHS->getAsString() +
                    "' to string");
    return nullptr;
  }
  ....
}

PVS-Studio خبرداری: V522 [CWE-476] د null pointer 'LHS' په نښه کول ممکن ترسره شي. TGParser.cpp 2152

که اشاره کوونکی LHS ړنګ دی، یو خبرداری باید خپور شي. په هرصورت، پرځای یې، دا ورته ناپاک ټکی به له پامه غورځول شي: 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-Studio خبرداری: V522 [CWE-476] د نول پوائنټر 'ProgClone' ته اشاره کول ممکن ترسره شي. Miscompilation.cpp 601

په پیل کې یو سمارټ اشاره پروګکلون د شیانو ملکیت بندوي:

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

په حقیقت کې، اوس پروګکلون یو ناپاک ټکی دی. له همدې امله، د null pointer 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-Studio خبرداری: V522 [CWE-476] د نول پوائنټر 'ټیسټ' ته اشاره کول ممکن ترسره شي. Miscompilation.cpp 709

بیا هماغه حالت. په لومړي سر کې، د شیانو مینځپانګې لیږدول کیږي، او بیا دا کارول کیږي لکه څنګه چې هیڅ شی نه وي. زه دا وضعیت ډیر ځله د برنامې کوډ کې ګورم وروسته له دې چې د حرکت سیمانټیک په C++ کې څرګند شو. له همدې امله زه د C++ ژبه خوښوم! د خپلې پښې د ډزو کولو لپاره ډیرې او ډیرې نوې لارې شتون لري. د PVS- سټوډیو شنونکی به تل کار ولري :).

ټوټه N8: Null pointer

void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
  uint32_t TypeId = Symbol.getTypeId();
  auto Type = Symbol.getSession().getSymbolById(TypeId);
  if (Type)
    Printer << "<unknown-type>";
  else
    Type->dump(*this);
}

د PVS-Studio خبرداری: V522 [CWE-476] د نول پوائنټر 'ډول' ته اشاره کول ممکن واقع شي. PrettyFunctionDumper.cpp 233

د خطا اداره کونکو سربیره ، د ډیبګ کولو چاپ آوټ افعال معمولا نه ازمول کیږي. زموږ په وړاندې یوازې دا ډول قضیه ده. فنکشن د کارونکي په تمه دی، څوک چې د هغه د ستونزو د حل کولو پر ځای، د دې حل کولو ته اړ کیږي.

سمه ده:

if (Type)
  Type->dump(*this);
else
  Printer << "<unknown-type>";

ټوټه N9: Null pointer

void SearchableTableEmitter::collectTableEntries(
    GenericTable &Table, const std::vector<Record *> &Items) {
  ....
  RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
  if (!Ty)                                                              // <=
    PrintFatalError(Twine("Field '") + Field.Name + "' of table '" +
                    Table.Name + "' has incompatible type: " +
                    Ty->getAsString() + " vs. " +                       // <=
                    TI->getType()->getAsString());
   ....
}

د PVS-Studio خبرداری: V522 [CWE-476] د نل پوائنټر 'Ty' ته اشاره کول ممکن واقع شي. SearchableTableEmitter.cpp 614

زما په اند هر څه روښانه دي او وضاحت ته اړتیا نلري.

ټوټه N10: ټایپ

bool FormatTokenLexer::tryMergeCSharpNullConditionals() {
  ....
  auto &Identifier = *(Tokens.end() - 2);
  auto &Question = *(Tokens.end() - 1);
  ....
  Identifier->ColumnWidth += Question->ColumnWidth;
  Identifier->Type = Identifier->Type;                    // <=
  Tokens.erase(Tokens.end() - 1);
  return true;
}

PVS-Studio خبرداری: V570 د 'پیژندونکي-> ډول' متغیر ځان ته ټاکل شوی. FormatTokenLexer.cpp 249

ځان ته د متغیر ټاکلو لپاره هیڅ معنی نشته. ډیری احتمال، دوی غوښتل چې لیکلي:

Identifier->Type = Question->Type;

ټوټه N11: شکمن وقفه

void SystemZOperand::print(raw_ostream &OS) const {
  switch (Kind) {
    break;
  case KindToken:
    OS << "Token:" << getToken();
    break;
  case KindReg:
    OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
    break;
  ....
}

PVS-Studio خبرداری: V622 [CWE-478] د 'سوئچ' بیان معاینه کول په پام کې ونیسئ. دا ممکنه ده چې لومړی 'کیس' چلونکی ورک شوی وي. SystemZAsmParser.cpp 652

په پیل کې یو ډیر شکمن آپریټر شتون لري د وقفې. ایا تاسو دلته د بل څه لیکل هیر کړی؟

ټوټه N12: د حوالې کولو وروسته د اشارې چک کول

InlineCost AMDGPUInliner::getInlineCost(CallSite CS) {
  Function *Callee = CS.getCalledFunction();
  Function *Caller = CS.getCaller();
  TargetTransformInfo &TTI = TTIWP->getTTI(*Callee);

  if (!Callee || Callee->isDeclaration())
    return llvm::InlineCost::getNever("undefined callee");
  ....
}

PVS-Studio خبرداری: V595 [CWE-476] د 'کالي' پوائنټر مخکې له دې چې د nullptr په وړاندې تایید شي کارول شوی و. لینونه چیک کړئ: 172، 174. AMDGPUInline.cpp 172

نښې کالي په پیل کې په هغه وخت کې چې فنکشن ویل کیږي بې برخې کیږي ترلاسه کړئ.

او بیا دا معلومه شوه چې دا اشاره باید د مساوات لپاره وڅیړل شي 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-Studio خبرداری: V629 [CWE-190] د '~(سایز - 1) << 1' بیان معاینه کول په پام کې ونیسئ. د 32-bit ارزښت بټ بدلول د 64-bit ډول ته د ورپسې توسعې سره. AArch64AddressingModes.h 260

دا ممکن بګ نه وي او کوډ په سمه توګه کار کوي لکه څنګه چې اراده ده. مګر دا په څرګنده توګه یو ډیر شکمن ځای دی او باید وڅیړل شي.

راځئ چې متغیر ته ووایو اندازه د 16 سره مساوي دی، او بیا د کوډ لیکوال پالن کړی چې دا په متغیر کې ترلاسه کړي NImms معنی:

1111111111111111111111111111111111111111111111111111111111100000

په هرصورت، په حقیقت کې پایله به دا وي:

0000000000000000000000000000000011111111111111111111111111100000

حقیقت دا دی چې ټول حسابونه د 32-bit غیر لاسلیک شوي ډول په کارولو سره ترسره کیږي. او یوازې بیا، دا 32-bit نه لاسلیک شوی ډول به په واضح ډول پراخ شي uint64_t. په دې حالت کې، خورا مهم بټونه به صفر وي.

تاسو کولی شئ وضعیت په لاندې ډول حل کړئ:

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

ورته حالت: V629 [CWE-190] د 'Immr <<6' بیان معاینه کول په پام کې ونیسئ. د 32-bit ارزښت بټ بدلول د 64-bit ډول ته د ورپسې توسعې سره. AArch64AddressingModes.h 269

ټوټه N19: ورک شوی کلیدي بل?

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

PVS-Studio خبرداری: V646 [CWE-670] د غوښتنلیک منطق معاینه کول په پام کې ونیسئ. دا ممکنه ده چې 'بل' کلیدي ورک وي. AMDGPUAsmParser.cpp 5655

دلته هیڅ غلطی نشته. له هغه وخت راهیسې د لومړي بلاک 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-Studio اخطارونه:

  • V655 [CWE-480] تارونه سره یو ځای شوي خو کارول شوي ندي. د 'پایلې + نوم.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-Studio تحلیل کونکي په کارولو سره په LLVM 8 کې د بګونو موندل

PVS-Studio خبرداری: 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-Studio خبرداری: V519 [CWE-563] د 'NType' متغیر دوه ځله په پرله پسې توګه ارزښتونه ټاکل شوي. شاید دا یوه تېروتنه وي. لینونه چیک کړئ: 1663, 1664. MachOObjectFile.cpp 1664

زه فکر نه کوم چې دلته ریښتیا غلطي شتون لري. یوازې یو غیر ضروري تکرار دنده. خو بیا هم یوه تېروتنه.

همدا ډول:

  • V519 [CWE-563] د B.NDesc متغیر دوه ځله په پرله پسې ډول ټاکل شوي ارزښتونه. شاید دا یوه تېروتنه وي. لینونه چیک کړئ: 1488، 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] متغیر دوه ځله په پرله پسې توګه ارزښتونه ټاکل شوي. شاید دا یوه تېروتنه وي. لینونه چیک کړئ: 59، 61. coff2yaml.cpp 61

ټوټه N25-N27: نور بیا ګمارنې

اوس راځئ چې د بیا ګمارنې یو څه مختلف نسخه وګورو.

bool Vectorizer::vectorizeLoadChain(
    ArrayRef<Instruction *> Chain,
    SmallPtrSet<Instruction *, 16> *InstructionsProcessed) {
  ....
  unsigned Alignment = getAlignment(L0);
  ....
  unsigned NewAlign = getOrEnforceKnownAlignment(L0->getPointerOperand(),
                                                 StackAdjustedAlignment,
                                                 DL, L0, nullptr, &DT);
  if (NewAlign != 0)
    Alignment = NewAlign;
  Alignment = NewAlign;
  ....
}

د PVS-Studio خبرداری: V519 [CWE-563] د 'سازینګ' متغیر دوه ځله په پرله پسې توګه ارزښتونه ټاکل شوي. شاید دا یوه تېروتنه وي. لاینونه چیک کړئ: 1158, 1160. LoadStoreVectorizer.cpp 1160

دا خورا عجیب کوډ دی چې ظاهرا منطقي تېروتنه لري. په پیل کې، متغیر د سکیچ یو ارزښت په شرایطو پورې اړه لري. او بیا دنده بیا پیښیږي، مګر اوس پرته له کوم چک څخه.

ورته حالتونه دلته لیدل کیدی شي:

  • V519 [CWE-563] د اغیزو متغیر دوه ځله په پرله پسې توګه ارزښتونه ټاکل شوي. شاید دا یوه تېروتنه وي. لینونه چیک کړئ: 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] د 'ExpectNoDerefChunk' متغیر دوه ځله پرله پسې ارزښتونه ټاکل شوي. شاید دا یوه تېروتنه وي. لینونه چیک کړئ: 4970, 4973. SemaType.cpp 4973

ټوټه N28: تل ریښتینی حالت

static int readPrefixes(struct InternalInstruction* insn) {
  ....
  uint8_t byte = 0;
  uint8_t nextByte;
  ....
  if (byte == 0xf3 && (nextByte == 0x88 || nextByte == 0x89 ||
                       nextByte == 0xc6 || nextByte == 0xc7)) {
    insn->xAcquireRelease = true;
    if (nextByte != 0x90) // PAUSE instruction support             // <=
      break;
  }
  ....
}

PVS-Studio خبرداری: V547 [CWE-571] بیان 'nextByte!= 0x90' تل ریښتیا وي. X86DisassemblerDecoder.cpp 379

چک کول معنی نلري. متغیر NextByte تل د ارزښت سره مساوي نه وي 0x90، کوم چې د تیر چیک څخه تعقیب کیږي. دا یو ډول منطقي تېروتنه ده.

ټوټه N29 - N...: تل ریښتیني / غلط شرایط

شنونکی ډیری اخطارونه ورکوي چې ټول حالت (V547) یا د هغې یوه برخه (V560) تل ریښتیا یا غلط وي. ډیری وختونه دا ریښتینې تېروتنې ندي، مګر په ساده ډول د کوډ کوډ، د میکرو پراخولو پایله، او داسې نور. په هرصورت، دا د دې ټولو اخطارونو په نظر کې نیولو سره معنی لري، ځکه چې ریښتینې منطقي تېروتنې وخت په وخت واقع کیږي. د مثال په توګه، د کوډ دا برخه مشکوکه ده:

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

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

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

PVS-Studio خبرداری: V560 [CWE-570] د مشروط بیان یوه برخه تل غلطه ده: RegNo == 0xe. ARMDisassembler.cpp 939

ثابت 0xE په لسیزه کې 14 ارزښت دی. ازموینه RegNo == 0xe معنی نه لري ځکه که شمیره> 13، نو فنکشن به خپل اجرا بشپړ کړي.

د IDs V547 او V560 سره ډیری نور اخطارونه شتون درلود ، مګر لکه څنګه چې V595، زه د دې اخطارونو مطالعې سره علاقه نه لرم. دا لا دمخه روښانه وه چې ما د مقالې لیکلو لپاره کافي مواد درلودل :). له همدې امله، دا معلومه نده چې د دې ډول څومره غلطۍ په LLVM کې د PVS-Studio په کارولو سره پیژندل کیدی شي.

زه به تاسو ته یو مثال درکړم چې ولې د دې محرکاتو مطالعه ستړي کوي. شنونکی د لاندې کوډ لپاره د خبرتیا په صادرولو کې بالکل سم دی. خو دا یوه تېروتنه نه ده.

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

د PVS-Studio خبرداری: V547 [CWE-570] بیان '!HasError' تل غلط وي. UnwrappedLineParser.cpp 1635

ټوټه N30: ​​شکمن بیرته راستنیدل

static bool
isImplicitlyDef(MachineRegisterInfo &MRI, unsigned Reg) {
  for (MachineRegisterInfo::def_instr_iterator It = MRI.def_instr_begin(Reg),
      E = MRI.def_instr_end(); It != E; ++It) {
    return (*It).isImplicitDef();
  }
  ....
}

PVS-Studio خبرداری: V612 [CWE-670] په یوه لوپ کې غیر مشروط 'بیرته راستنیدنه'. R600OptimizeVectorRegisters.cpp 63

دا یا یوه تېروتنه یا یو ځانګړی تخنیک دی چې هدف یې د کوډ لوستلو پروګرام کونکو ته یو څه تشریح کول دي. دا ډیزاین ما ته هیڅ شی نه تشریح کوي او ډیر شکمن ښکاري. دا غوره ده چې داسې ونه لیکل شي :).

ستړي شوي؟ بیا د چای یا کافي جوړولو وخت دی.

د PVS-Studio تحلیل کونکي په کارولو سره په 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-Studio خبرداری: V779 [CWE-561] د لاسرسي وړ کوډ وموندل شو. دا ممکنه ده چې یوه تېروتنه شتون ولري. ExecutionUtils.cpp 146

لکه څنګه چې تاسو لیدلی شئ، د آپریټر دواړه څانګې if آپریټر ته د زنګ وهلو سره پای ته رسیږي بېرته. په دې اساس، کانتینر CtorDtors By 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-Studio خبرداری: V779 [CWE-561] د لاسرسي وړ کوډ وموندل شو. دا ممکنه ده چې یوه تېروتنه شتون ولري. LLParser.cpp 835

په زړه پورې حالت. راځئ لومړی دا ځای وګورو:

return ParseTypeIdEntry(SummaryID);
break;

په لومړي نظر کې، داسې ښکاري چې دلته کومه تېروتنه نشته. دا د چلونکي په څیر ښکاري د وقفې دلته یو اضافي دی، او تاسو کولی شئ په ساده ډول یې حذف کړئ. په هرصورت، ټول دومره ساده ندي.

شنونکی په دې کرښو کې خبرداری ورکوي:

Lex.setIgnoreColonInIdentifiers(false);
return false;

او په حقیقت کې، دا کوډ د لاسرسي وړ نه دی. په ټولو قضیو کې د لېږد د آپریټر څخه د زنګ سره پای ته رسیږي بېرته. او اوس یوازې بې حسه د وقفې دومره بې ضرره نه ښکاري! شاید یو له څانګو سره پای ته ورسیږي د وقفېنه بېرته?

ټوټه N33: د لوړ بټونو تصادفي بیا تنظیم

unsigned getStubAlignment() override {
  if (Arch == Triple::systemz)
    return 8;
  else
    return 1;
}

Expected<unsigned>
RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
                             const SectionRef &Section,
                             bool IsCode) {
  ....
  uint64_t DataSize = Section.getSize();
  ....
  if (StubBufSize > 0)
    DataSize &= ~(getStubAlignment() - 1);
  ....
}

PVS-Studio خبرداری: V784 د بټ ماسک اندازه د لومړي عملیاتي اندازې څخه کمه ده. دا به د لوړو بټونو له لاسه ورکولو لامل شي. RuntimeDyld.cpp 815

مهرباني وکړئ په یاد ولرئ چې فعالیت getStubAlignment د بیرته راستنیدو ډول نه لاسليک شوی. راځئ چې د بیان ارزښت محاسبه کړو، فرض کړئ چې فنکشن 8 ارزښت بیرته راولي:

~( getStubAlignment() - 1)

~(8u-1)

0xFFFFFFFF8u

اوس پام وکړئ چې متغیر د ډاټا اندازه د 64-bit نه لاسلیک شوی ډول لري. دا معلومه شوه چې کله د ډیټا سایز او 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-Studio خبرداری: V1028 [CWE-190] احتمالي جریان. د 'size_t' ډول ته د 'NumElts * Scale' آپریټر کاسټ کولو عملیات په پام کې ونیسئ، نه پایله. X86ISelLowering.h 1577

واضح ډول کاسټینګ د ډیریدو څخه مخنیوي لپاره کارول کیږي کله چې د ډول متغیرونو ضرب کول اینټ. په هرصورت، دلته واضح ډول کاسټینګ د ډیر جریان په وړاندې ساتنه نه کوي. لومړی، متغیرونه به ضرب شي، او یوازې بیا د ضرب 32-bit پایله به ډول ته پراخه شي. اندازه_ت.

ټوټه N35: د کاپي پیسټ ناکامه

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

V778 [CWE-682] دوه ورته کوډ ټوټې وموندل شوې. شاید، دا یو ټایپ دی او د 'Op1' متغیر باید د 'Op0' پرځای وکارول شي. InstCombineCompares.cpp 5507

دا نوی په زړه پورې تشخیص داسې شرایط په ګوته کوي چې د کوډ یوه ټوټه کاپي شوې او په هغه کې ځینې نومونه بدلول پیل شوي ، مګر په یو ځای کې یې سم ندي کړي.

مهرباني وکړئ په یاد ولرئ چې دوی په دوهم بلاک کې بدل شوي Op0 په Op1. مګر په یو ځای کې دوی دا سم نه کړل. ډیری احتمال دا باید داسې لیکل شوي وي:

if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
  I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
  return &I;
}

ټوټه N36: متغیر مغشوش

struct Status {
  unsigned Mask;
  unsigned Mode;

  Status() : Mask(0), Mode(0){};

  Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
    Mode &= Mask;
  };
  ....
};

PVS-Studio خبرداری: V1001 [CWE-563] د 'موډ' متغیر ټاکل شوی مګر د فنکشن په پای کې نه کارول کیږي. SIModeRegister.cpp 48

دا خورا خطرناکه ده چې د فنکشن دلیلونه د ټولګي غړو ته ورته نومونه ورکړئ. دا ډیره اسانه ده چې مغشوش شي. زموږ په وړاندې یوازې دا ډول قضیه ده. دا بیان معنی نه لري:

Mode &= Mask;

د فعالیت دلیل بدلیږي. بس نور څه نه. دا استدلال نور نه کارول کیږي. ډیری احتمال تاسو باید دا داسې لیکلي وي:

Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
  this->Mode &= Mask;
};

ټوټه N37: متغیر مغشوش

class SectionBase {
  ....
  uint64_t Size = 0;
  ....
};

class SymbolTableSection : public SectionBase {
  ....
};

void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
                                   SectionBase *DefinedIn, uint64_t Value,
                                   uint8_t Visibility, uint16_t Shndx,
                                   uint64_t Size) {
  ....
  Sym.Value = Value;
  Sym.Visibility = Visibility;
  Sym.Size = Size;
  Sym.Index = Symbols.size();
  Symbols.emplace_back(llvm::make_unique<Symbol>(Sym));
  Size += this->EntrySize;
}

خبرداری PVS-Studio: V1001 [CWE-563] د 'سایز' متغیر ټاکل شوی مګر د فنکشن په پای کې نه کارول کیږي. Object.cpp 424

حالت د پخوا په شان دی. باید ولیکل شي:

this->Size += this->EntrySize;

ټوټه N38-N47: دوی د شاخص چک کول هیر کړل

مخکې، موږ د تشخیصي محرک مثالونو ته کتنه وکړه V595. د دې جوهر دا دی چې اشاره په پیل کې له پامه غورځول شوې ، او یوازې بیا چیک کیږي. ځوان تشخیص V1004 په معنی کې مخالف دی، مګر ډیری غلطۍ هم څرګندوي. دا هغه حالتونه پیژني چیرې چې پوائنټر په پیل کې چک شوی و او بیا یې هیر شوی و. راځئ چې د LLVM دننه موندل شوي داسې قضیې وګورو.

int getGEPCost(Type *PointeeType, const Value *Ptr,
               ArrayRef<const Value *> Operands) {
  ....
  if (Ptr != nullptr) {                                            // <=
    assert(....);
    BaseGV = dyn_cast<GlobalValue>(Ptr->stripPointerCasts());
  }
  bool HasBaseReg = (BaseGV == nullptr);

  auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());  // <=
  ....
}

د PVS-Studio خبرداری: V1004 [CWE-476] د 'Ptr' پوائنټر وروسته له هغه چې د nullptr په وړاندې تایید شو په ناامنه توګه کارول شوی و. لینونه چیک کړئ: 729، 738. TargetTransformInfoImpl.h 738

متغیر پیټر کیدای شي مساوي وي nullptrلکه څنګه چې د چک لخوا ثبوت شوی:

if (Ptr != nullptr)

په هرصورت، د دې ټکي لاندې د ابتدايي چک کولو پرته بې ځایه کیږي:

auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());

راځئ چې یو بل ورته قضیه په پام کې ونیسو.

llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
                                                          bool Stub) {
  ....
  auto *FD = dyn_cast<FunctionDecl>(GD.getDecl());
  SmallVector<QualType, 16> ArgTypes;
  if (FD)                                                                // <=
    for (const ParmVarDecl *Parm : FD->parameters())
      ArgTypes.push_back(Parm->getType());
  CallingConv CC = FD->getType()->castAs<FunctionType>()->getCallConv(); // <=
  ....
}

د PVS-Studio خبرداری: V1004 [CWE-476] د 'FD' پوائنټر وروسته له هغه چې د nullptr په وړاندې تایید شو په ناامنه توګه کارول شوی و. لاینونه چیک کړئ: 3228, 3231. CGDebugInfo.cpp 3231

نښه ته پام وکړئ FD. زه ډاډه یم چې ستونزه په ښکاره ډول لیدل کیږي او کوم ځانګړي توضیح ته اړتیا نشته.

او نور:

static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result,
                                         Value *&BasePtr,
                                         const DataLayout &DL) {
  PointerType *PtrTy = dyn_cast<PointerType>(Ptr.getType());
  if (!PtrTy) {                                                   // <=
    Result = Polynomial();
    BasePtr = nullptr;
  }
  unsigned PointerBits =
      DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace());     // <=
  ....
}

د PVS-Studio خبرداری: V1004 [CWE-476] 'PtrTy' پوائنټر وروسته له هغه چې د nullptr په وړاندې تایید شو په ناامنه توګه کارول شوی و. لاین چیک کړئ: 960, 965. InterleavedLoadCombinePass.cpp 965

څنګه له داسې تېروتنو ځان وژغورو؟ د کوډ بیاکتنې ته ډیر پام وکړئ او د PVS-Studio جامد تحلیل کونکي څخه کار واخلئ ترڅو په منظم ډول خپل کوډ وګورئ.

د دې ډول غلطیو سره د کوډ نورو برخو حواله کولو کې هیڅ معنی نشته. زه به په مقاله کې یوازې د اخطارونو لیست پریږدم:

  • V1004 [CWE-476] د 'Expr' پوائنټر وروسته له هغه چې د nullptr په وړاندې تایید شو په ناامنه توګه کارول شوی و. لینونه چیک کړئ: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] د 'PI' پوائنټر وروسته له هغه چې د nullptr په وړاندې تایید شو په ناامنه توګه کارول شوی و. لینونه چیک کړئ: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] د '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-Studio خبرداری: V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'Emplace_back' میتود په واسطه د ستراتیژیو کانټینر ته اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. llvm-isel-fuzzer.cpp 58

د کانټینر په پای کې یو عنصر اضافه کول لکه std::vector > تاسو یوازې لیکل نشئ کولی xxx.push_back(نوی ایکس)، ځکه چې له دې څخه هیڅ ډول تبادله شتون نلري X* в std::unique_ptr.

یو عام حل د لیکلو لپاره دی xxx.emplace_back(نوی ایکس)ځکه چې دا ترکیب کوي: میتود 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-Studio شنونکي د دې پیژندلو لپاره زده کړل.

د دې ډول نور اخطارونه:

  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'Passes' کانټینر ته د 'emplace_back' میتود لخوا اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. PassManager.h 546
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د AAs کانټینر ته د 'emplace_back' میتود لخوا اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. AliasAnalysis.h 324
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'emplace_back' میتود په واسطه 'انټریز' کانټینر ته اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه 'AllEdges' کانټینر ته د 'emplace_back' میتود لخوا اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. CFGMST.h 268
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د VMaps کانټینر ته د 'emplace_back' میتود لخوا اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'ریکارډز' کانټینر ته د 'emplace_back' میتود لخوا اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. FDRLogBuilder.h 30
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه 'PendingSubmodules' کانټینر ته د 'emplace_back' میتود لخوا اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. ModuleMap.cpp 810
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'Emplace_back' میتود په واسطه د 'آبجیکٹس' کانټینر ته اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. DebugMap.cpp 88
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'Emplace_back' میتود په واسطه د ستراتیژیو کانټینر ته اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. 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' میتود په واسطه 'Operands' کانټینر ته اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'emplace_back' میتود په واسطه 'Stash' کانټینر ته اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] یو پوائنټر پرته له مالک څخه د 'میچرز' کانټینر ته د 'emplace_back' میتود لخوا اضافه کیږي. د یادښت لیک به د استثنا په صورت کې پیښ شي. GlobalISelEmitter.cpp 2702

پایلې

ما په ټولیز ډول 60 اخطارونه خپاره کړل او بیا یې ودرول. ایا نور نیمګړتیاوې شتون لري چې د PVS-Studio شنونکی په LLVM کې کشف کوي؟ هو، زه لرم. په هرصورت ، کله چې ما د مقالې لپاره د کوډ ټوټې لیکلې ، دا د ماښام ناوخته وه ، یا حتی شپه وه ، او ما پریکړه وکړه چې دا د ورځې ویلو وخت و.

زه امید لرم چې تاسو دا په زړه پوري موندلی او غواړئ د PVS- سټوډیو شنونکي هڅه وکړئ.

تاسو کولی شئ تحلیل کونکي ډاونلوډ کړئ او د ماین پاکۍ کیلي په دې ځای کې ترلاسه کړئ دا پاڼه.

تر ټولو مهم، په منظمه توګه جامد تحلیل وکاروئ. یو ځل چکونه، زموږ لخوا ترسره شوي ترڅو د جامد تحلیل میتودولوژي مشهوره کړي او PVS-Studio عادي سناریو نده.

ستاسو د کوډ کیفیت او اعتبار ښه کولو کې ښه بخت!

د PVS-Studio تحلیل کونکي په کارولو سره په LLVM 8 کې د بګونو موندل

که تاسو غواړئ دا مقاله د انګلیسي ژبو لیدونکو سره شریکه کړئ، مهرباني وکړئ د ژباړې لینک وکاروئ: اندری کارپوف. د PVS-Studio سره په LLVM 8 کې د بګ موندنه.

سرچینه: www.habr.com

Add a comment