PVS-Studio analizörünü kullanarak LLVM 8'deki hataları bulma

PVS-Studio analizörünü kullanarak LLVM 8'deki hataları bulma
LLVM projesinin PVS-Studio analizörümüzü kullanarak yaptığı son kod kontrolünün üzerinden iki yıldan fazla zaman geçti. PVS-Studio analizörünün hataları ve potansiyel güvenlik açıklarını belirlemede hâlâ önde gelen bir araç olduğundan emin olalım. Bunu yapmak için LLVM 8.0.0 sürümündeki yeni hataları kontrol edip bulacağız.

Yazılacak makale

Dürüst olmak gerekirse bu yazıyı yazmak istemedim. Daha önce birkaç kez kontrol ettiğimiz bir proje hakkında yazmak ilginç değil (1, 2, 3). Yeni bir şey hakkında yazmak daha iyi ama başka seçeneğim yok.

LLVM'nin yeni bir sürümü yayınlandığında veya güncellendiğinde Clang Statik Analiz Cihazı, postamızda aşağıdaki türde sorular alıyoruz:

Bakın, Clang Static analyzer'ın yeni versiyonu yeni hataları bulmayı öğrendi! Bana öyle geliyor ki PVS-Studio kullanmanın önemi azalıyor. Clang, eskisinden daha fazla hata buluyor ve PVS-Studio'nun yeteneklerini yakalıyor. Bunun hakkında ne düşünüyorsun?

Buna her zaman şöyle bir cevap vermek isterim:

Biz de boş durmuyoruz! PVS-Studio analizörünün yeteneklerini önemli ölçüde geliştirdik. O yüzden endişelenmeyin, eskisi gibi liderlik etmeye devam ediyoruz.

Maalesef bu kötü bir cevap. İçinde hiçbir kanıt yok. İşte bu yüzden şimdi bu makaleyi yazıyorum. Böylece LLVM projesi bir kez daha kontrol edildi ve içinde çeşitli hatalar bulundu. Şimdi bana ilginç gelenleri göstereceğim. Clang Statik Analizör bu hataları bulamıyor (veya onun yardımıyla bunu yapmak son derece zahmetli). Ama yapabiliriz. Üstelik tüm bu hataları bir akşam bulup yazdım.

Ancak makalenin yazılması birkaç hafta sürdü. Bütün bunları metne dökmeye kendimi ikna edemedim :).

Bu arada, PVS-Studio analizöründe hataları ve olası güvenlik açıklarını tanımlamak için hangi teknolojilerin kullanıldığıyla ilgileniyorsanız, bunu tanımanızı öneririm Not.

Yeni ve eski teşhisler

Daha önce de belirtildiği gibi, yaklaşık iki yıl önce LLVM projesi bir kez daha kontrol edildi ve bulunan hatalar düzeltildi. Şimdi bu makale yeni bir dizi hata sunacak. Neden yeni hatalar bulundu? Bunun 3 nedeni var:

  1. LLVM projesi gelişiyor, eski kodu değiştiriyor ve yeni kod ekliyor. Doğal olarak değiştirilen ve yazılan kodda yeni hatalar var. Bu durum, statik analizin ara sıra değil, düzenli olarak kullanılması gerektiğini açıkça göstermektedir. Makalelerimiz PVS-Studio analizörünün yeteneklerini iyi gösteriyor, ancak bunun kod kalitesini iyileştirmek ve hataları düzeltme maliyetini azaltmakla hiçbir ilgisi yok. Düzenli olarak statik kod analizörü kullanın!
  2. Mevcut teşhisleri sonlandırıyor ve geliştiriyoruz. Bu nedenle analizci önceki taramalarda fark etmediği hataları tespit edebilir.
  3. PVS-Studio'da 2 yıl önce mevcut olmayan yeni teşhisler ortaya çıktı. PVS-Studio'nun gelişimini açıkça göstermek için bunları ayrı bir bölümde vurgulamaya karar verdim.

2 yıl önce var olan teşhislerle tespit edilen kusurlar

Parça N1: Kopyala-Yapıştır

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 uyarısı: V501 [CWE-570] '||' ifadesinin solunda ve sağında 'Name.startswith("avx512.mask.permvar.")' özdeş alt ifadeler vardır Şebeke. Otomatik Yükseltme.cpp 73

Adın "avx512.mask.permvar." alt dizesiyle başlayıp başlamadığı bir kez daha kontrol edilir. İkinci kontrolde belli ki başka bir şey yazmak istediler ama kopyalanan metni düzeltmeyi unuttular.

Parça N2: Yazım hatası

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

Uyarı PVS-Studio: V501 '|' işaretinin solunda ve sağında aynı 'CXNameRange_WantQualifier' alt ifadeleri vardır Şebeke. Cindex.cpp 7245

Yazım hatası nedeniyle aynı adlı sabit iki kez kullanıldı CXNameRange_WantQualifier.

Parça N3: Operatör önceliğiyle ilgili karışıklık

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

PVS-Studio uyarısı: V502 [CWE-783] Belki de '?:' operatörü beklenenden farklı bir şekilde çalışıyor. '?:' operatörü '==' operatöründen daha düşük önceliğe sahiptir. PPCTargetTransformInfo.cpp 404

Bana göre bu çok güzel bir yanılgıdır. Evet, güzellik hakkında tuhaf fikirlerim olduğunu biliyorum :).

Şimdi, göre operatör öncelikleriifade şu şekilde değerlendirilir:

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

Pratik açıdan böyle bir koşul mantıklı değildir çünkü aşağıdakilere indirgenebilir:

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

Bu açık bir hatadır. Büyük olasılıkla 0/1'i bir değişkenle karşılaştırmak istediler indeks. Kodu düzeltmek için üçlü operatörün etrafına parantez eklemeniz gerekir:

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

Bu arada, üçlü operatör çok tehlikelidir ve mantıksal hatalara neden olur. Bu konuda çok dikkatli olun ve parantez konusunda açgözlü olmayın. Bu konuyu daha detaylı inceledim burada, " ?: Operatörüne Dikkat Edin ve Onu Parantez içine Alın" bölümünde.

Parça N4, N5: Boş işaretçi

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 uyarısı: V522 [CWE-476] Boş işaretçi 'LHS'nin referansı kaldırılabilir. TGParser.cpp 2152

Eğer işaretçi LHS null ise uyarı verilmesi gerekir. Ancak bunun yerine aynı boş işaretçinin referansı kaldırılacaktır: LHS->getAsString().

Bu, bir hatanın hata işleyicisinde gizlendiği çok tipik bir durumdur, çünkü kimse onları test etmez. Statik analizörler, ne sıklıkla kullanılırsa kullanılsın, ulaşılabilir tüm kodları kontrol eder. Bu, statik analizin diğer test ve hata koruma tekniklerini nasıl tamamladığının çok iyi bir örneğidir.

Benzer işaretçi işleme hatası RHS hemen aşağıdaki kodda izin verilmektedir: V522 [CWE-476] 'RHS' boş işaretçisinin referansının kaldırılması gerçekleşebilir. TGParser.cpp 2186

Parça N6: Hareket ettikten sonra işaretçiyi kullanma

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 Uyarısı: V522 [CWE-476] 'ProgClone' boş işaretçisinin referansı kaldırılabilir. Yanlış derleme.cpp 601

Başlangıçta akıllı bir işaretçi ProgClone nesnenin sahibi olmaktan çıkar:

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

Aslında şimdi ProgClone bir boş göstericidir. Bu nedenle, hemen aşağıda bir boş işaretçi referansı gerçekleşmelidir:

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

Ancak gerçekte bu olmayacak! Döngünün gerçekte yürütülmediğini unutmayın.

Konteynerin başında Yanlış DerlenmişFonksiyonlar temizlendi:

MiscompiledFunctions.clear();

Daha sonra bu kabın boyutu döngü koşulunda kullanılır:

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

Döngünün başlamadığını görmek kolaydır. Bunun da bir bug olduğunu ve kodun farklı yazılması gerektiğini düşünüyorum.

Görünüşe göre o meşhur hata eşitliğiyle karşılaştık! Bir hata diğerini maskeler :).

Parça N7: Hareket ettikten sonra işaretçiyi kullanma

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 uyarısı: V522 [CWE-476] 'Test' boş işaretçisinin referansı kaldırılabilir. Yanlış derleme.cpp 709

Yine aynı durum. İlk başta nesnenin içeriği taşınır, ardından hiçbir şey olmamış gibi kullanılır. C++'da hareket semantiği ortaya çıktıktan sonra bu durumu program kodunda giderek daha sık görüyorum. C++ dilini bu yüzden seviyorum! Kendi bacağını vurmanın giderek daha fazla yeni yolu var. PVS-Studio analizörü her zaman çalışacak :).

Parça N8: Boş işaretçi

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 uyarısı: V522 [CWE-476] 'Tür' boş işaretçisinin referansı kaldırılabilir. PrettyFunctionDumper.cpp 233

Hata işleyicilerine ek olarak, hata ayıklama çıktısı işlevleri de genellikle test edilmez. Önümüzde böyle bir dava var. İşlev, sorunlarını çözmek yerine sorunu çözmek zorunda kalacak kullanıcıyı bekliyor.

düzeltin:

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

Parça N9: Boş işaretçi

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 uyarısı: V522 [CWE-476] 'Ty' boş işaretçisinin referansı kaldırılabilir. SearchableTableEmitter.cpp 614

Her şeyin açık olduğunu ve açıklamaya gerek olmadığını düşünüyorum.

Parça N10: Yazım hatası

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 uyarısı: V570 'Tanımlayıcı->Tür' değişkeni kendisine atanır. FormatTokenLexer.cpp 249

Bir değişkenin kendisine atanmasının bir anlamı yoktur. Büyük ihtimalle şunu yazmak istediler:

Identifier->Type = Question->Type;

Parça N11: Şüpheli kırılma

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 uyarısı: V622 [CWE-478] 'switch' ifadesini incelemeyi düşünün. İlk 'durum' operatörünün eksik olması mümkündür. SystemZAsmParser.cpp 652

Başlangıçta çok şüpheli bir operatör var kırılma. Buraya başka bir şey yazmayı mı unuttun?

Parça N12: Referans kaldırıldıktan sonra bir işaretçi kontrol ediliyor

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 uyarısı: V595 [CWE-476] 'Callee' işaretçisi, nullptr'a karşı doğrulanmadan önce kullanıldı. Satırları kontrol edin: 172, 174. AMDGPUInline.cpp 172

Endeks aranan başlangıçta, işlev çağrıldığında referans kaldırılır getTTI.

Ve sonra bu işaretçinin eşitlik açısından kontrol edilmesi gerektiği ortaya çıktı nullptr:

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

Fakat çok geç…

Parça N13 - N...: Referans kaldırıldıktan sonra işaretçi kontrol ediliyor

Önceki kod parçasında tartışılan durum benzersiz değildir. Burada görünüyor:

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 uyarısı: V595 [CWE-476] 'CalleeFn' işaretçisi, nullptr'a karşı doğrulanmadan önce kullanıldı. Satırları kontrol edin: 1079, 1081. SimplifyLibCalls.cpp 1079

Ve burada:

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 uyarısı: V595 [CWE-476] 'ND' işaretçisi, nullptr'ye karşı doğrulanmadan önce kullanıldı. Satırları kontrol edin: 532, 534. SemaTemplateInstantiateDecl.cpp 532

Ve burada:

  • V595 [CWE-476] 'U' işaretçisi, nullptr'ye karşı doğrulanmadan önce kullanıldı. Satırları kontrol edin: 404, 407. DWARFormValue.cpp 404
  • V595 [CWE-476] 'ND' işaretçisi, nullptr'ye karşı doğrulanmadan önce kullanıldı. Satırları kontrol edin: 2149, 2151. SemaTemplateInstantiate.cpp 2149

Daha sonra V595 numaralı uyarıları incelemek ilgimi çekti. Dolayısıyla burada listelenenlerin dışında daha fazla benzer hata olup olmadığını bilmiyorum. Büyük ihtimalle vardır.

Parça N17, N18: Şüpheli kayma

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

PVS-Studio uyarısı: V629 [CWE-190] '~(Boyut - 1) << 1' ifadesini incelemeyi düşünün. 32 bitlik değerin bit kaydırılması ve ardından 64 bitlik türe genişletilmesi. AArch64AddressingModes.h 260

Bu bir hata olmayabilir ve kod tam olarak amaçlandığı gibi çalışır. Ancak burası açıkça çok şüpheli bir yer ve kontrol edilmesi gerekiyor.

Değişken diyelim beden 16'ya eşittir ve kodun yazarı bunu bir değişkene almayı planlamıştır. NImm'ler değeri:

1111111111111111111111111111111111111111111111111111111111100000

Ancak gerçekte sonuç şöyle olacaktır:

0000000000000000000000000000000011111111111111111111111111100000

Gerçek şu ki, tüm hesaplamalar 32 bitlik imzasız tür kullanılarak gerçekleştirilir. Ve ancak o zaman bu 32 bit imzasız tür örtülü olarak şu şekilde genişletilecektir: uint64_t. Bu durumda en anlamlı bitler sıfır olacaktır.

Durumu şu şekilde düzeltebilirsiniz:

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

Benzer durum: V629 [CWE-190] 'Immr << 6' ifadesini incelemeyi düşünün. 32 bitlik değerin bit kaydırılması ve ardından 64 bitlik türe genişletilmesi. AArch64AddressingModes.h 269

Parça N19: Eksik anahtar kelime başka?

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 uyarısı: V646 [CWE-670] Uygulamanın mantığını incelemeyi düşünün. 'Else' anahtar sözcüğünün eksik olması mümkündür. AMDGPUAsmParser.cpp 5655

Burada bir hata yok. İlk bloktan bu yana if ile biter devam etmek, o zaman önemli değil, bir anahtar kelime var başka ya da değil. Her iki durumda da kod aynı şekilde çalışacaktır. Hala özledim başka kodu daha belirsiz ve tehlikeli hale getirir. Eğer gelecekte devam etmek kaybolduğunda kod tamamen farklı çalışmaya başlayacaktır. Bence eklemek daha iyi başka.

Parça N20: Aynı türden dört yazım hatası

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 uyarıları:

  • V655 [CWE-480] Dizeler birleştirildi ancak kullanılmıyor. 'Result + Name.str()' ifadesini incelemeyi düşünün. Sembol.cpp 32
  • V655 [CWE-480] Dizeler birleştirildi ancak kullanılmıyor. 'Result + "(ObjC Class)" + Name.str()' ifadesini incelemeyi düşünün. Sembol.cpp 35
  • V655 [CWE-480] Dizeler birleştirildi ancak kullanılmıyor. 'Result + "(ObjC Class EH) " + Name.str()' ifadesini incelemeyi düşünün. Sembol.cpp 38
  • V655 [CWE-480] Dizeler birleştirildi ancak kullanılmıyor. 'Result + "(ObjC IVar)" + Name.str()' ifadesini incelemeyi düşünün. Sembol.cpp 41

Yanlışlıkla += operatörü yerine + operatörü kullanılır. Sonuçta anlamdan yoksun tasarımlar ortaya çıkıyor.

Parça N21: Tanımsız davranış

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

Tehlikeli kodu kendiniz bulmaya çalışın. Ve bu, cevaba hemen bakmamak için dikkati dağıtmak için bir resim:

PVS-Studio analizörünü kullanarak LLVM 8'deki hataları bulma

PVS-Studio uyarısı: V708 [CWE-758] Tehlikeli yapı kullanılıyor: 'FeaturesMap[Op] = ÖzelliklerMap.size()'; burada 'FeaturesMap', 'map' sınıfındandır. Bu tanımsız davranışa yol açabilir. RISCVCompressInstEmitter.cpp 490

Sorun satırı:

FeaturesMap[Op] = FeaturesMap.size();

Eğer eleman Op bulunamazsa haritada yeni bir eleman oluşturulur ve bu haritadaki eleman sayısı oraya yazılır. Fonksiyonun çağrılıp çağrılmayacağı henüz bilinmiyor boyut yeni bir öğe eklemeden önce veya sonra.

Parça N22-N24: Tekrarlanan atamalar

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 uyarısı: V519 [CWE-563] 'NTip' değişkenine art arda iki kez değer atanıyor. Belki bu bir hatadır. Satırları kontrol edin: 1663, 1664. MachOObjectFile.cpp 1664

Burada gerçek bir hata olduğunu düşünmüyorum. Sadece gereksiz, tekrarlanan bir görev. Ama yine de bir hata.

Benzer şekilde:

  • V519 [CWE-563] 'B.NDesc' değişkenine art arda iki kez değer atanır. Belki bu bir hatadır. Satırları kontrol edin: 1488, 1489. llvm-nm.cpp 1489
  • V519 [CWE-563] Değişkene art arda iki kez değer atanır. Belki bu bir hatadır. Satırları kontrol edin: 59, 61. coff2yaml.cpp 61

Parça N25-N27: Daha fazla yeniden atama

Şimdi yeniden atamanın biraz farklı bir versiyonuna bakalım.

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 uyarısı: V519 [CWE-563] 'Hizalama' değişkenine art arda iki kez değer atanır. Belki bu bir hatadır. Satırları kontrol edin: 1158, 1160. LoadStoreVectorizer.cpp 1160

Bu, görünüşte mantıksal bir hata içeren çok garip bir koddur. Başlangıçta değişken hiza duruma bağlı olarak bir değer atanır. Ve sonra atama yeniden gerçekleşir, ancak artık herhangi bir kontrol yapılmaz.

Benzer durumları burada da görebilirsiniz:

  • V519 [CWE-563] 'Efektler' değişkenine art arda iki kez değer atanır. Belki bu bir hatadır. Satırları kontrol edin: 152, 165. WebAssemblyRegStackify.cpp 165
  • V519 [CWE-563] 'ExpectNoDerefChunk' değişkenine art arda iki kez değer atanır. Belki bu bir hatadır. Satırları kontrol edin: 4970, 4973. SemaType.cpp 4973

Parça N28: Her zaman doğru durum

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 uyarısı: V547 [CWE-571] 'nextByte != 0x90' ifadesi her zaman doğrudur. X86DisassemblerDecoder.cpp 379

Kontrol etmenin bir anlamı yok. Değişken sonrakiByte her zaman değere eşit değildir 0x90, önceki kontrolden çıkan sonuç. Bu bir tür mantıksal hatadır.

Parça N29 - N...: Her zaman doğru/yanlış koşulları

Analizör, durumun tamamının (V547) veya bir kısmı (V560) her zaman doğru veya yanlıştır. Çoğu zaman bunlar gerçek hatalar değil, yalnızca özensiz kod, makro genişletmenin sonucu ve benzerleridir. Ancak zaman zaman gerçek mantıksal hatalar meydana gelebileceği için tüm bu uyarılara bakmak mantıklıdır. Örneğin, kodun bu bölümü şüphelidir:

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 uyarısı: V560 [CWE-570] Koşullu ifadenin bir kısmı her zaman yanlıştır: RegNo == 0xe. ARMDisassembler.cpp 939

0xE sabiti ondalık sayı olarak 14 değeridir. Sınav Kayıt No == 0xe mantıklı değil çünkü eğer Kayıt No > 13, ardından işlev yürütülmesini tamamlayacaktır.

V547 ve V560 kimliklerine sahip başka birçok uyarı vardı, ancak V595, Bu uyarıları incelemek ilgimi çekmedi. Bir makale yazacak kadar materyalim olduğu zaten belliydi :). Bu nedenle, PVS-Studio kullanılarak LLVM'de bu türden kaç hatanın tanımlanabileceği bilinmemektedir.

Size bu tetikleyicileri çalışmanın neden sıkıcı olduğuna dair bir örnek vereceğim. Analizör aşağıdaki kod için uyarı vermekte kesinlikle haklıdır. Ancak bu bir hata değil.

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

PVS-Studio Uyarısı: V547 [CWE-570] '!HasError' ifadesi her zaman yanlıştır. UnwrappedLineParser.cpp 1635

Parça N30: ​​​​Şüpheli dönüş

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 uyarısı: V612 [CWE-670] Bir döngü içinde koşulsuz bir 'geri dönüş'. R600OptimizeVectorRegisters.cpp 63

Bu ya bir hatadır ya da kodu okuyan programcılara bir şeyi açıklamayı amaçlayan özel bir tekniktir. Bu tasarım bana hiçbir şey açıklamıyor ve çok şüpheli görünüyor. Böyle yazmamak daha iyi :).

Yorgun? O zaman çay veya kahve yapma zamanı.

PVS-Studio analizörünü kullanarak LLVM 8'deki hataları bulma

Yeni teşhislerle tespit edilen kusurlar

Eski teşhislerin 30 aktivasyonunun yeterli olduğunu düşünüyorum. Şimdi analizörde ortaya çıkan yeni teşhislerle ne gibi ilginç şeylerin bulunabileceğini görelim. önceki kontrol eder. Bu süre zarfında C++ analizörüne toplam 66 genel amaçlı teşhis eklendi.

Parça N31: Ulaşılamayan kod

Error CtorDtorRunner::run() {
  ....
  if (auto CtorDtorMap =
          ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
                    NoDependenciesToRegister, true))
  {
    ....
    return Error::success();
  } else
    return CtorDtorMap.takeError();

  CtorDtorsByPriority.clear();

  return Error::success();
}

PVS-Studio uyarısı: V779 [CWE-561] Ulaşılamaz kod algılandı. Bir hatanın mevcut olması mümkündür. ExecutionUtils.cpp 146

Gördüğünüz gibi operatörün her iki şubesi de if operatöre yapılan bir çağrıyla sona erer dönüş. Buna göre konteyner CtorDtorsByPriority asla temize çıkmayacak.

Parça N32: Ulaşılamayan kod

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

PVS-Studio uyarısı: V779 [CWE-561] Ulaşılamayan kod algılandı. Bir hatanın mevcut olması mümkündür. LLParser.cpp 835

İlginç bir durum. Önce şu yere bakalım:

return ParseTypeIdEntry(SummaryID);
break;

İlk bakışta burada bir hata yok gibi görünüyor. Operatöre benziyor kırılma burada fazladan bir tane var ve onu silebilirsiniz. Ancak her şey o kadar basit değil.

Analizör satırlarda bir uyarı verir:

Lex.setIgnoreColonInIdentifiers(false);
return false;

Ve gerçekten de bu koda ulaşılamıyor. Tüm vakalar anahtar operatörün aramasıyla sona erer dönüş. Ve şimdi tek başına anlamsız kırılma o kadar da zararsız görünmüyor! Belki dallardan biri şununla bitmeli: kırılmaama değil dönüş?

Parça N33: Yüksek bitlerin rastgele sıfırlanması

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 uyarısı: V784 Bit maskesinin boyutu, ilk işlenenin boyutundan daha küçüktür. Bu, daha yüksek bitlerin kaybına neden olacaktır. RuntimeDyld.cpp 815

Lütfen işlevin getStubAlignment dönüş türü imzasız. Fonksiyonun 8 değerini döndürdüğünü varsayarak ifadenin değerini hesaplayalım:

~(getStubAlignment() - 1)

~(8u-1)

0xFFFFFFFF8u

Şimdi değişkenin VeriBoyutu 64 bit imzasız bir türe sahiptir. DataSize & 0xFFFFFFF8u işlemini gerçekleştirirken otuz iki yüksek dereceli bitin tamamının sıfıra sıfırlanacağı ortaya çıktı. Büyük ihtimalle programcının istediği bu değildi. Şunu hesaplamak istediğinden şüpheleniyorum: DataSize & 0xFFFFFFFFFFFFFFFF8u.

Hatayı düzeltmek için şunu yazmalısınız:

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

Ya da:

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

Parça N34: Açık tür dönüşümü başarısız oldu

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 uyarısı: V1028 [CWE-190] Olası taşma. 'NumElts * Scale' operatörünün işlenenlerini sonuca değil, 'size_t' türüne dönüştürmeyi düşünün. X86ISelLowering.h 1577

Tür değişkenleri çarpılırken taşmayı önlemek için açık tür dönüşümü kullanılır int. Ancak buradaki açık tip dökümü taşmaya karşı koruma sağlamaz. İlk önce değişkenler çarpılacak ve ancak o zaman çarpmanın 32 bitlik sonucu türe genişletilecek size_t.

Parça N35: Başarısız Kopyala-Yapıştır

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] İki benzer kod parçası bulundu. Belki de bu bir yazım hatasıdır ve 'Op1' yerine 'Op0' değişkeni kullanılmalıdır. InstCombineCompares.cpp 5507

Bu yeni ilginç teşhis, bir kod parçasının kopyalandığı ve içindeki bazı isimlerin değiştirilmeye başlandığı ancak bir yerde düzeltilmediği durumları tespit ediyor.

Lütfen ikinci blokta değiştiklerini unutmayın. Op0 üzerinde Op1. Ama bir yerde düzeltmediler. Büyük ihtimalle şöyle yazılması gerekirdi:

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

Parça N36: Değişken Karışıklık

struct Status {
  unsigned Mask;
  unsigned Mode;

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

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

PVS-Studio uyarısı: V1001 [CWE-563] 'Mod' değişkeni atanır ancak işlevin sonunda kullanılmaz. SIModeRegister.cpp 48

İşlev argümanlarına sınıf üyeleriyle aynı isimleri vermek çok tehlikelidir. Kafanın karışması çok kolaydır. Önümüzde böyle bir dava var. Bu ifade mantıklı değil:

Mode &= Mask;

İşlev argümanı değişir. Bu kadar. Bu argüman artık kullanılmamaktadır. Büyük olasılıkla şu şekilde yazmanız gerekirdi:

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

Parça N37: Değişken Karışıklık

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

Uyarı PVS-Studio: V1001 [CWE-563] 'Boyut' değişkeni atanır ancak fonksiyonun sonunda kullanılmaz. Object.cpp 424

Durum öncekine benzer. Yazılmalıdır:

this->Size += this->EntrySize;

Parça N38-N47: Dizini kontrol etmeyi unuttular

Daha önce tanı tetikleme örneklerine bakmıştık V595. Bunun özü, işaretçinin başlangıçta referansının kaldırılması ve ancak o zaman kontrol edilmesidir. Genç teşhis V1004 anlam olarak tam tersidir ama aynı zamanda birçok hatayı da ortaya çıkarır. İşaretçinin başlangıçta kontrol edildiği ve daha sonra bunu yapmayı unuttuğu durumları tanımlar. LLVM'nin içinde bulunan bu gibi durumlara bakalım.

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 uyarısı: V1004 [CWE-476] 'Ptr' işaretçisi, nullptr'ye karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 729, 738. TargetTransformInfoImpl.h 738

değişken ptr eşit olabilir nullptr, kontrolde kanıtlandığı gibi:

if (Ptr != nullptr)

Ancak bu işaretçinin altında ön kontrol yapılmadan referans kaldırılır:

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

Benzer bir durumu daha ele alalım.

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 uyarısı: V1004 [CWE-476] 'FD' işaretçisi, nullptr'a karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 3228, 3231. CGDebugInfo.cpp 3231

İşarete dikkat edin FD. Sorunun açıkça görülebildiğinden ve özel bir açıklamaya gerek olmadığından eminim.

Ve dahası:

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 uyarısı: V1004 [CWE-476] 'PtrTy' işaretçisi, nullptr'ye karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 960, 965. InterleavedLoadCombinePass.cpp 965

Kendinizi bu tür hatalardan nasıl korursunuz? Code-Review konusunda daha dikkatli olun ve kodunuzu düzenli olarak kontrol etmek için PVS-Studio statik analiz cihazını kullanın.

Bu tür hataların olduğu diğer kod parçalarından alıntı yapmanın bir anlamı yok. Makalede yalnızca bir uyarı listesi bırakacağım:

  • V1004 [CWE-476] 'Expr' işaretçisi, nullptr'ye karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 1049, 1078. DebugInfoMetadata.cpp 1078
  • V1004 [CWE-476] 'PI' işaretçisi, nullptr'a karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 733, 753. LegacyPassManager.cpp 753
  • V1004 [CWE-476] 'StatepointCall' işaretçisi, nullptr'ye karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 4371, 4379. Verifier.cpp 4379
  • V1004 [CWE-476] 'RV' işaretçisi, nullptr'a karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 2263, 2268. TGParser.cpp 2268
  • V1004 [CWE-476] 'CalleeFn' işaretçisi, nullptr'ye karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 1081, 1096. SimplifyLibCalls.cpp 1096
  • V1004 [CWE-476] 'TC' işaretçisi, nullptr'a karşı doğrulandıktan sonra güvenli olmayan bir şekilde kullanıldı. Satırları kontrol edin: 1819, 1824. Driver.cpp 1824

Parça N48-N60: Kritik değil, ancak kusur (olası bellek sızıntısı)

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

PVS-Studio uyarısı: V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'Stratejiler' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-isel-fuzzer.cpp 58

Gibi bir kabın sonuna bir öğe eklemek için std::vektör > öylece yazamazsın xxx.push_back(yeni X), örtülü bir dönüşüm olmadığından X* в std::unique_ptr.

Yaygın bir çözüm yazmaktır xxx.emplace_back(yeni X)derlendiğinden beri: yöntem emplace_back bir öğeyi doğrudan argümanlarından oluşturur ve bu nedenle açık kurucuları kullanabilir.

Güvenli değil. Vektör doluysa bellek yeniden tahsis edilir. Belleğin yeniden tahsisi işlemi başarısız olabilir ve bu da bir istisnanın oluşmasına neden olabilir std::bad_alloc. Bu durumda işaretçi kaybolacak ve oluşturulan nesne hiçbir zaman silinmeyecektir.

Güvenli bir çözüm yaratmaktır benzersiz_ptrvektör belleği yeniden ayırmaya çalışmadan önce işaretçinin sahibi olacak:

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

C++ 14'ten beri 'std::make_unique' kullanabilirsiniz:

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

Bu tür bir kusur LLVM için kritik değildir. Bellek tahsis edilemezse derleyici duracaktır. Ancak uzun süreli uygulamalar için çalışma süresiBellek tahsisi başarısız olursa sonlandırılamayan bu gerçekten kötü bir hata olabilir.

Dolayısıyla, bu kod LLVM için pratik bir tehdit oluşturmasa da, bu hata modelinden ve PVS-Studio analizörünün bunu tanımlamayı öğrendiğinden bahsetmeyi faydalı buldum.

Bu türden diğer uyarılar:

  • V1023 [CWE-460] 'Emplace_back' yöntemiyle 'Geçişler' kapsayıcısına sahibi olmayan bir işaretçi eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. PassManager.h 546
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'AA' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. Takma AdAnaliz.h 324
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Girişler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. DWARFDebugFrame.cpp 519
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'AllEdges' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. CFGMST.h 268
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'VMaps' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. SimpleLoopUnswitch.cpp 2012
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'Kayıtlar' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. FDRLogBuilder.h 30
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'PendingSubmodules' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. ModuleMap.cpp 810
  • V1023 [CWE-460] 'Emplace_back' yöntemiyle 'Nesneler' kapsayıcısına sahibi olmayan bir işaretçi eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. DebugMap.cpp 88
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'Stratejiler' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-isel-fuzzer.cpp 60
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 685
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 686
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 688
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 689
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 690
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 691
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 692
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 693
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Değiştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. llvm-stres.cpp 694
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'İşlenenler' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. GlobalISelEmitter.cpp 1911
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'emplace_back' yöntemiyle 'Stash' kapsayıcısına eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. GlobalISelEmitter.cpp 2100
  • V1023 [CWE-460] Sahibi olmayan bir işaretçi, 'Eşleştiriciler' kapsayıcısına 'emplace_back' yöntemiyle eklenir. Bir istisna durumunda bellek sızıntısı meydana gelecektir. GlobalISelEmitter.cpp 2702

Sonuç

Toplamda 60 uyarı verdim, sonra bıraktım. PVS-Studio analizörünün LLVM'de tespit ettiği başka kusurlar var mı? Evet bende var. Ancak makale için kod parçalarını yazarken akşamın geç saatleriydi, hatta geceydi ve buna bir gün demenin zamanının geldiğine karar verdim.

Umarım bunu ilginç buldunuz ve PVS-Studio analizörünü denemek isteyeceksiniz.

Analizörü indirebilir ve mayın tarama gemisi anahtarını şu adresten alabilirsiniz: Bu sayfa.

En önemlisi statik analizi düzenli olarak kullanın. Tek seferlik kontrollerStatik analiz metodolojisini yaygınlaştırmak amacıyla tarafımızca yürütülen PVS-Studio senaryoları normal bir senaryo değildir.

Kodunuzun kalitesini ve güvenilirliğini artırmada iyi şanslar!

PVS-Studio analizörünü kullanarak LLVM 8'deki hataları bulma

Bu makaleyi İngilizce konuşan bir kitleyle paylaşmak istiyorsanız lütfen çeviri bağlantısını kullanın: Andrey Karpov. PVS-Studio ile LLVM 8'deki Hataları Bulma.

Kaynak: habr.com

Yorum ekle