PVS-Studio анализаторунун жардамы менен LLVM 8де мүчүлүштүктөрдү табуу

PVS-Studio анализаторунун жардамы менен LLVM 8де мүчүлүштүктөрдү табуу
Биздин PVS-Studio анализаторубузду колдонуу менен LLVM долбоорунун акыркы коду текшерилгенден бери эки жылдан ашык убакыт өттү. Келгиле, PVS-Studio анализатору дагы эле каталарды жана мүмкүн болуучу алсыздыктарды аныктоонун алдыңкы куралы экенине ынаналы. Бул үчүн, биз LLVM 8.0.0 релизинде жаңы каталарды текшерип, табабыз.

Жазыла турган макала

Чынын айтсам бул макаланы жазгым келген жок. Биз буга чейин бир нече жолу текшерген долбоор жөнүндө жазуу кызыктуу эмес (1, 2, 3). Жаңы нерсе жөнүндө жазганым жакшы, бирок менде башка арга жок.

LLVMдин жаңы версиясы чыккан сайын же жаңыртылганда Clang Static Analyzer, биз почтабызга төмөнкү түрдөгү суроолорду алабыз:

Караңыз, 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-Студиодо 2 жыл мурун болбогон жаңы диагностика пайда болду. Мен PVS-Студионун өнүгүшүн ачык көрсөтүү үчүн аларды өзүнчө бөлүмдө бөлүп көрсөтүүнү чечтим.

2 жыл мурун болгон диагностика менен аныкталган кемчиликтер

Fragment 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." субсап менен башталышы эки жолу текшерилет. Экинчи текшерүүдө алар, албетте, дагы бир нерсе жазгысы келген, бирок көчүрүлгөн текстти оңдоону унутуп коюшкан.

Fragment N2: Typo

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.

Fragment 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))

Айтмакчы, үчтүк оператор өтө кооптуу жана логикалык каталарды жаратат. Аны менен өтө этият болуңуз жана кашаа менен ач көздүк кылбаңыз. Мен бул теманы кененирээк карап чыктым бул жерде, бөлүмүндө "Абайлаңыз?: Оператор жана аны кашаага салыңыз."

Fragment 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->getAsString().

Бул ката иштеп чыгуучуда ката жашырылган өтө типтүү жагдай, анткени аларды эч ким сынабайт. Статикалык анализаторлор канчалык көп колдонулганына карабастан, бардык жеткиликтүү кодду текшерет. Бул статикалык талдоо башка тестирлөө жана каталарды коргоо ыкмаларын кантип толуктайт деген абдан жакшы мисал.

Окшош көрсөткүчтү иштетүү катасы Жолдун төмөнкү коддо уруксат берилген: V522 [CWE-476] 'RHS' нөл көрсөткүчүнүн шилтемеси алынып салынышы мүмкүн. TGParser.cpp 2186

Fragment N6: жылдыргандан кийин көрсөткүчтү колдонуу

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

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

PVS-Studio Эскертүү: V522 [CWE-476] "ProgClone" нөл көрсөткүчүнүн шилтемеси алынып салынышы мүмкүн. Miscompilation.cpp 601

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

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

Чынында, азыр ProgClone нөл көрсөткүч болуп саналат. Демек, нөл көрсөткүчтү жокко чыгаруу төмөндөгүдөй болушу керек:

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

Бирок, чындыгында, мындай болбойт! Белгилеп кетсек, цикл иш жүзүндө аткарылбайт.

Контейнердин башында MiscompiledFunctions тазаланган:

MiscompiledFunctions.clear();

Андан кийин, бул контейнердин өлчөмү цикл шартында колдонулат:

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

Укуктун башталбай жатканын көрүү оңой. Менимче, бул да ката жана кодду башкача жазыш керек.

Биз каталардын атактуу паритетине туш болдук окшойт! Бир ката экинчисин жаап калат :).

Fragment 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 анализатору дайыма иштейт :).

Fragment 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] "Type" нөл көрсөткүчүнүн шилтемеси алынып салынышы мүмкүн. PrettyFunctionDumper.cpp 233

Ката иштетүүчүлөрдөн тышкары, мүчүлүштүктөрдү оңдоо басып чыгаруу функциялары, адатта, текшерилбейт. Биздин алдыбызда ушундай иш бар. Функция өз көйгөйлөрүн чечүүнүн ордуна аны оңдоого аргасыз болгон колдонуучуну күтүп жатат.

туура:

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

Fragment 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

Менимче, баары түшүнүктүү жана түшүндүрүүнү талап кылбайт.

Fragment N10: Typo

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;

Fragment 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

Башында абдан шектүү оператор бар тыныгуу. Бул жерге дагы бир нерсе жазууну унутуп калдыңбы?

Fragment 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

Көрсөтүүчү Callee башында функция чакырылып жаткан учурда жокко чыгарылат getTTI.

Анан бул көрсөткүчтүн теңдигин текшерүү керек экен nullptr:

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

Бирок өтө кеч…

Fragment 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 номери менен эскертүүлөрдү изилдөөгө кызыкпай калдым. Демек, бул жерде саналган каталардан башка дагы окшош каталар барбы, билбейм. Көбүнчө бар.

Fragment 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

Fragment 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 менен аяктайт улантуу, анда маанилүү эмес, ачкыч сөз бар дагы же жок. Кандай болбосун, код бирдей иштейт. Дагы эле сагындым дагы кодду дагы түшүнүксүз жана коркунучтуу кылат. Эгерде келечекте улантуу жок болуп кетсе, код такыр башкача иштей баштайт. Менин оюмча кошумчалаган жакшы дагы.

Fragment 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] Саптар бириктирилген, бирок колдонулган эмес. 'Result + "(ObjC Class)" + Name.str()' туюнтмасын текшерүүнү карап көрүңүз. Symbol.cpp 35
  • V655 [CWE-480] Саптар бириктирилген, бирок колдонулган эмес. 'Result + "(ObjC Class EH)" + Name.str()' туюнтмасын текшерүүнү карап көрүңүз. Symbol.cpp 38
  • V655 [CWE-480] Саптар бириктирилген, бирок колдонулган эмес. 'Result + "(ObjC IVar)" + Name.str()' туюнтмасын текшерүүнү карап көрүңүз. Symbol.cpp 41

Кокусунан += операторунун ордуна + оператору колдонулат. Жыйынтыгында мааниси жок конструкциялар пайда болот.

Fragment 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 табылбаса, анда картада жаңы элемент түзүлөт жана ал жерде бул картадагы элементтердин саны жазылат. Функция чакырылабы же жокпу белгисиз көлөм жаңы элементти кошууга чейин же андан кийин.

Fragment 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

Fragment 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

Fragment 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

Текшерүүнүн мааниси жок. Variable nextByte ар дайым наркына барабар эмес 0x90, бул мурунку текшерүүдөн келип чыккан. Бул кандайдыр бир логикалык ката.

Fragment 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

Fragment 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 жалпы диагностика кошулду.

Fragment 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 эч качан тазаланбайт.

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

Жана чындап эле, бул код жеткиликсиз. Бардык учурларда өчүрүп күйгүзгүч оператордун чакыруусу менен аяктайт кайра. Ал эми азыр акылсыз жалгыз тыныгуу анчалык зыянсыз көрүнбөйт! Балким, бутактарынын бири менен аякташы керек тыныгууордуна кайра?

Fragment 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

Эми өзгөрмө экенин байкаңыз DataSize 64-бит кол коюлбаган түрү бар. Көрсө, DataSize & 0xFFFFFFFF8u операциясын аткарганда, бардык отуз эки жогорку бит нөлгө кайтарылат. Кыязы, бул программист каалаган нерсе эмес. Мен ал эсептегиси келген деп ойлойм: DataSize & 0xFFFFFFFFFFFFFF8u.

Катаны оңдоо үчүн, бул жазуу керек:

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

Же бул:

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

Fragment 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 биттик натыйжасы түргө кеңейтилет. size_t.

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

Fragment N36: Variable Confusion

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

Fragment N37: Variable Confusion

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;

Fragment 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

Variable 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

Fragment 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] Ээси жок көрсөткүч "Strategies" контейнерине "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. Бул учурда көрсөткүч жоголот жана түзүлгөн объект эч качан жок кылынбайт.

Коопсуз чечим түзүү болуп саналат уникалдуу_птрвектор эстутумду кайра бөлүштүрүүгө аракет кылганга чейин көрсөткүчкө ээ болот:

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

C++ 14 болгондуктан, сиз 'std::make_unique' колдоно аласыз:

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

Кемчиликтин бул түрү LLVM үчүн маанилүү эмес. Эгерде эстутумду бөлүштүрүү мүмкүн болбосо, компилятор жөн эле токтойт. Бирок, узак колдонмолор үчүн иштөө убактысы, эстутумду бөлүштүрүү иштебей калса, жөн эле токтото албайт, бул чыныгы жаман ката болушу мүмкүн.

Ошентип, бул код LLVM үчүн практикалык коркунуч туудурбаса да, мен бул ката үлгүсү жана PVS-Studio анализатору аны аныктоону үйрөндүм деп айтууну пайдалуу деп таптым.

Бул түрдөгү башка эскертүүлөр:

  • V1023 [CWE-460] Ээси жок көрсөткүч "Passes" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. PassManager.h 546
  • V1023 [CWE-460] Ээси жок көрсөткүч "AAs" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. AliasAnalysis.h 324
  • V1023 [CWE-460] Ээси жок көрсөткүч "Entries" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] Ээси жок көрсөткүч "AllEdges" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. CFGMST.h 268
  • V1023 [CWE-460] Ээси жок көрсөткүч "VMaps" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Records' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. FDRLogBuilder.h 30
  • V1023 [CWE-460] Ээси жок көрсөткүч "PendingSubmodules" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. ModuleMap.cpp 810
  • V1023 [CWE-460] Ээси жок көрсөткүч "Objects" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. DebugMap.cpp 88
  • V1023 [CWE-460] Ээси жок көрсөткүч "Strategies" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 685
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 686
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 688
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 689
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 690
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 691
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 692
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 693
  • V1023 [CWE-460] Ээси жок көрсөткүч 'emplace_back' ыкмасы менен 'Modifiers' контейнерине кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. llvm-stress.cpp 694
  • V1023 [CWE-460] Ээси жок көрсөткүч "Operands" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] Ээси жок көрсөткүч "Stash" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Ээси жок көрсөткүч "Matchers" контейнерине "emplace_back" ыкмасы менен кошулат. Эстутумдун агып кетиши өзгөчө учурларда пайда болот. GlobalISelEmitter.cpp 2702

жыйынтыктоо

Жалпысынан 60 жолу эскертүү берип, анан токтоттум. LLVMде PVS-Studio анализатору аныктаган башка кемчиликтер барбы? Ооба бар. Бирок, мен макала үчүн код фрагменттерин жазып жатканда, кечкисин, тагыраагы, түн болуп калды, мен аны бир күн деп атай турган убак келди деп чечтим.

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

Сиз анализаторду жүктөп алып, мина тазалоочу кеменин ачкычын бул жерден ала аласыз бул барак.

Эң негизгиси, статикалык анализди үзгүлтүксүз колдонуу. Бир жолку текшерүүлөрСтатикалык анализдин методологиясын жайылтуу максатында биз тарабынан ишке ашырылган жана PVS-Studio кадимки сценарий эмес.

Кодуңуздун сапатын жана ишенимдүүлүгүн жогорулатууда ийгилик!

PVS-Studio анализаторунун жардамы менен LLVM 8де мүчүлүштүктөрдү табуу

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

Source: www.habr.com

Комментарий кошуу