LLVM 8 ۾ ڪيگ ڳولڻ PVS-اسٽوڊيو اينالائيزر استعمال ڪندي

LLVM 8 ۾ ڪيگ ڳولڻ PVS-اسٽوڊيو اينالائيزر استعمال ڪندي
اسان جي PVS-اسٽوڊيو اينالائيزر استعمال ڪندي LLVM پروجيڪٽ جي آخري ڪوڊ چيڪ کي ٻن سالن کان وڌيڪ گذري ويا آهن. اچو ته پڪ ڪريون ته PVS-اسٽوڊيو تجزيه نگار اڃا تائين هڪ اهم اوزار آهي غلطين ۽ امڪاني نقصانن جي نشاندهي ڪرڻ لاءِ. هن کي ڪرڻ لاء، اسان LLVM 8.0.0 رليز ۾ نئين غلطيون چيڪ ڪنداسين ۽ ڳوليندا سين.

لکڻ لاءِ مضمون

ايماندار ٿيڻ لاء، مون کي اهو مضمون لکڻ نه چاهيو. اهو دلچسپ ناهي ته هڪ منصوبي بابت لکڻ لاء جيڪو اسان اڳ ۾ ئي ڪيترائي ڀيرا چيڪ ڪيو آهي (1, 2, 3). اهو بهتر آهي ته ڪجهه نئين بابت لکڻ، پر مون وٽ ڪو اختيار ناهي.

هر دفعي LLVM جو نئون ورزن جاري يا اپڊيٽ ڪيو ويندو آهي ڪلنگ جامد تجزيه ڪندڙ، اسان کي اسان جي ميل ۾ هيٺين قسم جا سوال ملن ٿا:

ڏسو، Clang Static Analyzer جو نئون نسخو نئين غلطيون ڳولڻ سکيو آھي! اهو مون کي لڳي ٿو ته PVS-اسٽوڊيو استعمال ڪرڻ جي لاڳاپا گهٽجي رهي آهي. Clang اڳي کان وڌيڪ غلطيون ڳولي ٿو ۽ PVS-اسٽوڊيو جي صلاحيتن سان پڪڙي ٿو. ان بابت توهان جو ڇا خيال آهي؟

ان لاءِ مان هميشه ڪجهه جواب ڏيڻ چاهيان ٿو جهڙوڪ:

اسان به بيڪار نه ويٺا آهيون! اسان PVS-اسٽوڊيو تجزيي جي صلاحيتن کي خاص طور تي بهتر ڪيو آهي. تنهن ڪري پريشان نه ٿيو، اسان اڳي وانگر اڳواڻي جاري رکون ٿا.

بدقسمتي سان، هي هڪ خراب جواب آهي. ان ۾ ڪو به ثبوت نه آهي. ۽ اهو ئي سبب آهي ته مان هاڻي هي مضمون لکي رهيو آهيان. تنهن ڪري، LLVM پروجيڪٽ هڪ ڀيرو ٻيهر چيڪ ڪيو ويو آهي ۽ ان ۾ مختلف غلطيون مليون آهن. مان ھاڻي انھن کي ڏيکاريندس جيڪي مون کي دلچسپ لڳي رھيا ھئا. Clang Static Analyzer انهن نقصن کي ڳولي نه ٿو سگهي (يا ان جي مدد سان ائين ڪرڻ انتهائي مشڪل آهي). پر اسان ڪري سگهون ٿا. ان کان سواء، مون هڪ شام ۾ اهي سڀئي غلطيون ڳولي ۽ لکي ڇڏيو.

پر مضمون لکڻ ۾ ڪيترائي هفتا لڳي ويا. مان صرف پنهنجو پاڻ کي نه آڻي سگهيو ته اهو سڀ ڪجهه متن ۾ وجهي :).

رستي جي ذريعي، جيڪڏهن توهان دلچسپي رکو ٿا ته PVS-اسٽوڊيو تجزيي ۾ ڪهڙي ٽيڪنالاجي استعمال ڪيا ويا آهن غلطين ۽ امڪاني نقصانن جي نشاندهي ڪرڻ لاء، پوء آئون هن سان واقف ٿيڻ جي صلاح ڏيان ٿو. نوٽ.

نئين ۽ پراڻي تشخيص

جيئن اڳ ۾ ئي نوٽ ڪيو ويو آهي، اٽڪل ٻه سال اڳ LLVM پروجيڪٽ کي هڪ ڀيرو ٻيهر چيڪ ڪيو ويو، ۽ غلطيون مليون آهن انهن کي درست ڪيو ويو. هاڻي هي مضمون غلطين جي هڪ نئين بيچ پيش ڪندو. نوان ڪيڙا ڇو مليا؟ هن جا 3 سبب آهن:

  1. LLVM پروجيڪٽ ترقي ڪري رهيو آهي، پراڻي ڪوڊ کي تبديل ڪندي ۽ نئون ڪوڊ شامل ڪري رهيو آهي. قدرتي طور تي، تبديل ٿيل ۽ لکيل ڪوڊ ۾ نيون غلطيون آهن. اهو واضح طور تي ظاهر ڪري ٿو ته جامد تجزيو باقاعده استعمال ڪيو وڃي، ۽ ڪڏهن ڪڏهن نه. اسان جا آرٽيڪل PVS-اسٽوڊيو تجزيه نگار جي صلاحيتن کي چڱي طرح ڏيکارين ٿا، پر اهو ڪوڊ جي معيار کي بهتر ڪرڻ ۽ غلطي کي درست ڪرڻ جي قيمت کي گهٽائڻ سان ڪو به تعلق ناهي. استعمال ڪريو جامد ڪوڊ تجزيي ڪندڙ باقاعدي!
  2. اسان موجوده تشخيص کي حتمي ۽ بهتر ڪري رهيا آهيون. تنهن ڪري، تجزيه ڪندڙ غلطين جي نشاندهي ڪري سگهي ٿو جيڪا هن اڳوڻي اسڪين دوران نوٽيس نه ڪئي هئي.
  3. PVS-Studio ۾ نوان تشخيص ظاهر ٿيا آهن جيڪي 2 سال اڳ موجود نه هئا. مون فيصلو ڪيو ته انهن کي الڳ سيڪشن ۾ نمايان ڪرڻ لاءِ واضح طور تي PVS-Studio جي ترقي کي ڏيکاري.

خرابين جي نشاندهي ڪئي وئي جيڪا 2 سال اڳ موجود هئي

ٽڪرا N1: ڪاپي پيسٽ ڪريو

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

PVS-اسٽوڊيو ڊيڄاريندڙ: V501 [CWE-570] هڪجهڙا ذيلي اظهار آهن 'Name.startswith("avx512.mask.permvar.")' جي کاٻي ۽ ساڄي طرف '||' آپريٽر AutoUpgrade.cpp 73

اهو ٻه ڀيرا چيڪ ڪيو ويو آهي ته نالو "avx512.mask.permvar" جي سبسٽرنگ سان شروع ٿئي ٿو. ٻئي چيڪ ۾، اهي واضح طور تي ڪجهه ٻيو لکڻ چاهيندا هئا، پر نقل ٿيل متن کي درست ڪرڻ وساريو.

ٽڪرو N2: ٽائپو

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

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

خبردار PVS-اسٽوڊيو: V501 هڪجهڙا ذيلي اظهار آهن 'CXNameRange_WantQualifier' کاٻي ۽ ساڄي پاسي '|' آپريٽر CIndex.cpp 7245

ٽائپو جي ڪري، ساڳيو نالو مسلسل ٻه ڀيرا استعمال ٿيندو آهي CXNameRange_WantQualifier.

ٽڪرا N3: آپريٽر جي ترجيح سان مونجهارو

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

PVS-اسٽوڊيو ڊيڄاريندڙ: V502 [CWE-783] شايد '؟:' آپريٽر ڪم ڪري ٿو مختلف انداز ۾ ان جي توقع کان. '؟:' آپريٽر کي '==' آپريٽر کان گھٽ ترجيح آھي. PPCTargetTransformInfo.cpp 404

منهنجي خيال ۾، هي هڪ تمام خوبصورت غلطي آهي. ها، مون کي خبر آهي ته مون وٽ خوبصورتي بابت عجيب خيال آهن :).

هاڻي، جي مطابق آپريٽر جي ترجيحات، اظهار جو اندازو هن ريت ڪيو ويو آهي:

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

عملي نقطي نظر کان، اهڙي حالت ڪا به معني نه آهي، ڇاڪاڻ ته ان کي گهٽائي سگهجي ٿو:

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

هي هڪ واضح غلطي آهي. گهڻو ڪري، اهي هڪ متغير سان 0/1 جو مقابلو ڪرڻ چاهيندا هئا اشهد. ڪوڊ کي درست ڪرڻ لاءِ توهان کي ٽيرنري آپريٽر جي چوڌاري قوس شامل ڪرڻ جي ضرورت آهي:

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

رستي ۾، ٽرينري آپريٽر تمام خطرناڪ آهي ۽ منطقي غلطين کي ثابت ڪري ٿو. ان سان تمام محتاط رھو ۽ قوس وارن سان لالچ نه ٿيو. مون هن موضوع کي وڌيڪ تفصيل سان ڏٺو هتي, باب ۾ “Beware of the?: Operator and enclose it in parentheses.”

ٽڪرا N4، N5: Null pointer

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 null آهي، هڪ ڊيڄاريندڙ جاري ڪيو وڃي. جڏهن ته، ان جي بدران، هي ساڳيو نال پوائنٽر کي رد ڪيو ويندو: LHS->getAsString().

هي هڪ تمام عام صورتحال آهي جڏهن هڪ غلطي لڪايو ويندو آهي هڪ غلطي هينڊلر ۾، ڇاڪاڻ ته ڪو به انهن کي جانچ نه ڪندو آهي. جامد تجزيي وارا سڀئي رسيبل ڪوڊ چيڪ ڪن ٿا، پوءِ به اهو ڪيترو به استعمال نه ٿئي. اهو هڪ تمام سٺو مثال آهي ته ڪيئن جامد تجزيو ٻين جاچ ۽ غلطي جي تحفظ جي ٽيڪنالاجي کي پورو ڪري ٿو.

ساڳي پوائنٽر سنڀالڻ جي غلطي RHS صرف ھيٺ ڏنل ڪوڊ ۾ اجازت ڏنل آھي: V522 [CWE-476] نال پوائنٽر 'RHS' جي حوالي سان ٿي سگھي ٿي. TGParser.cpp 2186

ٽڪرا N6: حرڪت کان پوء پوائنٽر استعمال ڪندي

static Expected<bool>
ExtractBlocks(....)
{
  ....
  std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap);
  ....
  BD.setNewProgram(std::move(ProgClone));                                // <=
  MiscompiledFunctions.clear();

  for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
    Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);  // <=
    assert(NewF && "Function not found??");
    MiscompiledFunctions.push_back(NewF);
  }
  ....
}

PVS-اسٽوڊيو خبردار: V522 [CWE-476] نيل پوائنٽر جي حوالي سان 'ProgClone' ٿي سگھي ٿي. Miscompilation.cpp 601

شروعات ۾ هڪ سمارٽ اشارو ProgClone اعتراض کي ختم ڪري ٿو:

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

حقيقت ۾، هاڻي ProgClone هڪ null اشارو آهي. تنهن ڪري، هڪ null pointer dereference صرف هيٺ ٿيڻ گهرجي:

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

پر، حقيقت ۾، ائين نه ٿيندو! نوٽ ڪريو ته لوپ اصل ۾ عمل نه ڪيو ويو آهي.

ڪنٽينر جي شروعات ۾ Miscompiled functions صاف ڪيو:

MiscompiledFunctions.clear();

اڳيون، هن ڪنٽينر جي ماپ لوپ حالت ۾ استعمال ڪيو ويندو آهي:

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

اهو ڏسڻ ۾ آسان آهي ته لوپ شروع نٿو ٿئي. منهنجو خيال آهي ته اهو پڻ هڪ بگ آهي ۽ ڪوڊ کي مختلف انداز ۾ لکڻ گهرجي.

اهو لڳي ٿو ته اسان غلطين جي مشهور برابري سان منهن ڪيو آهي! هڪ غلطي ٻئي کي ڇڪيندي آهي :).

ٽڪرا N7: حرڪت کان پوء پوائنٽر استعمال ڪندي

static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
                                    std::unique_ptr<Module> Safe) {
  outs() << "  Optimizing functions being tested: ";
  std::unique_ptr<Module> Optimized =
      BD.runPassesOn(Test.get(), BD.getPassesToRun());
  if (!Optimized) {
    errs() << " Error running this sequence of passes"
           << " on the input program!n";
    BD.setNewProgram(std::move(Test));                       // <=
    BD.EmitProgressBitcode(*Test, "pass-error", false);      // <=
    if (Error E = BD.debugOptimizerCrash())
      return std::move(E);
    return false;
  }
  ....
}

PVS-اسٽوڊيو وارننگ: V522 [CWE-476] نيل پوائنٽر جي حوالي سان 'ٽيسٽ' ٿي سگھي ٿي. Miscompilation.cpp 709

وري ساڳي صورتحال. پهرين تي، اعتراض جي مواد کي منتقل ڪيو ويو آهي، ۽ پوء اهو استعمال ڪيو ويندو آهي ڄڻ ته ڪجھ به نه ٿيو هو. مان هن صورتحال کي پروگرام ڪوڊ ۾ وڌيڪ ۽ گهڻو ڪري ڏسان ٿو جڏهن تحريڪ سيمينٽڪس C ++ ۾ ظاهر ٿيو. اهو ئي سبب آهي ته مون کي C ++ ٻولي پيار آهي! توهان جي پنهنجي ٽنگ بند ڪرڻ لاء وڌيڪ ۽ وڌيڪ نوان طريقا آهن. PVS-اسٽوڊيو تجزيه ڪندڙ هميشه ڪم ڪندو :).

فريگمينٽ N8: نيل پوائنٽر

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

PVS-اسٽوڊيو ڊيڄاريندڙ: V522 [CWE-476] نيل پوائنٽر جي حوالي سان 'قسم' ٿي سگھي ٿي. PrettyFunctionDumper.cpp 233

غلطي سنڀاليندڙن کان علاوه، ڊيبگنگ پرنٽ آئوٽ افعال عام طور تي آزمائشي نه آهن. اسان جي سامهون صرف هڪ ڪيس آهي. فنڪشن صارف جي انتظار ۾ آهي، جيڪو، پنهنجي مسئلن کي حل ڪرڻ بدران، ان کي درست ڪرڻ تي مجبور ڪيو ويندو.

صحيح طور تي

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

فريگمينٽ N9: نيل پوائنٽر

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

PVS-اسٽوڊيو ڊيڄاريندڙ: V522 [CWE-476] نيل پوائنٽر 'Ty' جي حوالي سان ٿي سگھي ٿي. SearchableTableEmitter.cpp 614

منهنجو خيال آهي ته هر شي واضح آهي ۽ وضاحت جي ضرورت ناهي.

ٽڪرو N10: ٽائپو

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

PVS-اسٽوڊيو ڊيڄاريندڙ: V570 'Identifier->Type' متغير پاڻ کي لڳايو ويو آهي. 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] nullptr جي خلاف تصديق ٿيڻ کان اڳ 'U' پوائنٽر استعمال ڪيو ويو. لائنون چيڪ ڪريو: 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-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-اسٽوڊيو ڊيڄاريندڙ: 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] تارن کي ڳنڍيو ويو پر استعمال نه ڪيو ويو. 'نتيجو + 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();
    }
  }
}

پاڻ کي خطرناڪ ڪوڊ ڳولڻ جي ڪوشش ڪريو. ۽ هي هڪ تصوير آهي ڌيان ڇڪائڻ لاءِ ته جيئن فوري طور تي جواب تي نظر نه اچي:

LLVM 8 ۾ ڪيگ ڳولڻ PVS-اسٽوڊيو اينالائيزر استعمال ڪندي

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] The 'Alignment' variable کي لڳاتار ٻه ڀيرا قدر لڳايو ويو آهي. شايد هي هڪ غلطي آهي. لائنون چيڪ ڪريو: 1158, 1160. LoadStoreVectorizer.cpp 1160

هي تمام عجيب ڪوڊ آهي جنهن ۾ ظاهري طور تي هڪ منطقي غلطي آهي. شروعات ۾، متغير سڌي هڪ قدر مقرر ڪيو ويو آهي شرط تي منحصر آهي. ۽ پوءِ تفويض ٻيهر ٿيندي آهي، پر هاڻي بغير ڪنهن چيڪ جي.

ساڳي حالتون هتي ڏسي سگهجن ٿيون:

  • V519 [CWE-563] 'اثرات' متغير کي ٻه ڀيرا لڳاتار قدر مقرر ڪيو ويو آهي. شايد هي هڪ غلطي آهي. لائنون چيڪ ڪريو: 152، 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] 'ExpectNoDerefChunk' متغير کي ٻه ڀيرا لڳاتار قدر مقرر ڪيو ويو آهي. شايد هي هڪ غلطي آهي. لائنون چيڪ ڪريو: 4970, 4973. SemaType.cpp 4973

ٽڪرا N28: هميشه صحيح حالت

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

PVS-اسٽوڊيو ڊيڄاريندڙ: V547 [CWE-571] اظهار 'nextByte != 0x90' هميشه سچو آهي. X86DisassemblerDecoder.cpp 379

چڪاس ڪرڻ جو مطلب ناهي. متغير nextByte هميشه قيمت جي برابر ناهي 0x90، جيڪو پوئين چيڪ کان پٺيان آهي. هي ڪجهه قسم جي منطقي غلطي آهي.

ٽڪرا 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 decimal ۾. امتحان 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-اسٽوڊيو ڊيڄاريندڙ: V612 [CWE-670] هڪ لوپ اندر هڪ غير مشروط 'واپسي'. R600OptimizeVectorRegisters.cpp 63

هي يا ته هڪ غلطي آهي يا هڪ مخصوص ٽيڪنڪ جنهن جو مقصد آهي ته ڪجهه وضاحت ڪرڻ لاءِ پروگرامرز کي ڪوڊ پڙهڻ. هي ڊزائن مون کي ڪجهه به وضاحت نٿو ڪري ۽ تمام مشڪوڪ ڏسڻ ۾ اچي ٿو. ائين نه لکڻ بهتر آهي :).

ٿڪل؟ پوءِ چانهه يا ڪافي ٺاهڻ جو وقت آهي.

LLVM 8 ۾ ڪيگ ڳولڻ PVS-اسٽوڊيو اينالائيزر استعمال ڪندي

نقصن جي نشاندهي ڪئي وئي نئين تشخيص ذريعي

منهنجو خيال آهي ته پراڻي تشخيص جون 30 سرگرميون ڪافي آهن. اچو ته هاڻي ڏسون ته ڪهڙيون دلچسپ شيون ڳولي سگهجن ٿيون نئين تشخيص سان جيڪي تجزيي ۾ ظاهر ٿيڻ کانپوءِ پوئين چيڪ. مجموعي طور تي، 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 آپريٽر کي ڪال سان ختم ٿئي ٿو موٽڻ. مطابق، ڪنٽينر 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-اسٽوڊيو ڊيڄاريندڙ: 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

هاڻي نوٽ ڪريو ته variable DataSize ھڪڙو 64-bit غير دستخط ٿيل قسم آھي. اهو ظاهر ٿئي ٿو ته جڏهن 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' آپريٽر جي casting operands کي 'size_t' قسم تي، نتيجو نه. X86ISelLowering.h 1577

واضح قسم جو ڪاسٽنگ استعمال ڪيو ويندو آهي اوور فلو کان بچڻ لاءِ جڏهن ضرب قسم جي متغير کي int. بهرحال، هتي واضح قسم جي ڪاسٽنگ اوور فلو جي خلاف حفاظت نٿو ڪري. پهرين، متغيرن کي ضرب ڪيو ويندو، ۽ صرف ان کان پوء ضرب جو 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-اسٽوڊيو ڊيڄاريندڙ: 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-اسٽوڊيو جامد تجزيه ڪندڙ توهان جي ڪوڊ کي باقاعده چيڪ ڪرڻ لاء.

ھن قسم جي غلطين سان ٻين ڪوڊ ٽڪرن جو حوالو ڏيڻ ۾ ڪو به مقصد نه آھي. مان آرٽيڪل ۾ صرف ڊيڄاريندڙن جي فهرست ڇڏيندس:

  • V1004 [CWE-476] 'Expr' پوائنٽر غير محفوظ طريقي سان استعمال ڪيو ويو ان جي تصديق ٿيڻ کان پوءِ nullptr جي خلاف. لائنون چيڪ ڪريو: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] 'PI' پوائنٽر غير محفوظ طور تي استعمال ڪيو ويو ان کان پوءِ ان جي تصديق ڪئي وئي nullptr جي خلاف. لائنون چيڪ ڪريو: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] 'StatepointCall' پوائنٽر غير محفوظ طور استعمال ڪيو ويو ان کان پوءِ ان جي تصديق ڪئي وئي nullptr جي خلاف. لائنون چيڪ ڪريو: 4371، 4379. Verifier.cpp 4379
  • V1004 [CWE-476] 'RV' پوائنٽر غير محفوظ طور استعمال ڪيو ويو ان کان پوءِ ان جي تصديق ڪئي وئي nullptr جي خلاف. لائنون چيڪ ڪريو: 2263، 2268. TGParser.cpp 2268
  • V1004 [CWE-476] 'CalleeFn' پوائنٽر غير محفوظ طور تي استعمال ڪيو ويو ان جي تصديق ٿيڻ کان پوءِ nullptr جي خلاف. لائنون چيڪ ڪريو: 1081، 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] 'TC' پوائنٽر غير محفوظ طور تي استعمال ڪيو ويو ان جي تصديق ٿيڻ کان پوءِ nullptr جي خلاف. لائنون چيڪ ڪريو: 1819، 1824. Driver.cpp 1824

ٽڪرا N48-N60: نازڪ نه، پر هڪ عيب (ممڪن ياداشت جي ليکي)

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

PVS-اسٽوڊيو ڊيڄاريندڙ: V1023 [CWE-460] مالڪ کان سواءِ هڪ پوائنٽر شامل ڪيو ويو آهي 'اسٽريٽيز' ڪنٽينر ۾ 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-isel-fuzzer.cpp 58

ڪنٽينر جي آخر ۾ هڪ عنصر شامل ڪرڻ جهڙوڪ std::vector > توهان صرف لکي نٿا سگهو xxx.push_back(نئون X), ڇاڪاڻ ته اتي ڪا به غير واضح تبديلي نه آهي X* в std::unique_ptr.

هڪ عام حل لکڻ آهي xxx.emplace_back(نئون X)ڇاڪاڻ ته اهو گڏ ڪري ٿو: طريقو emplace_back ھڪڙي عنصر کي سڌو سنئون پنھنجي دليلن مان ٺاھي ٿو ۽ تنھنڪري واضح ٺاھيندڙ استعمال ڪري سگھن ٿا.

اهو محفوظ ناهي. جيڪڏهن ویکٹر مڪمل آهي، پوء ياداشت ٻيهر مختص ڪئي وئي آهي. ياداشت جي بحالي واري آپريشن ناڪام ٿي سگھي ٿي، نتيجي ۾ ھڪڙو استثنا اڇلايو وڃي ٿو std::bad_alloc. انهي صورت ۾، پوائنٽر گم ٿي ويندو ۽ ٺاهيل اعتراض ڪڏهن به ختم نه ڪيو ويندو.

هڪ محفوظ حل پيدا ڪرڻ آهي منفرد_پيٽرجيڪو پوائنٽر جو مالڪ هوندو ان کان اڳ جو ویکٹر ياداشت کي ٻيهر ترتيب ڏيڻ جي ڪوشش ڪري:

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] هڪ پوائنٽر بغير مالڪ جي 'Passes' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. PassManager.h 546
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'AAs' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. الياس ايناليسس ايڇ 324
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Entry' ڪنٽينر ۾ شامل ڪيو ويو آهي '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] هڪ پوائنٽر بغير مالڪ جي 'Records' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. FDRLogBuilder.h 30
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'PendingSubmodules' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. ModuleMap.cpp 810
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Objects' ڪنٽينر ۾ 'emplace_back' طريقي سان شامل ڪيو ويو آهي. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. DebugMap.cpp 88
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'اسٽريٽيز' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 685
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 686
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 688
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 689
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 690
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 691
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 692
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 693
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Modifiers' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. llvm-stress.cpp 694
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Operands' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'Stash' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] هڪ پوائنٽر بغير مالڪ جي 'ميچز' ڪنٽينر ۾ شامل ڪيو ويو آهي 'emplace_back' طريقي سان. هڪ ميموري ليک هڪ استثنا جي صورت ۾ ٿيندي. GlobalISelEmitter.cpp 2702

ٿڪل

مون مجموعي طور تي 60 ڊيڄاريندڙ جاري ڪيا ۽ پوء بند ڪيو. ڇا ٻيا نقص آھن جيڪي PVS-اسٽوڊيو تجزيه ڪندڙ LLVM ۾ ڳولي ٿو؟ ها مون وٽ آهي. بهرحال، جڏهن آئون آرٽيڪل لاءِ ڪوڊ جا ٽڪرا لکي رهيو هوس، اهو دير سان شام جو هو، يا بلڪه رات به، ۽ مون فيصلو ڪيو ته اهو وقت هو ان کي هڪ ڏينهن سڏڻ جو.

مون کي اميد آهي ته توهان ان کي دلچسپ محسوس ڪيو ۽ ڪوشش ڪرڻ چاهيندا PVS-اسٽوڊيو تجزيه ڪندڙ.

توھان ڊائون لوڊ ڪري سگھوٿا تجزيو ڪندڙ ۽ حاصل ڪريو مائنسويپر ڪيچ تي هي صفحو.

سڀ کان اهم، جامد تجزيو باقاعده استعمال ڪريو. ھڪڙي وقت جي چڪاسجامد تجزيي جي طريقي کي مشهور ڪرڻ لاءِ اسان جي طرفان ڪيو ويو آهي ۽ PVS-اسٽوڊيو هڪ عام منظر نه آهي.

توهان جي ڪوڊ جي معيار ۽ اعتبار کي بهتر بڻائڻ ۾ سٺي قسمت!

LLVM 8 ۾ ڪيگ ڳولڻ PVS-اسٽوڊيو اينالائيزر استعمال ڪندي

جيڪڏهن توهان هن مضمون کي انگريزي ڳالهائيندڙ سامعين سان حصيداري ڪرڻ چاهيو ٿا، مهرباني ڪري ترجمو لنڪ استعمال ڪريو: Andrey Karpov. PVS-Studio سان LLVM 8 ۾ بگ ڳولڻ.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو