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 تجزیہ کار کی صلاحیتوں کو نمایاں طور پر بہتر کیا ہے۔ تو پریشان نہ ہوں، ہم پہلے کی طرح قیادت کرتے رہیں گے۔

بدقسمتی سے، یہ ایک برا جواب ہے۔ اس میں کوئی ثبوت نہیں۔ اور اسی لیے اب یہ مضمون لکھ رہا ہوں۔ لہذا، ایل ایل وی ایم پروجیکٹ کی ایک بار پھر جانچ پڑتال کی گئی ہے اور اس میں کئی طرح کی غلطیاں پائی گئی ہیں۔ اب میں ان کا مظاہرہ کروں گا جو مجھے دلچسپ لگے۔ کلینگ سٹیٹک اینالائزر ان خرابیوں کو تلاش نہیں کر سکتا (یا اس کی مدد سے ایسا کرنا انتہائی تکلیف دہ ہے)۔ لیکن ہم کر سکتے ہیں۔ مزید یہ کہ میں نے ایک شام میں یہ تمام غلطیاں ڈھونڈ کر لکھ دیں۔

لیکن مضمون لکھنے میں کئی ہفتے لگ گئے۔ میں یہ سب کچھ ٹیکسٹ میں ڈالنے کے لیے خود کو نہیں لا سکا :)۔

ویسے، اگر آپ اس بات میں دلچسپی رکھتے ہیں کہ PVS-Studio تجزیہ کار میں غلطیوں اور ممکنہ کمزوریوں کی نشاندہی کرنے کے لیے کون سی ٹیکنالوجیز استعمال کی جاتی ہیں، تو میرا مشورہ ہے کہ اس سے واقف ہو جائیں۔ نوٹ.

نئی اور پرانی تشخیص

جیسا کہ پہلے ہی نوٹ کیا جا چکا ہے، تقریباً دو سال پہلے ایل ایل وی ایم پروجیکٹ کو ایک بار پھر چیک کیا گیا، اور جو غلطیاں پائی گئیں ان کو درست کر دیا گیا۔ اب یہ مضمون غلطیوں کی ایک نئی کھیپ پیش کرے گا۔ نئے کیڑے کیوں پائے گئے؟ اس کی 3 وجوہات ہیں:

  1. LLVM پروجیکٹ تیار ہو رہا ہے، پرانے کوڈ کو تبدیل کر رہا ہے اور نیا کوڈ شامل کر رہا ہے۔ قدرتی طور پر، ترمیم شدہ اور تحریری کوڈ میں نئی ​​غلطیاں ہیں۔ یہ واضح طور پر ظاہر کرتا ہے کہ جامد تجزیہ کو باقاعدگی سے استعمال کیا جانا چاہئے، اور کبھی کبھار نہیں۔ ہمارے مضامین PVS-Studio تجزیہ کار کی صلاحیتوں کو اچھی طرح سے ظاہر کرتے ہیں، لیکن اس کا کوڈ کے معیار کو بہتر بنانے اور غلطیوں کو ٹھیک کرنے کی لاگت کو کم کرنے سے کوئی لینا دینا نہیں ہے۔ جامد کوڈ تجزیہ کار کو باقاعدگی سے استعمال کریں!
  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-Studio وارننگ: 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-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 null پوائنٹر 'LHS' کی ڈیریفرنسنگ ہو سکتی ہے۔ TGParser.cpp 476

اگر اشارہ کرنے والا LHS null ہے، ایک انتباہ جاری کیا جانا چاہئے. تاہم، اس کے بجائے، اسی null پوائنٹر کا حوالہ دیا جائے گا: LHS->getAsString().

یہ ایک بہت ہی عام صورت حال ہے جب ایک ایرر ہینڈلر میں غلطی چھپی ہوئی ہے، کیونکہ کوئی بھی ان کی جانچ نہیں کرتا ہے۔ جامد تجزیہ کار تمام قابل رسائی کوڈ کو چیک کرتے ہیں، اس سے کوئی فرق نہیں پڑتا ہے کہ اسے کتنی بار استعمال کیا گیا ہے۔ یہ ایک بہت اچھی مثال ہے کہ کس طرح جامد تجزیہ دیگر جانچ اور غلطی سے بچاؤ کی تکنیکوں کی تکمیل کرتا ہے۔

اسی طرح کے پوائنٹر ہینڈلنگ کی خرابی۔ RHS بالکل نیچے کوڈ میں اجازت دی گئی ہے: V522 [CWE-476] null pointer '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] null pointer 'ProgClone' کی ڈیریفرنسنگ ہو سکتی ہے۔ Miscompilation.cpp 601

شروع میں ایک سمارٹ پوائنٹر پروگ کلون آبجیکٹ کا مالک ہونا چھوڑ دیتا ہے:

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

اصل میں، اب پروگ کلون ایک کالعدم پوائنٹر ہے۔ لہذا، ایک null پوائنٹر 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-Studio تجزیہ کار کے پاس ہمیشہ کام رہے گا :)۔

فریگمنٹ 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-Studio وارننگ: V522 [CWE-476] null pointer 'Type' کی ڈیریفرنسنگ ہو سکتی ہے۔ 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-Studio وارننگ: V522 [CWE-476] null pointer '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 '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-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 nullptr کے خلاف تصدیق کرنے سے پہلے 'Callee' پوائنٹر کا استعمال کیا گیا تھا۔ لائنیں چیک کریں: 476, 172. AMDGPUInline.cpp 174

پوائنٹر کالے شروع میں اس وقت ڈیریفرنس کیا جاتا ہے جب فنکشن کو بلایا جاتا ہے۔ ٹی ٹی آئی حاصل کریں۔.

اور پھر پتہ چلتا ہے کہ اس پوائنٹر کو برابری کے لیے چیک کیا جانا چاہیے۔ 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-بٹ قسم میں بعد میں توسیع کے ساتھ 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-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] تاروں کو جوڑا گیا لیکن استعمال نہیں کیا گیا۔ '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-Studio تجزیہ کار کا استعمال کرتے ہوئے LLVM 8 میں کیڑے تلاش کرنا

PVS-Studio وارننگ: V708 [CWE-758] خطرناک تعمیر استعمال کی جاتی ہے: 'FeaturesMap[Op] = FeaturesMap.size()'، جہاں 'FeaturesMap' 'map' کلاس کا ہے۔ یہ غیر متعینہ رویے کی قیادت کر سکتا ہے. 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 کوئی معنی نہیں رکھتا کیونکہ اگر RegNo > 13، پھر فنکشن اپنا عمل مکمل کر لے گا۔

IDs V547 اور V560 کے ساتھ بہت سی دوسری وارننگز تھیں، لیکن جیسا کہ V595میں ان انتباہات کا مطالعہ کرنے میں دلچسپی نہیں رکھتا تھا۔ یہ پہلے ہی واضح تھا کہ میرے پاس مضمون لکھنے کے لیے کافی مواد موجود ہے :)۔ لہذا، یہ معلوم نہیں ہے کہ PVS-Studio کا استعمال کرتے ہوئے LLVM میں اس قسم کی کتنی غلطیوں کی نشاندہی کی جا سکتی ہے۔

میں آپ کو ایک مثال دوں گا کہ ان محرکات کا مطالعہ کیوں بورنگ ہے۔ مندرجہ ذیل کوڈ کے لیے انتباہ جاری کرنے میں تجزیہ کار بالکل درست ہے۔ لیکن یہ کوئی غلطی نہیں ہے۔

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 آپریٹر کو کال کے ساتھ ختم ہوتا ہے۔ واپسی. اس کے مطابق، کنٹینر CtorDtorsByPriority کبھی صاف نہیں کیا جائے گا۔

فریگمنٹ N32: ناقابل رسائی کوڈ

bool LLParser::ParseSummaryEntry() {
  ....
  switch (Lex.getKind()) {
  case lltok::kw_gv:
    return ParseGVEntry(SummaryID);
  case lltok::kw_module:
    return ParseModuleEntry(SummaryID);
  case lltok::kw_typeid:
    return ParseTypeIdEntry(SummaryID);                        // <=
    break;                                                     // <=
  default:
    return Error(Lex.getLoc(), "unexpected summary kind");
  }
  Lex.setIgnoreColonInIdentifiers(false);                      // <=
  return false;
}

PVS-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 بٹ غیر دستخط شدہ قسم ہے۔ یہ پتہ چلتا ہے کہ 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-Studio وارننگ: V1028 [CWE-190] ممکنہ اوور فلو۔ 'NumElts * Scale' آپریٹر کے 'size_t' قسم کے کام کرنے پر غور کریں، نتیجہ نہیں۔ X86ISelLowering.h 1577

قسم کے متغیرات کو ضرب کرتے وقت اوور فلو سے بچنے کے لیے واضح قسم کاسٹنگ کا استعمال کیا جاتا ہے۔ int. تاہم، یہاں واضح قسم کاسٹنگ اوور فلو سے تحفظ نہیں دیتی۔ سب سے پہلے، متغیرات کو ضرب دیا جائے گا، اور صرف اس کے بعد ضرب کے 32 بٹ نتیجہ کو قسم تک بڑھایا جائے گا سائز_ٹ.

فریگمنٹ N35: ناکام کاپی پیسٹ

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

V778 [CWE-682] دو ملتے جلتے کوڈ کے ٹکڑے ملے۔ شاید، یہ ایک ٹائپو ہے اور 'Op1' کی بجائے 'Op0' متغیر استعمال کیا جانا چاہیے۔ InstCombineCompares.cpp 5507

یہ نئی دلچسپ تشخیص ان حالات کی نشاندہی کرتی ہے جہاں کوڈ کا ایک ٹکڑا کاپی کیا گیا ہے اور اس میں کچھ نام تبدیل کیے جانے لگے ہیں، لیکن ایک جگہ انہوں نے اسے درست نہیں کیا ہے۔

براہ کرم نوٹ کریں کہ دوسرے بلاک میں وہ بدل گئے ہیں۔ Op0 پر Op1. لیکن ایک جگہ انہوں نے اسے ٹھیک نہیں کیا۔ غالباً اسے اس طرح لکھا جانا چاہیے تھا:

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

ٹکڑا N36: متغیر کنفیوژن

struct Status {
  unsigned Mask;
  unsigned Mode;

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

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

PVS-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

متغیر 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-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۔ ڈرائیور سی پی پی 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 :: ویکٹر > آپ صرف لکھ نہیں سکتے xxx.push_back(نیا X)، چونکہ یہاں سے کوئی مضمر تبدیلی نہیں ہے۔ X* в std::unique_ptr.

ایک مشترکہ حل لکھنا ہے۔ xxx.emplace_back(نیا X)چونکہ یہ مرتب کرتا ہے: طریقہ emplace_back ایک عنصر کو براہ راست اس کے دلائل سے بناتا ہے اور اس وجہ سے واضح کنسٹرکٹرز استعمال کرسکتا ہے۔

یہ محفوظ نہیں ہے۔ اگر ویکٹر بھرا ہوا ہے، تو میموری کو دوبارہ مختص کیا جاتا ہے۔ میموری ری لوکیشن آپریشن ناکام ہو سکتا ہے، جس کے نتیجے میں ایک استثناء پھینک دیا جائے گا۔ std::bad_alloc. اس صورت میں، پوائنٹر کھو جائے گا اور تخلیق شدہ آبجیکٹ کو کبھی حذف نہیں کیا جائے گا۔

ایک محفوظ حل تخلیق کرنا ہے۔ منفرد_ptrویکٹر میموری کو دوبارہ مختص کرنے کی کوشش کرنے سے پہلے جو پوائنٹر کا مالک ہوگا:

xxx.push_back(std::unique_ptr<X>(new X))

C++14 کے بعد سے، آپ 'std::make_unique' استعمال کر سکتے ہیں:

xxx.push_back(std::make_unique<X>())

اس قسم کی خرابی LLVM کے لیے اہم نہیں ہے۔ اگر میموری کو مختص نہیں کیا جاسکتا ہے تو، کمپائلر صرف بند ہو جائے گا. تاہم، طویل کے ساتھ ایپلی کیشنز کے لئے اپ ٹائم، جو صرف میموری مختص کرنے میں ناکام ہونے کی صورت میں ختم نہیں ہوسکتا ہے ، یہ ایک حقیقی گندا بگ ہوسکتا ہے۔

لہذا، اگرچہ یہ کوڈ LLVM کے لیے کوئی عملی خطرہ نہیں بناتا، لیکن میں نے اس غلطی کے پیٹرن کے بارے میں بات کرنا مفید پایا اور یہ کہ PVS-Studio تجزیہ کار نے اسے پہچاننا سیکھ لیا ہے۔

اس قسم کے دیگر انتباہات:

  • V1023 [CWE-460] ایک پوائنٹر بغیر مالک کے 'Passes' کنٹینر میں 'emplace_back' طریقہ سے شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ PassManager.h 546
  • V1023 [CWE-460] بغیر مالک کے ایک پوائنٹر کو 'ایمپلیس_بیک' طریقہ سے 'AAs' کنٹینر میں شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ عرفی تجزیہ.h 324
  • V1023 [CWE-460] ایک پوائنٹر بغیر مالک کے 'انٹریز' کنٹینر میں 'emplace_back' طریقہ سے شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] ایک پوائنٹر بغیر مالک کے 'AllEdges' کنٹینر میں 'emplace_back' طریقہ سے شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ CFGMST.h 268
  • V1023 [CWE-460] 'emplace_back' طریقہ سے 'VMaps' کنٹینر میں مالک کے بغیر ایک پوائنٹر شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] ایک پوائنٹر بغیر مالک کے 'ریکارڈز' کنٹینر میں 'emplace_back' طریقہ سے شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ FDRLogBuilder.h 30
  • V1023 [CWE-460] 'emplace_back' طریقہ سے 'PendingSubmodules' کنٹینر میں مالک کے بغیر ایک پوائنٹر شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ ModuleMap.cpp 810
  • V1023 [CWE-460] ایک پوائنٹر بغیر مالک کے 'آبجیکٹس' کنٹینر میں 'emplace_back' طریقہ سے شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ DebugMap.cpp 88
  • V1023 [CWE-460] 'emplace_back' طریقہ سے 'اسٹریٹیجیز' کنٹینر میں مالک کے بغیر ایک پوائنٹر شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] ایک پوائنٹر بغیر مالک کے '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] ایک پوائنٹر بغیر مالک کے 'اوپرینڈز' کنٹینر میں 'emplace_back' طریقہ سے شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] 'emplace_back' طریقہ سے 'Stash' کنٹینر میں مالک کے بغیر ایک پوائنٹر شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] ایک پوائنٹر بغیر مالک کے 'ایمپلیس_بیک' طریقہ سے 'میچرز' کنٹینر میں شامل کیا جاتا ہے۔ ایک استثناء کی صورت میں میموری لیک ہو جائے گی۔ GlobalISelEmitter.cpp 2702

حاصل يہ ہوا

میں نے مجموعی طور پر 60 انتباہات جاری کیے اور پھر روک دیا۔ کیا PVS-Studio تجزیہ کار LLVM میں دیگر نقائص کا پتہ لگاتا ہے؟ ہاں میرے پاس ہے. تاہم، جب میں مضمون کے لیے کوڈ کے ٹکڑے لکھ رہا تھا، شام کا وقت تھا، یا اس کے بجائے رات، اور میں نے فیصلہ کیا کہ اب وقت آگیا ہے کہ اسے ایک دن کہا جائے۔

مجھے امید ہے کہ آپ کو یہ دلچسپ لگا اور آپ PVS-Studio تجزیہ کار کو آزمانا چاہیں گے۔

آپ تجزیہ کار ڈاؤن لوڈ کر سکتے ہیں اور مائن سویپر کی کلید حاصل کر سکتے ہیں۔ اس صفحہ پر.

سب سے اہم بات یہ ہے کہ جامد تجزیہ کو باقاعدگی سے استعمال کریں۔ ایک بار چیک، ہمارے ذریعہ جامد تجزیہ کے طریقہ کار کو مقبول بنانے کے لئے انجام دیا گیا ہے اور PVS-Studio کوئی عام منظرنامہ نہیں ہے۔

آپ کے کوڈ کے معیار اور وشوسنییتا کو بہتر بنانے میں اچھی قسمت!

PVS-Studio تجزیہ کار کا استعمال کرتے ہوئے LLVM 8 میں کیڑے تلاش کرنا

اگر آپ انگریزی بولنے والے سامعین کے ساتھ اس مضمون کا اشتراک کرنا چاہتے ہیں، تو براہ کرم ترجمہ کا لنک استعمال کریں: Andrey Karpov۔ PVS-Studio کے ساتھ LLVM 8 میں کیڑے تلاش کرنا.

ماخذ: www.habr.com

نیا تبصرہ شامل کریں