PVS-Studio анализаторы арқылы LLVM 8 қателерін табу

PVS-Studio анализаторы арқылы LLVM 8 қателерін табу
Біздің PVS-Studio анализаторымыз арқылы LLVM жобасының соңғы код тексерісінен бері екі жылдан астам уақыт өтті. PVS-Studio анализаторы әлі де қателер мен ықтимал осалдықтарды анықтаудың жетекші құралы екеніне көз жеткізейік. Ол үшін біз LLVM 8.0.0 шығарылымында жаңа қателерді тексереміз және табамыз.

Жазылатын мақала

Шынымды айтсам, бұл мақаланы жазғым келмеді. Біз бірнеше рет тексерген жоба туралы жазу қызық емес (1, 2, 3). Жаңа нәрсе туралы жазған дұрыс, бірақ менде басқа амал жоқ.

LLVM жаңа нұсқасы шығарылған немесе жаңартылған сайын Clang статикалық анализаторы, біз поштамызға келесі түрдегі сұрақтарды аламыз:

Қараңыз, Clang Static Analyzer бағдарламасының жаңа нұсқасы жаңа қателерді табуды үйренді! Менің ойымша, PVS-Studio қолданудың өзектілігі төмендеп бара жатқан сияқты. Clang бұрынғыға қарағанда көбірек қателерді тауып, PVS-Studio мүмкіндіктерін қуып жетеді. Бұл туралы не ойлайсыз?

Бұған мен әрқашан келесідей жауап бергім келеді:

Біз де бос отырмаймыз! Біз PVS-Studio анализаторының мүмкіндіктерін айтарлықтай жақсарттық. Сондықтан уайымдамаңыз, біз бұрынғыдай көшбасшылықты жалғастырамыз.

Өкінішке орай, бұл жаман жауап. Онда ешқандай дәлелдер жоқ. Сондықтан мен бұл мақаланы қазір жазып отырмын. Сонымен, LLVM жобасы тағы бір рет тексеріліп, одан әртүрлі қателер табылды. Енді маған қызықты көрінгендерін көрсетемін. Clang Static Analyzer бұл қателерді таба алмайды (немесе оның көмегімен мұны істеу өте ыңғайсыз). Бірақ біз аламыз. Оның үстіне бұл қателердің бәрін бір кеште тауып жазып алдым.

Бірақ мақаланы жазу бірнеше аптаға созылды. Мен мұның бәрін мәтінге енгізуге шыдай алмадым :).

Айтпақшы, егер сізді PVS-Studio анализаторында қателер мен ықтимал осалдықтарды анықтау үшін қолданылатын технологиялар қызықтырса, мен онымен танысуды ұсынамын. Ескерту.

Жаңа және ескі диагностика

Жоғарыда айтылғандай, шамамен екі жыл бұрын LLVM жобасы тағы бір рет тексеріліп, табылған қателер түзетілді. Енді бұл мақала қателердің жаңа топтамасын ұсынады. Неліктен жаңа қателер табылды? Мұның 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 [CWE-476] "LHS" нөлдік көрсеткішінің сілтемесі жойылуы мүмкін. TGParser.cpp 2152

Көрсеткіш болса LHS нөл болса, ескерту керек. Дегенмен, оның орнына дәл сол нөлдік көрсеткішке сілтеме жойылады: LHS->getAsString().

Бұл қате өңдегіште қате жасырылған өте әдеттегі жағдай, өйткені оларды ешкім тексермейді. Статикалық анализаторлар қаншалықты жиі пайдаланылғанына қарамастан, барлық қолжетімді кодты тексереді. Бұл статикалық талдаудың басқа сынақтар мен қателерден қорғау әдістерін қалай толықтыратынын көрсететін өте жақсы мысал.

Ұқсас көрсеткішті өңдеу қатесі ҚҚТ төмендегі кодта рұқсат етілген: V522 [CWE-476] 'RHS' нөлдік көрсеткішінің сілтемесі жойылуы мүмкін. TGParser.cpp 2186

N6 фрагмент: жылжытқаннан кейін көрсеткішті пайдалану

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

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

PVS-Studio Ескерту: V522 [CWE-476] "ProgClone" нөлдік көрсеткішінің сілтемесі жойылуы мүмкін. Қате құрастыру.cpp 601

Басында ақылды көрсеткіш ProgClone объектіні иеленуін тоқтатады:

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

Шын мәнінде, қазір ProgClone нөлдік көрсеткіш болып табылады. Сондықтан, нөлдік көрсеткішті жою төменде орын алуы керек:

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

Бірақ, шын мәнінде, бұл болмайды! Цикл іс жүзінде орындалмағанын ескеріңіз.

Контейнердің басында Қате құрастырылған функциялар тазартылды:

MiscompiledFunctions.clear();

Әрі қарай, бұл контейнердің өлшемі цикл жағдайында қолданылады:

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

Цикл басталмайтынын көру оңай. Менің ойымша, бұл да қате және кодты басқаша жазу керек.

Біз қателердің атақты паритетіне тап болған сияқтымыз! Бір қате екіншісін жасырады :).

N7 фрагмент: жылжытқаннан кейін көрсеткішті пайдалану

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

PVS-Studio ескертуі: V522 [CWE-476] «Тест» нөлдік көрсеткіштің сілтемесі жойылуы мүмкін. Қате құрастыру.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] "Түр" нөлдік көрсеткіштің сілтемесі жойылуы мүмкін. 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] "Ty" нөлдік көрсеткішінің сілтемесі жойылуы мүмкін. SearchableTableEmitter.cpp 614

Менің ойымша, бәрі түсінікті және түсіндіруді қажет етпейді.

N10 фрагмент: қате

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

PVS-Studio ескертуі: V570 "Идентификатор->Түр" айнымалысы өзіне тағайындалады. FormatTokenLexer.cpp 249

Өзіне айнымалыны тағайындаудың мағынасы жоқ. Мүмкін олар жазғысы келді:

Identifier->Type = Question->Type;

N11 фрагмент: күдікті үзіліс

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

PVS-Studio ескертуі: V622 [CWE-478] "Ауыстыру" мәлімдемесін тексеруді қарастырыңыз. Бірінші «жағдай» операторы жоқ болуы мүмкін. SystemZAsmParser.cpp 652

Басында өте күдікті оператор бар Үзіліс. Мұнда тағы бірдеңе жазуды ұмытып қалдыңыз ба?

N12 фрагменті: сілтемені алып тастағаннан кейін көрсеткішті тексеру

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

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

PVS-Studio ескертуі: V595 [CWE-476] "Қоңырау шалушы" көрсеткіші nullptr параметріне қарсы тексерілмей тұрып пайдаланылған. Тексеру жолдары: 172, 174. AMDGPUInline.cpp 172

Индекс Калли басында функция шақырылған кезде сілтеме алынып тасталады алу TTI.

Содан кейін бұл көрсеткіштің теңдігін тексеру керек екен 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] Қолданбаның логикасын тексеруді қарастырыңыз. 'else' кілт сөзі жоқ болуы мүмкін. 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] Жолдар біріктірілді, бірақ пайдаланылмайды. "Нәтиже + 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' 'карта' сыныбына жатады. Бұл анықталмаған мінез-құлыққа әкелуі мүмкін. 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

Тексерудің мағынасы жоқ. Айнымалы келесіБайт әрқашан мәнге тең емес 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, содан кейін функция өзінің орындалуын аяқтайды.

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 & 0xFFFFFFFF8u әрекетін орындау кезінде барлық отыз екі жоғары ретті бит нөлге дейін қалпына келтірілетіні белгілі болды. Сірә, бұл бағдарламашы қалаған нәрсе емес. Мен оның есептеуді қалайтынына күмәнданамын: 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

Бұл жаңа қызықты диагностика код бөлігі көшірілген және ондағы кейбір атаулар өзгерте бастаған, бірақ бір жерде оны түзетпеген жағдайларды анықтайды.

Екінші блокта олар өзгергенін ескеріңіз 0-де туралы 1-де. Бірақ бір жерде олар оны түзетпеді. Сірә, ол былай жазылуы керек еді:

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

Мұндай қателіктерден өзіңізді қалай қорғауға болады? Code-Review бағдарламасында мұқият болыңыз және кодты жүйелі түрде тексеру үшін PVS-Studio статикалық анализаторын пайдаланыңыз.

Осы типтегі қателері бар басқа код фрагменттеріне сілтеме жасаудың қажеті жоқ. Мен мақалада ескертулер тізімін ғана қалдырамын:

  • V1004 [CWE-476] "Expr" көрсеткіші nullptr мәніне қарсы тексерілгеннен кейін қауіпті пайдаланылды. Тексеру жолдары: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] "PI" көрсеткіші nullptr мәніне қарсы тексерілгеннен кейін қауіпті пайдаланылды. Тексеру жолдары: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] "StatepointCall" көрсеткіші nullptr арқылы тексерілгеннен кейін қауіпсіз пайдаланылды. Тексеру жолдары: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] "RV" көрсеткіші nullptr арқылы тексерілгеннен кейін қауіпті пайдаланылды. Тексеру жолдары: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] "CalleeFn" көрсеткіші nullptr арқылы тексерілгеннен кейін қауіпті пайдаланылды. Тексеру жолдары: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] "TC" көрсеткіші nullptr арқылы тексерілгеннен кейін қауіпсіз пайдаланылды. Тексеру жолдары: 1819, 1824. Driver.cpp 1824

N48-N60 фрагменті: маңызды емес, бірақ ақау (мүмкін жадтың ағып кетуі)

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

PVS-Studio ескертуі: V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Strategies" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-isel-fuzzer.cpp 58

Контейнердің соңына элемент қосу үшін сияқты std::вектор > жай жаза алмайсың 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-Studio анализаторы оны анықтауды үйренді.

Осы түрдегі басқа ескертулер:

  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Өту" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. PassManager.h 546
  • V1023 [CWE-460] Иесі жоқ көрсеткіш "AAs" контейнеріне "emplace_back" әдісі арқылы қосылады. Ерекше жағдайда жадтың ағуы орын алады. AliasAnalysis.h 324
  • V1023 [CWE-460] Иесі жоқ көрсеткіш "Entries" контейнеріне "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] Иесі жоқ көрсеткіш "PendingSubmodules" контейнеріне "emplace_back" әдісі арқылы қосылады. Ерекше жағдайда жадтың ағуы орын алады. ModuleMap.cpp 810
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Нысандар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. DebugMap.cpp 88
  • V1023 [CWE-460] Иесі жоқ көрсеткіш "emplace_back" әдісі арқылы "Strategies" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 685
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 686
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 688
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 689
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 690
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 691
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 692
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 693
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Модификаторлар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. llvm-stress.cpp 694
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Операндтар" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. GlobalISelEmitter.cpp 1911 ж
  • V1023 [CWE-460] Иесі жоқ көрсеткіш "Stash" контейнеріне "emplace_back" әдісі арқылы қосылады. Ерекше жағдайда жадтың ағуы орын алады. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Иесі жоқ көрсеткіш 'emplace_back' әдісі арқылы "Сәйкестіктер" контейнеріне қосылады. Ерекше жағдайда жадтың ағуы орын алады. GlobalISelEmitter.cpp 2702

қорытынды

Мен барлығы 60 ескерту жасап, кейін тоқтаттым. PVS-Studio анализаторы LLVM жүйесінде анықтайтын басқа ақаулар бар ма? Ия бар. Дегенмен, мен мақаланың кодтық фрагменттерін жазып жатқанда, кеш, дәлірек айтсақ, түн болды, мен оны күн деп атайтын кез келді деп шештім.

Сізге бұл қызықты болды және PVS-Studio анализаторын қолданып көргіңіз келеді деп үміттенемін.

Сіз анализаторды жүктеп алып, мина іздеу кемесінің кілтін мына жерден ала аласыз Бұл бет.

Ең бастысы, статикалық талдауды үнемі қолданыңыз. Бір реттік тексерулер, статикалық талдау әдістемесін танымал ету мақсатында біз жүзеге асырдық және PVS-Studio қалыпты сценарий емес.

Кодыңыздың сапасы мен сенімділігін арттыруда сәттілік тілейміз!

PVS-Studio анализаторы арқылы LLVM 8 қателерін табу

Егер сіз осы мақаланы ағылшын тілінде сөйлейтін аудиториямен бөліскіңіз келсе, аударма сілтемесін пайдаланыңыз: Андрей Карпов. PVS-Studio көмегімен LLVM 8 жүйесінде қателерді табу.

Ақпарат көзі: www.habr.com

пікір қалдыру