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 жилийн өмнө байсан оношилгоогоор илэрсэн согогууд

Fragment N1: Copy-Paste

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.

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: null заагч

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

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' хоосон заагчаас ялгах тохиолдол гарч болзошгүй. Буруу эмхэтгэл.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) {

Гогцоо эхлэхгүй байгааг харахад хялбар байдаг. Энэ нь бас алдаа бөгөөд кодыг өөрөөр бичих ёстой гэж би бодож байна.

Бид тэр алдартай алдааны паритеттай тулгарсан бололтой! Нэг алдаа нөгөөг нь далдалдаг :).

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] "Туршилт" гэсэн хоосон заагчийг ялгаж салгаж магадгүй. Буруу эмхэтгэл.cpp 709

Дахин ижил нөхцөл байдал. Эхлээд объектын агуулгыг зөөж, дараа нь юу ч болоогүй юм шиг ашигладаг. C++ хэл дээр хөдөлгөөний семантик гарч ирсний дараа би энэ байдлыг програмын код дээр илүү олон удаа харж байна. Ийм учраас би C++ хэлэнд дуртай! Өөрийнхөө хөлийг буудах шинэ арга замууд улам бүр нэмэгдсээр байна. PVS-Studio анализатор үргэлж ажиллах болно :).

Fragment N8: null заагч

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

Fragment N9: null заагч

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 'Identifier->Type' хувьсагч нь өөртөө оноогдсон. FormatTokenLexer.cpp 249

Хувьсагчийг өөртөө оноох нь утгагүй юм. Тэд бичихийг хүссэн бололтой:

Identifier->Type = Question->Type;

Хэсэг N11: Сэжигтэй эвдрэл

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

PVS-Studio анхааруулга: V622 [CWE-478] 'Switch' мэдэгдлийг шалгах талаар бодож үзээрэй. Эхний "тохиолдол" оператор байхгүй байж магадгүй юм. 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] 'Callee' заагчийг nullptr-ийн эсрэг баталгаажуулахаас өмнө ашигласан. Шалгах мөр: 172, 174. AMDGPUInline.cpp 172

Заагч Калли функцийг дуудах үед эхэнд нь хамааралгүй болно авах TTI.

Тэгээд дараа нь энэ заагчийг тэгш байдлыг шалгах хэрэгтэй болж байна 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' нь 'map' ангилалд хамаарна. Энэ нь тодорхойгүй зан төлөвт хүргэж болзошгүй юм. 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] 'Alignment' хувьсагч нь дараалсан хоёр удаа утгыг оноодог. Магадгүй энэ нь алдаа байж магадгүй юм. Шалгах мөр: 1158, 1160. LoadStoreVectorizer.cpp 1160

Энэ бол логик алдаа агуулсан маш хачирхалтай код юм. Эхэндээ хувьсагч шугам нөхцөлөөс хамааран утга онооно. Дараа нь даалгавар дахин гарч ирдэг, гэхдээ одоо ямар ч шалгалтгүйгээр.

Үүнтэй төстэй нөхцөл байдлыг эндээс харж болно:

  • V519 [CWE-563] 'Effects' хувьсагчид хоёр удаа дараалан утгыг оноодог. Магадгүй энэ нь алдаа байж магадгүй юм. Шалгах мөр: 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, өмнөх шалгалтаас үүдэлтэй. Энэ бол нэг төрлийн логик алдаа юм.

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 ID-тай өөр олон анхааруулга байсан боловч одоо байгаа шиг 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 идэвхжүүлэлт хангалттай гэж бодож байна. Дараа нь анализатор дээр гарч ирсэн шинэ оношлогооны тусламжтайгаар ямар сонирхолтой зүйлсийг олж болохыг харцгаая өмнөх шалгалтууд. Энэ хугацаанд нийт 66 ерөнхий зориулалтын оношилгоо C++ анализаторт нэмэгдсэн байна.

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 оператор руу залгаснаар дуусна буцах. Үүний дагуу сав CtorDtors By Priority хэзээ ч цэвэрлэхгүй.

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

Одоо хувьсагч гэдгийг анхаарна уу Өгөгдлийн хэмжээ 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 битийн үр дүнг төрөл болгон өргөжүүлнэ. хэмжээ_т.

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: Хувьсах төөрөгдөл

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] 'Mode' хувьсагчийг өгсөн боловч функцийн төгсгөлд ашиглагдахгүй. SIModeRegister.cpp 48

Функцийн аргументуудад ангийн гишүүдтэй ижил нэр өгөх нь маш аюултай. Төөрөлдөх нь маш амархан. Яг ийм хэрэг бидний өмнө байна. Энэ илэрхийлэл утгагүй байна:

Mode &= Mask;

Функцийн аргумент өөрчлөгдөнө. Тэгээд л болоо. Энэ аргументыг ашиглахаа больсон. Та үүнийг ингэж бичих ёстой байсан байх.

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

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

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

Хувьсах 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] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'Strategies' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. llvm-isel-fuzzer.cpp 58

Савны төгсгөлд элемент нэмэхийн тулд гэх мэт std::вектор > чи зүгээр бичиж болохгүй xxx.push_back(шинэ X), -аас далд хөрвүүлэлт байхгүй тул X* в std::unique_ptr.

Нийтлэг шийдэл бол бичих явдал юм xxx.emplace_back(шинэ X)Энэ нь эмхэтгэсэн тул: арга буцах нь аргументаасаа шууд элемент бүтээдэг тул тодорхой бүтээгчийг ашиглаж болно.

Энэ нь аюулгүй биш юм. Хэрэв вектор дүүрсэн бол санах ой дахин хуваарилагдана. Санах ойг дахин хуваарилах ажиллагаа бүтэлгүйтсэнээр үл хамаарах зүйл гарч болзошгүй 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' аргаар 'Passes' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. PassManager.h 546
  • V1023 [CWE-460] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'AAs' саванд нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. AliasAnalysis.h 324
  • V1023 [CWE-460] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'Entries' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. 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' аргаар 'Records' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. FDRLogBuilder.h 30
  • V1023 [CWE-460] Эзэмшигчгүй заагчийг 'PendingSubmodules' контейнерт 'emplace_back' аргаар нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. ModuleMap.cpp 810
  • V1023 [CWE-460] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'Objects' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. DebugMap.cpp 88
  • V1023 [CWE-460] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'Strategies' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. 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] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'Operands' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'Stash' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Эзэмшигчгүй заагчийг 'emplace_back' аргаар 'Matchers' контейнерт нэмдэг. Онцгой тохиолдолд санах ой алдагдах болно. GlobalISelEmitter.cpp 2702

дүгнэлт

Би нийтдээ 60 удаа сануулга өгөөд зогссон. LLVM-д PVS-Studio анализатор илрүүлдэг бусад согогууд бий юу? Тиймээ, надад байгаа. Гэсэн хэдий ч би нийтлэлийн кодын хэсгүүдийг бичиж байх үед орой, эсвэл бүр шөнө болсон тул би үүнийг өдөр гэж нэрлэх цаг болсон гэж шийдсэн.

Энэ нь танд сонирхолтой санагдаж, PVS-Studio анализаторыг туршиж үзэхийг хүсч байна гэж найдаж байна.

Та анализаторыг татаж аваад мина тээгч хөлөг онгоцны түлхүүрийг эндээс авах боломжтой энэ хуудас.

Хамгийн чухал нь статик анализыг тогтмол ашиглах. Нэг удаагийн шалгалтСтатик шинжилгээний арга зүйг сурталчлах зорилгоор бидний хийсэн PVS-Studio нь ердийн хувилбар биш юм.

Кодын чанар, найдвартай байдлыг сайжруулахад амжилт хүсье!

PVS-Studio анализатор ашиглан LLVM 8 дээрх алдааг хайж олох

Хэрэв та энэ нийтлэлийг англи хэлээр ярьдаг үзэгчидтэй хуваалцахыг хүсвэл орчуулгын холбоосыг ашиглана уу: Андрей Карпов. PVS-Studio ашиглан LLVM 8-д алдаа хайж байна.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх