PVS-Studio analizatori yordamida LLVM 8 da xatolarni topish

PVS-Studio analizatori yordamida LLVM 8 da xatolarni topish
PVS-Studio analizatorimiz yordamida LLVM loyihasining oxirgi kod tekshiruvidan ikki yildan ko'proq vaqt o'tdi. Keling, PVS-Studio analizatori hali ham xatolar va mumkin bo'lgan zaifliklarni aniqlash uchun etakchi vosita ekanligiga ishonch hosil qilaylik. Buning uchun biz LLVM 8.0.0 versiyasida yangi xatolarni tekshiramiz va topamiz.

Maqola yozilishi kerak

Rostini aytsam, bu maqolani yozishni xohlamadim. Biz bir necha bor tekshirgan loyiha haqida yozish qiziq emas (1, 2, 3). Yangi narsa haqida yozish yaxshiroq, lekin menda boshqa tanlov yo'q.

Har safar LLVM ning yangi versiyasi chiqarilganda yoki yangilanganda Clang statik analizatori, biz pochtamizga quyidagi turdagi savollarni olamiz:

Qarang, Clang Static Analyzer-ning yangi versiyasi yangi xatolarni topishni o'rgandi! Menimcha, PVS-Studio-dan foydalanishning dolzarbligi pasaymoqda. Clang avvalgidan ko'ra ko'proq xatolarni topadi va PVS-Studio imkoniyatlariga ega bo'ladi. Bu haqda qanday fikrdasiz?

Bunga men har doim shunday javob berishni xohlayman:

Biz ham bekor o‘tirmaymiz! Biz PVS-Studio analizatorining imkoniyatlarini sezilarli darajada yaxshiladik. Shuning uchun xavotir olmang, biz avvalgidek yetakchilik qilishda davom etamiz.

Afsuski, bu yomon javob. Unda hech qanday dalil yo'q. Va shuning uchun men hozir ushbu maqolani yozyapman. Shunday qilib, LLVM loyihasi yana bir bor tekshirildi va unda turli xil xatolar topildi. Endi men uchun qiziqarli bo'lgan narsalarni namoyish qilaman. Clang Static Analyzer bu xatolarni topa olmaydi (yoki uning yordami bilan buni qilish juda noqulay). Lekin biz qila olamiz. Qolaversa, men bu xatolarni bir oqshomda topib yozdim.

Ammo maqolani yozish bir necha hafta davom etdi. Men bularning barchasini matnga kiritishga o'zimni topa olmadim :).

Aytgancha, agar siz xatolar va mumkin bo'lgan zaifliklarni aniqlash uchun PVS-Studio analizatorida qanday texnologiyalar qo'llanilishi bilan qiziqsangiz, men bu bilan tanishishni taklif qilaman. Eslatma.

Yangi va eski diagnostika

Yuqorida aytib o'tilganidek, taxminan ikki yil oldin LLVM loyihasi yana bir bor tekshirildi va topilgan xatolar tuzatildi. Endi ushbu maqola xatolarning yangi to'plamini taqdim etadi. Nima uchun yangi xatolar topildi? Buning 3 ta sababi bor:

  1. LLVM loyihasi rivojlanmoqda, eski kodni o'zgartirmoqda va yangi kod qo'shmoqda. Tabiiyki, o'zgartirilgan va yozilgan kodda yangi xatolar mavjud. Bu statik tahlilni vaqti-vaqti bilan emas, muntazam ravishda qo'llash kerakligini aniq ko'rsatib turibdi. Bizning maqolalarimiz PVS-Studio analizatorining imkoniyatlarini yaxshi ko'rsatib beradi, ammo bu kod sifatini yaxshilash va xatolarni tuzatish xarajatlarini kamaytirish bilan hech qanday aloqasi yo'q. Doimiy ravishda statik kod analizatoridan foydalaning!
  2. Biz mavjud diagnostikani yakunlaymiz va takomillashtirmoqdamiz. Shu sababli, analizator oldingi skanerlash paytida sezmagan xatolarni aniqlay oladi.
  3. PVS-Studio-da 2 yil oldin mavjud bo'lmagan yangi diagnostika paydo bo'ldi. PVS-Studio ning rivojlanishini aniq ko'rsatish uchun ularni alohida bo'limda ta'kidlashga qaror qildim.

2 yil oldin mavjud bo'lgan diagnostika bilan aniqlangan nuqsonlar

Fragment N1: Nusxalash va joylashtirish

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 ogohlantirishi: V501 [CWE-570] '||' chap va o'ng tomonida bir xil 'Name.startswith("avx512.mask.permvar.")' pastki ifodalari mavjud. operator. AutoUpgrade.cpp 73

Nomning "avx512.mask.permvar." pastki qatori bilan boshlanishi ikki marta tekshiriladi. Ikkinchi tekshiruvda ular boshqa narsa yozmoqchi bo'lishdi, lekin ko'chirilgan matnni tuzatishni unutishdi.

Fragment N2: xato

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;
  ....
}

Ogohlantirish PVS-Studio: V501 "|" ning chap va o'ng tomonida bir xil "CXNameRange_WantQualifier" pastki ifodalari mavjud. operator. Cindex.cpp 7245

Matn xatosi tufayli bir xil nomdagi doimiy ikki marta ishlatiladi CXNameRange_WantQualifier.

Fragment N3: Operator ustunligi bilan chalkashlik

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

PVS-Studio ogohlantirishi: V502 [CWE-783] Balki "?:" operatori kutilganidan boshqacha ishlaydi. '?:' operatori '==' operatoriga qaraganda pastroq ustuvorlikka ega. PPCTargetTransformInfo.cpp 404

Menimcha, bu juda chiroyli xato. Ha, menda go'zallik haqida g'alati fikrlar borligini bilaman :).

Endi, shunga ko'ra operator ustuvorliklari, ifoda quyidagicha baholanadi:

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

Amaliy nuqtai nazardan, bunday holat mantiqiy emas, chunki uni quyidagilarga qisqartirish mumkin:

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

Bu aniq xato. Ehtimol, ular 0/1 ni o'zgaruvchi bilan solishtirishni xohlashdi indeks. Kodni tuzatish uchun uchlik operator atrofida qavslar qo'shishingiz kerak:

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

Aytgancha, uchlik operatori juda xavfli va mantiqiy xatolarni keltirib chiqaradi. U bilan juda ehtiyot bo'ling va qavslar bilan ochko'zlik qilmang. Men ushbu mavzuni batafsil ko'rib chiqdim shu yerda, bobida "Ehtiyot bo'ling?: Operator va uni qavs ichiga kiriting."

Fragment N4, N5: Null ko'rsatgich

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 ogohlantirishi: V522 [CWE-476] "LHS" null ko'rsatgichga havolalar bekor qilinishi mumkin. TGParser.cpp 2152

Agar ko'rsatgich LHS null bo'lsa, ogohlantirish berilishi kerak. Biroq, buning o'rniga, xuddi shu null ko'rsatgichga murojaat qilinadi: LHS->getAsString().

Bu xato ishlov beruvchida xato yashiringan juda odatiy holat, chunki hech kim ularni sinab ko'rmaydi. Statik analizatorlar qanchalik tez-tez ishlatilishidan qat'i nazar, barcha kirish mumkin bo'lgan kodlarni tekshiradi. Bu statik tahlil boshqa sinov va xatolardan himoya qilish usullarini qanday to'ldirishiga juda yaxshi misoldir.

Xuddi shunday ko'rsatgich bilan ishlash xatosi rhs Quyidagi kodda ruxsat berilgan: V522 [CWE-476] "RHS" null ko'rsatgichining referensiyasi bekor qilinishi mumkin. TGParser.cpp 2186

Fragment N6: Ko'chirilgandan keyin ko'rsatgichdan foydalanish

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 Ogohlantirish: V522 [CWE-476] 'ProgClone' null ko'rsatgichga havolalar bekor qilinishi mumkin. Miscompilation.cpp 601

Boshida aqlli ko'rsatgich ProgClone ob'ektga egalik qilishni to'xtatadi:

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

Aslida, hozir ProgClone null ko'rsatkich hisoblanadi. Shuning uchun, null ko'rsatgichga murojaat qilish quyida paydo bo'lishi kerak:

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

Ammo, aslida, bu sodir bo'lmaydi! E'tibor bering, tsikl aslida bajarilmaydi.

Idishning boshida Noto'g'ri tuzilgan funksiyalar tozalangan:

MiscompiledFunctions.clear();

Keyinchalik, ushbu idishning o'lchami pastadir holatida ishlatiladi:

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

Loop boshlanmayotganini ko'rish oson. Menimcha, bu ham xato va kod boshqacha yozilishi kerak.

Biz o'sha mashhur xatolar paritetiga duch kelganga o'xshaymiz! Bir xato boshqasini yashiradi :).

Fragment N7: Ko'chirilgandan keyin ko'rsatgichdan foydalanish

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 ogohlantirishi: V522 [CWE-476] "Test" null ko'rsatgichga havolalar bekor qilinishi mumkin. Noto'g'ri kompilyatsiya.cpp 709

Yana bir xil holat. Dastlab, ob'ektning mazmuni ko'chiriladi, keyin esa hech narsa bo'lmagandek ishlatiladi. Men bu holatni C++ da harakat semantikasi paydo bo'lgandan keyin dastur kodida tez-tez ko'raman. Shuning uchun men C++ tilini yaxshi ko'raman! O'z oyog'ingizni otishning tobora ko'proq yangi usullari mavjud. PVS-Studio analizatori har doim ishlaydi :).

Fragment N8: Null ko'rsatgich

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 ogohlantirishi: V522 [CWE-476] “Type” null koʻrsatgichga havolalar bekor qilinishi mumkin. PrettyFunctionDumper.cpp 233

Xatolarni qayta ishlash vositalaridan tashqari, chop etishni tuzatish funktsiyalari odatda sinovdan o'tkazilmaydi. Oldimizda shunday holat bor. Funktsiya o'z muammolarini hal qilish o'rniga, uni tuzatishga majbur bo'ladigan foydalanuvchini kutmoqda.

To'g'ri:

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

Fragment N9: Null ko'rsatgich

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 ogohlantirishi: V522 [CWE-476] 'Ty' null ko'rsatgichga havolalar bekor qilinishi mumkin. SearchableTableEmitter.cpp 614

Menimcha, hamma narsa aniq va tushuntirishni talab qilmaydi.

Fragment N10: xato

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 ogohlantirishi: V570 'Identifier->Type' o'zgaruvchisi o'ziga tayinlangan. FormatTokenLexer.cpp 249

O'zgaruvchini o'ziga belgilashning ma'nosi yo'q. Ehtimol, ular yozishni xohlashgan:

Identifier->Type = Question->Type;

Fragment N11: Shubhali tanaffus

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 ogohlantirishi: V622 [CWE-478] "Switch" bayonotini tekshirishni o'ylab ko'ring. Birinchi "hal" operatori etishmayotgan bo'lishi mumkin. SystemZAsmParser.cpp 652

Boshida juda shubhali operator bor tanaffus. Bu yerda boshqa narsa yozishni unutdingizmi?

Fragment N12: havolani bekor qilgandan so'ng ko'rsatgichni tekshirish

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 ogohlantirishi: V595 [CWE-476] "Callee" ko'rsatkichi nullptr bilan tekshirilishidan oldin ishlatilgan. Tekshirish qatorlari: 172, 174. AMDGPUInline.cpp 172

Ko'rsatkich Callee boshida funktsiya chaqirilganda referensiya bekor qilinadi olish TTI.

Va keyin bu ko'rsatgich tenglik uchun tekshirilishi kerakligi ma'lum bo'ldi nullptr:

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

Lekin juda kech...

Fragment N13 - N...: havolani bekor qilgandan keyin ko'rsatgichni tekshirish

Oldingi kod fragmentida muhokama qilingan vaziyat noyob emas. Bu erda paydo bo'ladi:

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 ogohlantirishi: V595 [CWE-476] "CalleeFn" ko'rsatkichi nullptr bilan tekshirilishidan oldin ishlatilgan. Tekshirish qatorlari: 1079, 1081. SimplifyLibCalls.cpp 1079

Va bu erda:

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 ogohlantirishi: V595 [CWE-476] "ND" ko'rsatkichi nullptr bilan tekshirilishidan oldin ishlatilgan. Tekshirish qatorlari: 532, 534. SemaTemplateInstantiateDecl.cpp 532

Va bu erda:

  • V595 [CWE-476] "U" ko'rsatkichi nullptr bilan tekshirilishidan oldin ishlatilgan. Tekshirish qatorlari: 404, 407. DWARFormValue.cpp 404
  • V595 [CWE-476] "ND" ko'rsatkichi nullptr bilan tekshirilishidan oldin ishlatilgan. Tekshirish qatorlari: 2149, 2151. SemaTemplateInstantiate.cpp 2149

Va keyin men V595 raqami bilan ogohlantirishlarni o'rganishga qiziqmay qoldim. Shuning uchun bu erda sanab o'tilganlardan tashqari shunga o'xshash xatolar bor yoki yo'qligini bilmayman. Katta ehtimol bilan bor.

Fragment N17, N18: Shubhali siljish

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

PVS-Studio ogohlantirishi: V629 [CWE-190] '~(Hajmi - 1) << 1' ifodasini tekshirishni o'ylab ko'ring. 32-bit qiymatining bit o'zgarishi, keyinchalik 64-bitli turga kengayishi. AArch64AddressingModes.h 260

Bu xato bo'lmasligi mumkin va kod aynan mo'ljallanganidek ishlaydi. Ammo bu juda shubhali joy va uni tekshirish kerak.

O'zgaruvchini aytaylik O'lcham 16 ga teng, keyin kod muallifi uni o'zgaruvchida olishni rejalashtirgan NImms qiymat:

1111111111111111111111111111111111111111111111111111111111100000

Biroq, aslida natija quyidagicha bo'ladi:

0000000000000000000000000000000011111111111111111111111111100000

Gap shundaki, barcha hisob-kitoblar 32-bitli imzosiz tur yordamida amalga oshiriladi. Va shundan keyingina, bu 32-bitli imzosiz tur bilvosita kengaytiriladi uint64_t. Bunday holda, eng muhim bitlar nolga teng bo'ladi.

Siz vaziyatni quyidagicha tuzatishingiz mumkin:

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

Shunga o'xshash vaziyat: V629 [CWE-190] "Immr << 6" ifodasini tekshirishni o'ylab ko'ring. 32-bit qiymatining bit o'zgarishi, keyinchalik 64-bitli turga kengayishi. AArch64AddressingModes.h 269

Fragment N19: Kalit so'z etishmayapti yana boshqa?

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 ogohlantirishi: V646 [CWE-670] Ilova mantig'ini tekshirishni o'ylab ko'ring. "alse" kalit so'zi etishmayotgan bo'lishi mumkin. AMDGPUAsmParser.cpp 5655

Bu erda xatolik yo'q. O'sha paytdan boshlab birinchi blok if bilan tugaydi davom, keyin bu muhim emas, kalit so'z bor yana boshqa yoki yo'q. Qanday bo'lmasin, kod bir xil ishlaydi. Hali ham sog'indim yana boshqa kodni yanada tushunarsiz va xavfli qiladi. Agar kelajakda davom yo'qoladi, kod butunlay boshqacha ishlay boshlaydi. Menimcha, qo'shish yaxshidir yana boshqa.

Fragment N20: Bir xil turdagi to'rtta matn terish xatosi

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 ogohlantirishlari:

  • V655 [CWE-480] Satrlar birlashtirildi, lekin ishlatilmaydi. 'Result + Name.str()' ifodasini tekshirishni o'ylab ko'ring. Symbol.cpp 32
  • V655 [CWE-480] Satrlar birlashtirildi, lekin ishlatilmaydi. "Natija + "(ObjC Class)" + Name.str()' ifodasini tekshirishni o'ylab ko'ring. Symbol.cpp 35
  • V655 [CWE-480] Satrlar birlashtirildi, lekin ishlatilmaydi. "Natija + "(ObjC Class EH) " + Name.str()' ifodasini tekshirishni o'ylab ko'ring. Symbol.cpp 38
  • V655 [CWE-480] Satrlar birlashtirildi, lekin ishlatilmaydi. "Natija + "(ObjC IVar) " + Name.str()' ifodasini tekshirishni o'ylab ko'ring. Symbol.cpp 41

Tasodifan += operatori o'rniga + operatori ishlatiladi. Natijada ma'nosiz dizaynlar paydo bo'ladi.

Fragment N21: Belgilanmagan xatti-harakatlar

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();
    }
  }
}

Xavfli kodni o'zingiz topishga harakat qiling. Va bu darhol javobga qaramaslik uchun e'tiborni chalg'itadigan rasm:

PVS-Studio analizatori yordamida LLVM 8 da xatolarni topish

PVS-Studio ogohlantirishi: V708 [CWE-758] Xavfli qurilishdan foydalaniladi: 'FeaturesMap[Op] = FeaturesMap.size()', bu erda 'FeaturesMap' 'map' sinfiga tegishli. Bu aniqlanmagan xatti-harakatlarga olib kelishi mumkin. RISCVCompressInstEmitter.cpp 490

Muammo qatori:

FeaturesMap[Op] = FeaturesMap.size();

Agar element Op topilmasa, xaritada yangi element yaratiladi va u yerda ushbu xaritadagi elementlar soni yoziladi. Funktsiya chaqiriladimi yoki yo'qmi noma'lum hajmi yangi element qo'shishdan oldin yoki keyin.

Fragment N22-N24: Takroriy topshiriqlar

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 ogohlantirishi: V519 [CWE-563] "NType" o'zgaruvchisiga ketma-ket ikki marta qiymatlar beriladi. Balki bu xatodir. Tekshirish qatorlari: 1663, 1664. MachOObjectFile.cpp 1664

Menimcha, bu yerda haqiqiy xatolik yo‘q. Faqat keraksiz takroriy topshiriq. Ammo baribir qo'pol xato.

Xuddi shunday:

  • V519 [CWE-563] "B.NDesc" o'zgaruvchisiga ketma-ket ikki marta qiymatlar beriladi. Balki bu xatodir. Tekshirish satrlari: 1488, 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] O'zgaruvchiga ketma-ket ikki marta qiymatlar beriladi. Balki bu xatodir. Tekshirish qatorlari: 59, 61. coff2yaml.cpp 61

Fragment N25-N27: Ko'proq qayta tayinlash

Endi qayta tayinlashning biroz boshqacha versiyasini ko'rib chiqaylik.

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 ogohlantirishi: V519 [CWE-563] "Alignment" o'zgaruvchisiga ketma-ket ikki marta qiymatlar beriladi. Balki bu xatodir. Tekshirish qatorlari: 1158, 1160. LoadStoreVectorizer.cpp 1160

Bu juda g'alati kod bo'lib, unda mantiqiy xatolik bor. Boshida, o'zgaruvchan Alignment shartga qarab qiymat belgilanadi. Va keyin topshiriq yana sodir bo'ladi, lekin hozir hech qanday tekshiruvsiz.

Shunga o'xshash vaziyatlarni bu erda ko'rish mumkin:

  • V519 [CWE-563] "Effektlar" o'zgaruvchisiga ketma-ket ikki marta qiymatlar beriladi. Balki bu xatodir. Tekshirish qatorlari: 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] "ExpectNoDerefChunk" o'zgaruvchisiga ketma-ket ikki marta qiymatlar beriladi. Balki bu xatodir. Tekshirish qatorlari: 4970, 4973. SemaType.cpp 4973

Fragment N28: Har doim haqiqiy holat

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 ogohlantirishi: V547 [CWE-571] 'nextByte != 0x90' ifodasi har doim to'g'ri. X86DisassemblerDecoder.cpp 379

Tekshirish mantiqiy emas. Oʻzgaruvchan keyingi bayt har doim qiymatga teng emas 0x90, bu avvalgi tekshiruvdan kelib chiqadi. Bu qandaydir mantiqiy xato.

Fragment N29 - N...: Har doim to'g'ri/noto'g'ri shartlar

Analizator butun holat (V547) yoki uning bir qismi (V560) har doim to'g'ri yoki noto'g'ri. Ko'pincha bu haqiqiy xatolar emas, balki oddiy kod, makro kengayish natijasi va boshqalar. Biroq, bu barcha ogohlantirishlarni ko'rib chiqish mantiqan to'g'ri keladi, chunki vaqti-vaqti bilan haqiqiy mantiqiy xatolar yuzaga keladi. Masalan, kodning ushbu bo'limi shubhali:

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 ogohlantirishi: V560 [CWE-570] Shartli ifodaning bir qismi har doim noto'g'ri: RegNo == 0xe. ARMDisassembler.cpp 939

Doimiy 0xE o'nlikdagi 14 qiymatidir. Imtihon RegNo == 0xe mantiqiy emas, chunki agar RegNo > 13, keyin funktsiya o'z bajarilishini yakunlaydi.

V547 va V560 identifikatorlari bilan ko'plab boshqa ogohlantirishlar bor edi, lekin xuddi shunday V595, Men bu ogohlantirishlarni o'rganishga qiziqmadim. Maqola yozish uchun yetarli materialim borligi allaqachon aniq edi :). Shuning uchun, PVS-Studio yordamida LLVM-da qancha turdagi xatolarni aniqlash mumkinligi noma'lum.

Men sizga bu triggerlarni o'rganish nega zerikarli ekanligiga misol keltiraman. Analizator quyidagi kod uchun ogohlantirish berishda mutlaqo haqlidir. Lekin bu xato emas.

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

PVS-Studio Ogohlantirish: V547 [CWE-570] '!HasError' ifodasi har doim noto'g'ri. UnwrappedLineParser.cpp 1635

Fragment N30: ​​shubhali qaytish

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 ogohlantirishi: V612 [CWE-670] Loop ichidagi shartsiz "qaytish". R600OptimizeVectorRegisters.cpp 63

Bu xato yoki kodni o'qiyotgan dasturchilarga biror narsani tushuntirishga mo'ljallangan maxsus texnikadir. Ushbu dizayn menga hech narsani tushuntirmaydi va juda shubhali ko'rinadi. Bunday yozmaganingiz ma'qul :).

Charchadimi? Keyin choy yoki qahva tayyorlash vaqti keldi.

PVS-Studio analizatori yordamida LLVM 8 da xatolarni topish

Yangi diagnostika bilan aniqlangan nuqsonlar

Menimcha, eski diagnostikaning 30 ta faollashuvi etarli. Keling, keyin analizatorda paydo bo'lgan yangi diagnostika bilan qanday qiziqarli narsalarni topish mumkinligini ko'rib chiqaylik oldingi tekshiruvlar. Bu vaqt ichida C++ analizatoriga jami 66 ta umumiy maqsadli diagnostika qo‘shildi.

Fragment N31: Urib bo'lmaydigan kod

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 ogohlantirishi: V779 [CWE-561] Ulanib bo'lmaydigan kod aniqlandi. Ehtimol, xatolik mavjud. ExecutionUtils.cpp 146

Ko'rib turganingizdek, operatorning ikkala filiali if operatorga qo'ng'iroq qilish bilan tugaydi Qaytish. Shunga ko'ra, konteyner CtorDtorsByPriority hech qachon tozalanmaydi.

Fragment N32: Urib bo'lmaydigan kod

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 ogohlantirishi: V779 [CWE-561] Ulanib bo'lmaydigan kod aniqlandi. Ehtimol, xatolik mavjud. LLparser.cpp 835

Qiziqarli holat. Keling, avval bu joyni ko'rib chiqaylik:

return ParseTypeIdEntry(SummaryID);
break;

Bir qarashda, bu erda xatolik yo'qdek tuyuladi. Bu operatorga o'xshaydi tanaffus Bu yerda qo'shimchasi bor va siz uni shunchaki o'chirib tashlashingiz mumkin. Biroq, hammasi ham oddiy emas.

Analizator satrlarda ogohlantirish beradi:

Lex.setIgnoreColonInIdentifiers(false);
return false;

Va haqiqatan ham, bu kodga etib bo'lmaydi. Barcha holatlar Switch operator qo'ng'irog'i bilan tugaydi Qaytish. Va endi yolg'iz ma'nosiz tanaffus unchalik zararsiz ko'rinmaydi! Ehtimol, filiallardan biri bilan tugashi kerak tanaffus, yoqilmagan Qaytish?

Fragment N33: Yuqori bitlarni tasodifiy tiklash

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 ogohlantirishi: V784 Bit niqobining o'lchami birinchi operandning o'lchamidan kamroq. Bu yuqori bitlarning yo'qolishiga olib keladi. RuntimeDyld.cpp 815

Funktsiyaga e'tibor bering getStubAlignment qaytaradi turi imzosiz. Funktsiya 8 qiymatini qaytaradi deb faraz qilib, ifoda qiymatini hisoblaymiz:

~(getStubAlignment() - 1)

~(8u-1)

0xFFFFFFFF8u

Endi o'zgaruvchiga e'tibor bering DataSize 64 bitli imzosiz turiga ega. Ma'lum bo'lishicha, DataSize & 0xFFFFFFFF8u operatsiyasini bajarishda barcha o'ttiz ikkita yuqori tartibli bitlar nolga qaytariladi. Ehtimol, bu dasturchi xohlagan narsa emas. Men u hisoblashni xohlaganidan shubhalanaman: DataSize & 0xFFFFFFFFFFFFFF8u.

Xatoni tuzatish uchun siz quyidagilarni yozishingiz kerak:

DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);

Yoki shunday:

DataSize &= ~(getStubAlignment() - 1ULL);

Fragment N34: aniq turdagi translatsiya muvaffaqiyatsiz tugadi

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 ogohlantirishi: V1028 [CWE-190] Toshib ketishi mumkin. "NumElts * Scale" operatorining operandlarini natijani emas, balki "size_t" turiga o'tkazishni ko'rib chiqing. X86ISelLowering.h 1577

Turi o'zgaruvchilarni ko'paytirishda to'lib ketishining oldini olish uchun aniq turdagi quyma qo'llaniladi int. Biroq, bu erda aniq turdagi quyma to'lib ketishdan himoya qilmaydi. Birinchidan, o'zgaruvchilar ko'paytiriladi va shundan keyingina ko'paytirishning 32-bitli natijasi turga kengaytiriladi. hajmi_t.

Fragment N35: Nusxalash va joylashtirish muvaffaqiyatsiz tugadi

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] Ikki o'xshash kod bo'lagi topildi. Ehtimol, bu matn terish xatosi va "Op1" o'rniga "Op0" o'zgaruvchisidan foydalanish kerak. InstCombineCompares.cpp 5507

Ushbu yangi qiziqarli diagnostika kod parchasi ko'chirilgan va undagi ba'zi nomlar o'zgartirila boshlagan, biroq bir joyda ular tuzatmagan holatlarni aniqlaydi.

E'tibor bering, ikkinchi blokda ular o'zgargan Op0 haqida Op1. Ammo bir joyda ular buni tuzatmadilar. Katta ehtimol bilan shunday yozilishi kerak edi:

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

Fragment N36: o'zgaruvchan chalkashlik

struct Status {
  unsigned Mask;
  unsigned Mode;

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

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

PVS-Studio ogohlantirishi: V1001 [CWE-563] "Rejim" o'zgaruvchisi tayinlangan, lekin funksiya oxirida foydalanilmaydi. SIModeRegister.cpp 48

Funktsiya argumentlariga sinf a'zolari bilan bir xil nom berish juda xavflidir. Buni chalkashtirib yuborish juda oson. Oldimizda shunday holat bor. Bu ibora mantiqiy emas:

Mode &= Mask;

Funktsiya argumenti o'zgaradi. Va tamom. Bu dalil endi ishlatilmaydi. Katta ehtimol bilan siz buni shunday yozishingiz kerak edi:

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

Fragment N37: o'zgaruvchan chalkashlik

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;
}

Ogohlantirish PVS-Studio: V1001 [CWE-563] "Hajm" o'zgaruvchisi tayinlangan, lekin funksiya oxirida foydalanilmaydi. Object.cpp 424

Vaziyat avvalgisiga o'xshaydi. Bu shunday yozilishi kerak:

this->Size += this->EntrySize;

Fragment N38-N47: Ular indeksni tekshirishni unutishdi

Ilgari biz diagnostik tetiklash misollarini ko'rib chiqdik V595. Uning mohiyati shundan iboratki, ko'rsatgich boshida yo'qoladi va shundan keyingina tekshiriladi. Yosh diagnostika V1004 ma’nosiga qarama-qarshi bo‘lib, ko‘p xatolarni ham ochib beradi. U ko'rsatgich boshida tekshirilgan va keyin buni unutgan holatlarni aniqlaydi. Keling, LLVM ichida topilgan bunday holatlarni ko'rib chiqaylik.

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 ogohlantirishi: V1004 [CWE-476] “Ptr” koʻrsatkichi nullptr bilan tekshirilgandan soʻng ishonchsiz ishlatilgan. Tekshirish qatorlari: 729, 738. TargetTransformInfoImpl.h 738

Argumentlar Ptr teng bo'lishi mumkin nullptr, chek bilan tasdiqlangan:

if (Ptr != nullptr)

Biroq, quyida ushbu ko'rsatkich oldindan tekshirilmasdan o'chiriladi:

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

Keling, shunga o'xshash yana bir ishni ko'rib chiqaylik.

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 ogohlantirishi: V1004 [CWE-476] “FD” koʻrsatkichi nullptr bilan tekshirilgandan soʻng ishonchsiz ishlatilgan. Tekshirish qatorlari: 3228, 3231. CGDebugInfo.cpp 3231

Belgiga e'tibor bering FD. Ishonchim komilki, muammo aniq ko'rinadi va hech qanday maxsus tushuntirish talab qilinmaydi.

Va yana:

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 ogohlantirishi: V1004 [CWE-476] "PtrTy" ko'rsatgichi nullptr bilan tekshirilgandan so'ng ishonchsiz ishlatilgan. Tekshirish qatorlari: 960, 965. InterleavedLoadCombinePass.cpp 965

O'zingizni bunday xatolardan qanday himoya qilish kerak? Code-Review-da ehtiyot bo'ling va kodingizni muntazam tekshirish uchun PVS-Studio statik analizatoridan foydalaning.

Ushbu turdagi xatolarga ega bo'lgan boshqa kod qismlarini keltirishning ma'nosi yo'q. Men maqolada faqat ogohlantirishlar ro'yxatini qoldiraman:

  • V1004 [CWE-476] "Expr" ko'rsatkichi nullptr bilan tasdiqlanganidan so'ng ishonchsiz ishlatilgan. Tekshirish qatorlari: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] "PI" ko'rsatkichi nullptrga qarshi tekshirilgandan so'ng ishonchsiz ishlatilgan. Tekshirish qatorlari: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] "StatepointCall" ko'rsatkichi nullptr bilan tekshirilgandan so'ng ishonchsiz ishlatilgan. Tekshirish satrlari: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] "RV" ko'rsatkichi nullptr bilan tekshirilgandan so'ng xavfsiz ishlatildi. Tekshirish qatorlari: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] "CalleeFn" ko'rsatkichi nullptr bilan tasdiqlanganidan so'ng ishonchsiz ishlatilgan. Tekshirish qatorlari: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] "TC" ko'rsatkichi nullptr bilan tekshirilgandan so'ng ishonchsiz ishlatilgan. Tekshirish chiziqlari: 1819, 1824. Driver.cpp 1824

Fragment N48-N60: Muhim emas, lekin nuqson (xotiraning oqishi mumkin)

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

PVS-Studio ogohlantirishi: V1023 [CWE-460] Egasi boʻlmagan koʻrsatgich “Strategiyalar” konteyneriga “emplace_back” usuli bilan qoʻshiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-isel-fuzzer.cpp 58

Konteynerning oxiriga element qo'shish kabi std :: vektor > shunchaki yoza olmaysiz xxx.push_back(yangi X), chunki dan yashirin konvertatsiya yo'q X* в std::unique_ptr.

Umumiy yechim - yozish xxx.emplace_back(yangi X)chunki u kompilyatsiya qiladi: usul emplace_back elementni to'g'ridan-to'g'ri argumentlardan tuzadi va shuning uchun aniq konstruktorlardan foydalanishi mumkin.

Bu xavfsiz emas. Agar vektor to'lgan bo'lsa, xotira qayta taqsimlanadi. Xotirani qayta taqsimlash operatsiyasi muvaffaqiyatsiz bo'lishi mumkin, bu esa istisnoga olib keladi std :: bad_alloc. Bunday holda, ko'rsatgich yo'qoladi va yaratilgan ob'ekt hech qachon o'chirilmaydi.

Xavfsiz yechim yaratishdir noyob_ptrvektor xotirani qayta taqsimlashga urinishdan oldin ko'rsatgichga egalik qiladi:

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

C++ 14 dan boshlab siz 'std::make_unique' dan foydalanishingiz mumkin:

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

Ushbu turdagi nuqson LLVM uchun muhim emas. Xotirani ajratib bo'lmasa, kompilyator shunchaki to'xtaydi. Biroq, uzoq muddatli ilovalar uchun ish vaqti, agar xotira taqsimoti bajarilmasa, bu faqat tugatib bo'lmaydi, bu haqiqiy yomon xato bo'lishi mumkin.

Shunday qilib, ushbu kod LLVM uchun amaliy xavf tug'dirmasa ham, men ushbu xato namunasi va PVS-Studio analizatori uni aniqlashni o'rganganligi haqida gapirishni foydali deb topdim.

Ushbu turdagi boshqa ogohlantirishlar:

  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Passes" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. PassManager.h 546
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "AA" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. AliasAnalysis.h 324
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Entries" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "AllEdges" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. CFGMST.h 268
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "VMaps" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Yozuvlar" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. FDRLogBuilder.h 30
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "PendingSubmodules" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. ModuleMap.cpp 810
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Ob'ektlar" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. DebugMap.cpp 88
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Strategiyalar" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 685
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 686
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 688
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 689
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 690
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 691
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 692
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 693
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Modifiers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. llvm-stress.cpp 694
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Operandlar" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. GlobalISelEmitter.cpp 1911 yil
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Stash" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Egasi bo'lmagan ko'rsatgich "Matchers" konteyneriga "emplace_back" usuli bilan qo'shiladi. Istisno holatida xotiraning oqishi sodir bo'ladi. GlobalISelEmitter.cpp 2702

xulosa

Men jami 60 ta ogohlantirish berdim va keyin to'xtadim. LLVM da PVS-Studio analizatori aniqlaydigan boshqa nuqsonlar bormi? Ha bor. Biroq, men maqola uchun kod bo'laklarini yozayotganimda, kechqurun, to'g'rirog'i, tun bo'ldi va men buni kun deb atash vaqti keldi, deb qaror qildim.

Umid qilamanki, sizga qiziqarli bo'ldi va PVS-Studio analizatorini sinab ko'rishni xohlaysiz.

Siz analizatorni yuklab olishingiz va mina qidiruvi kalitini olishingiz mumkin Ushbu sahifa.

Eng muhimi, statik tahlilni muntazam ravishda ishlatish. Bir martalik tekshiruvlar, statik tahlil metodologiyasini ommalashtirish maqsadida biz tomonidan amalga oshirilgan va PVS-Studio oddiy stsenariy emas.

Kodingiz sifati va ishonchliligini oshirishda omad tilaymiz!

PVS-Studio analizatori yordamida LLVM 8 da xatolarni topish

Agar siz ushbu maqolani ingliz tilida so'zlashuvchi auditoriya bilan baham ko'rmoqchi bo'lsangiz, tarjima havolasidan foydalaning: Andrey Karpov. PVS-Studio yordamida LLVM 8 da xatolarni topish.

Manba: www.habr.com

a Izoh qo'shish