Lebih dari dua tahun telah berlalu sejak verifikasi terakhir dari kode proyek LLVM menggunakan penganalisa PVS-Studio kami. Mari kita pastikan bahwa penganalisa PVS-Studio masih merupakan alat terkemuka untuk mendeteksi kesalahan dan kerentanan potensial. Untuk melakukan ini, periksa dan temukan kesalahan baru dalam rilis LLVM 8.0.0.
Artikel yang akan ditulis
Sejujurnya, saya tidak ingin menulis artikel ini. Tidak menarik untuk menulis tentang proyek yang telah kami uji berulang kali (
1 ,
2 ,
3 ). Lebih baik menulis tentang sesuatu yang baru, tetapi saya tidak punya pilihan.
Setiap kali versi baru LLVM dirilis atau
Clang Static Analyzer diperbarui, pertanyaan-pertanyaan berikut muncul di surat kami:
Lihat, versi baru Clang Static Analyzer telah belajar menemukan bug baru! Menurut saya relevansi penggunaan PVS-Studio menurun. Dentang menemukan lebih banyak kesalahan dari sebelumnya dan mengejar kemampuan PVS-Studio. Apa yang Anda pikirkan tentang ini?Untuk ini, saya selalu ingin menjawab sesuatu dalam roh:
Kami juga tidak duduk diam! Kami telah secara signifikan meningkatkan kemampuan penganalisa PVS-Studio. Jadi jangan khawatir, kami terus memimpin, seperti sebelumnya.
Sayangnya, ini jawaban yang buruk. Tidak ada bukti di dalamnya. Dan itulah sebabnya saya menulis artikel ini sekarang. Jadi, proyek LLVM sekali lagi telah diuji dan berbagai kesalahan telah ditemukan di dalamnya. Yang tampaknya menarik bagi saya, sekarang saya akan tunjukkan. Kesalahan ini tidak dapat ditemukan oleh Clang Static Analyzer (atau sangat tidak nyaman untuk melakukannya). Dan kita bisa. Dan saya menemukan dan menulis semua kesalahan ini dalam satu malam.
Tetapi penulisan artikel itu berlangsung selama beberapa minggu. Saya tidak bisa memaksakan diri untuk mengatur semua ini dalam bentuk teks :).
Ngomong-ngomong, jika Anda tertarik pada teknologi apa yang digunakan dalam alat analisa PVS-Studio untuk mendeteksi kesalahan dan kerentanan potensial, maka saya sarankan agar Anda membiasakan
diri dengan
catatan ini.
Diagnostik baru dan lama
Seperti yang sudah dicatat, sekitar dua tahun lalu, proyek LLVM sekali lagi diverifikasi, dan kesalahan yang ditemukan diperbaiki. Sekarang di artikel ini sebagian kesalahan baru akan disajikan. Mengapa bug baru ditemukan? Ada 3 alasan untuk ini:
- Proyek LLVM berkembang, kode lama berubah di dalamnya, dan yang baru muncul. Secara alami, ada kesalahan baru dalam kode yang dimodifikasi dan ditulis. Ini menunjukkan dengan baik bahwa analisis statis harus diterapkan secara teratur, dan bukan dari kasus ke kasus. Artikel kami menunjukkan kemampuan alat analisa PVS-Studio dengan baik, tetapi ini tidak ada hubungannya dengan meningkatkan kualitas kode dan mengurangi biaya memperbaiki kesalahan. Gunakan penganalisa kode statis secara teratur!
- Kami sedang menyelesaikan dan meningkatkan diagnostik yang ada. Oleh karena itu, penganalisa dapat mendeteksi kesalahan yang tidak diperhatikan selama pemeriksaan sebelumnya.
- PVS-Studio memperkenalkan diagnostik baru, yang tidak 2 tahun yang lalu. Saya memutuskan untuk memisahkan mereka menjadi bagian yang terpisah untuk menunjukkan dengan jelas perkembangan PVS-Studio.
Cacat diidentifikasi oleh diagnostik yang ada 2 tahun yang lalu
Fragmen N1: Salin-Tempelstatic bool ShouldUpgradeX86Intrinsic(Function *F, StringRef Name) { if (Name == "addcarryx.u32" ||
Peringatan PVS-Studio:
V501 [CWE-570] Ada sub-ekspresi identik 'Name.startswith ("avx512.mask.permvar.")' Di sebelah kiri dan di sebelah kanan '||' operator. AutoUpgrade.cpp 73
Diperiksa ulang bahwa namanya dimulai dengan substring “avx512.mask.permvar.”. Pada tes kedua, mereka jelas ingin menulis sesuatu yang lain, tetapi lupa untuk memperbaiki teks yang disalin.
Fragmen 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; .... }
Peringatan PVS-Studio: V501 Ada sub-ekspresi identik 'CXNameRange_WantQualifier' di kiri dan di kanan '|' operator. CIndex.cpp 7245
Karena kesalahan ketik,
CXNameRange_WantQualifier konstanta yang
sama digunakan dua kali .
Cuplikan N3: Kebingungan tentang Prioritas Operator int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) { .... if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0) return 0; .... }
Peringatan PVS-Studio:
V502 [CWE-783] Mungkin Operator '?:' Bekerja dengan cara yang berbeda dari yang diharapkan. Operator '?:' Memiliki prioritas lebih rendah daripada operator '=='. PPCTargetTransformInfo.cpp 404
Menurut pendapat saya, ini adalah kesalahan yang sangat indah. Ya, saya tahu saya punya ide aneh tentang kecantikan :).
Sekarang, sesuai dengan
prioritas operator , ekspresi dihitung sebagai berikut:
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0
Dari sudut pandang praktis, kondisi seperti itu tidak masuk akal, karena dapat direduksi menjadi:
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())
Ini adalah kesalahan yang jelas. Kemungkinan besar, 0/1 ingin membandingkan dengan variabel
Indeks . Untuk memperbaiki kode, tambahkan tanda kurung di sekitar operator ternary:
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))
Omong-omong, operator ternary sangat berbahaya dan memicu kesalahan logis. Berhati-hatilah dengan itu dan jangan serakah untuk menempatkan tanda kurung. Saya membahas topik ini secara lebih rinci di
sini, di bab “Takut pada operator?: Dan lampirkan dalam tanda kurung”.
Fragmen N4, N5: Null pointer 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; } .... }
Peringatan PVS-Studio:
V522 [CWE-476] Dereferencing dari null pointer 'LHS' mungkin terjadi. TGParser.cpp 2152
Jika pointer
LHS adalah nol, peringatan harus dikeluarkan. Namun, alih-alih ini akan
mengubah pointer nol ini:
LHS-> getAsString () .
Ini adalah situasi yang sangat khas ketika kesalahan disembunyikan di penangan kesalahan, karena tidak ada yang menguji mereka. Analisis statis memeriksa semua kode yang dapat dijangkau, tidak peduli seberapa sering digunakan. Ini adalah contoh yang sangat baik tentang bagaimana analisis statis melengkapi teknik pengujian dan perlindungan kesalahan lainnya.
Kesalahan serupa yang memproses pointer
RHS dibuat dalam kode di bawah ini: V522 [CWE-476] Dereferencing dari null pointer 'RHS' mungkin terjadi. TGParser.cpp 2186
Fragmen N6: Menggunakan kursor setelah bergerak static Expected<bool> ExtractBlocks(....) { .... std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap); .... BD.setNewProgram(std::move(ProgClone));
Peringatan PVS-Studio: V522 [CWE-476] Dereferencing dari penunjuk nol 'ProgClone' mungkin terjadi. Miscompilation.cpp 601
Pada awalnya, penunjuk pintar
ProgClone berhenti memiliki objek:
BD.setNewProgram(std::move(ProgClone));
Bahkan, sekarang
ProgClone adalah pointer nol. Oleh karena itu, dereferencing dari null pointer harus terjadi tepat di bawah:
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
Tetapi, pada kenyataannya, ini tidak akan terjadi! Perhatikan bahwa loop tidak benar-benar berjalan.
Pada awalnya, wadah
MiscompiledFunctions dihapus:
MiscompiledFunctions.clear();
Selanjutnya, ukuran wadah ini digunakan dalam kondisi loop:
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
Sangat mudah untuk melihat bahwa loop tidak dimulai. Saya pikir ini juga kesalahan, dan kode harus ditulis berbeda.
Sepertinya kita bertemu dengan kesalahan yang sangat terkenal itu! Satu kesalahan menyamarkan yang lain :).
Fragmen N7: Menggunakan kursor setelah bergerak 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));
Peringatan PVS-Studio: V522 [CWE-476] Dereferencing dari null pointer 'Test' mungkin terjadi. Miscompilation.cpp 709
Lagi-lagi situasi yang sama. Pada awalnya, isi objek dipindahkan, dan kemudian digunakan seolah-olah tidak ada yang terjadi. Saya semakin melihat situasi ini dalam kode program, setelah semantik gerakan muncul di C ++. Untuk ini saya suka bahasa C ++! Ada banyak cara baru untuk menembak kaki Anda sendiri. Alat analisa PVS-Studio akan selalu bekerja :).
Fragmen N8: Null Pointer 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 Warning: V522 [CWE-476] Dereferencing dari null pointer 'Type' mungkin terjadi. PrettyFunctionDumper.cpp 233
Selain penangan kesalahan, fungsi untuk debugging cetakan biasanya tidak diuji. Di hadapan kita adalah kasus seperti itu. Fungsi sedang menunggu pengguna yang, alih-alih menyelesaikan masalahnya, akan dipaksa untuk memperbaikinya.
Dengan benar:
if (Type) Type->dump(*this); else Printer << "<unknown-type>";
Fragmen N9: Null Pointer void SearchableTableEmitter::collectTableEntries( GenericTable &Table, const std::vector<Record *> &Items) { .... RecTy *Ty = resolveTypes(Field.RecType, TI->getType()); if (!Ty)
PVS-Studio Warning: V522 [CWE-476] Dereferencing dari null pointer 'Ty' mungkin terjadi. SearchableTableEmitter.cpp 614
Saya pikir, jadi semuanya jelas dan tidak memerlukan penjelasan.
Fragmen N10: Typo bool FormatTokenLexer::tryMergeCSharpNullConditionals() { .... auto &Identifier = *(Tokens.end() - 2); auto &Question = *(Tokens.end() - 1); .... Identifier->ColumnWidth += Question->ColumnWidth; Identifier->Type = Identifier->Type;
PVS-Studio Warning:
V570 Variabel 'Identifier-> Type' ditugaskan untuk dirinya sendiri. FormatTokenLexer.cpp 249
Tidak masuk akal untuk menetapkan variabel ke dirinya sendiri. Kemungkinan besar mereka ingin menulis:
Identifier->Type = Question->Type;
Fragmen N11: Istirahat yang mencurigakan void SystemZOperand::print(raw_ostream &OS) const { switch (Kind) { break; case KindToken: OS << "Token:" << getToken(); break; case KindReg: OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg()); break; .... }
Peringatan PVS-Studio:
V622 [CWE-478] Pertimbangkan untuk memeriksa pernyataan 'sakelar'. Mungkin saja operator 'case' pertama hilang. SystemZAsmParser.cpp 652
Pada awalnya ada pernyataan
istirahat yang sangat mencurigakan. Apakah Anda lupa menulis sesuatu yang lain di sini?
Fragmen N12: Memeriksa pointer setelah dereferencing 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"); .... }
Peringatan PVS-Studio:
V595 [CWE-476] Pointer 'Callee' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 172, 174. AMDGPUInline.cpp 172
Pointer
Callee di awal
ditereferensi ketika fungsi
getTTI dipanggil .
Dan kemudian ternyata pointer ini harus diperiksa untuk kesetaraan
nullptr :
if (!Callee || Callee->isDeclaration())
Tapi sudah terlambat ...
Fragment N13 - N ...: Memeriksa pointer setelah dereferencingSituasi yang dibahas dalam cuplikan kode sebelumnya tidak unik. Dia ditemukan di sini:
static Value *optimizeDoubleFP(CallInst *CI, IRBuilder<> &B, bool isBinary, bool isPrecise = false) { .... Function *CalleeFn = CI->getCalledFunction(); StringRef CalleeNm = CalleeFn->getName();
Peringatan PVS-Studio: V595 [CWE-476] Pointer 'CalleeFn' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 1079, 1081. SederhanakanLibCalls.cpp 1079
Dan di sini:
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());
Peringatan PVS-Studio: V595 [CWE-476] Pointer 'ND' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 532, 534. SemaTemplateInstantiateDecl.cpp 532
Dan di sini:
- V595 [CWE-476] Pointer 'U' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 404, 407. DWARFFormValue.cpp 404
- V595 [CWE-476] Pointer 'ND' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 2149, 2151. SemaTemplateInstantiate.cpp 2149
Dan kemudian menjadi tidak menarik bagi saya untuk mempelajari peringatan dengan nomor V595. Jadi saya tidak tahu apakah ada kesalahan serupa selain yang tercantum di sini. Kemungkinan besar ada.
Fragmen N17, N18: Pergeseran mencurigakan static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize, uint64_t &Encoding) { .... unsigned Size = RegSize; .... uint64_t NImms = ~(Size-1) << 1; .... }
Peringatan PVS-Studio:
V629 [CWE-190] Pertimbangkan untuk memeriksa ekspresi '~ (Ukuran - 1) << 1'. Pergeseran bit dari nilai 32-bit dengan ekspansi selanjutnya ke tipe 64-bit. AArch64AddressingModes.h 260
Mungkin ini bukan kesalahan, dan kode berfungsi persis seperti yang dimaksudkan. Tapi ini jelas tempat yang sangat mencurigakan, dan perlu diperiksa.
Misalkan variabel
Ukuran adalah 16, dan kemudian pembuat kode berencana untuk mendapatkan nilai dalam variabel
NImms :
11111111111111111111111111111111111111111111111111111111111111100000000
Namun, pada kenyataannya, hasilnya adalah:
00000000000000000000000000000000000000111111111111111111111111111111000000
Faktanya adalah bahwa semua perhitungan terjadi menggunakan tipe unsigned 32-bit. Dan hanya kemudian, tipe unsigned 32-bit ini akan secara implisit diperluas ke
uint64_t . Dalam hal ini, bit yang paling signifikan adalah nol.
Anda dapat memperbaiki situasi seperti ini:
uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
Situasi serupa: V629 [CWE-190] Pertimbangkan untuk memeriksa ekspresi 'Immr << 6'. Pergeseran bit dari nilai 32-bit dengan ekspansi selanjutnya ke tipe 64-bit. AArch64AddressingModes.h 269
Cuplikan N19: Kehilangan kata kunci lain ? void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) { .... if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
Peringatan PVS-Studio:
V646 [CWE-670] Pertimbangkan untuk memeriksa logika aplikasi. Mungkin kata kunci 'lain' tidak ada. AMDGPUAsmParser.cpp 5655
Tidak ada kesalahan di sini. Karena kemudian blok pertama
jika diakhiri dengan
melanjutkan , tidak masalah
apakah ada kata kunci
lain atau tidak. Bagaimanapun, kode akan bekerja sama. Namun, kehilangan yang
lain membuat kode lebih tidak jelas dan berbahaya. Jika di masa depan
terus menghilang, maka kode akan mulai bekerja dengan cara yang sama sekali berbeda. Menurut pendapat saya, lebih baik menambahkan yang
lain .
Fragment N20: Empat kesalahan ketik dari jenis yang sama 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();
Peringatan PVS-Studio:
- V655 [CWE-480] Senar digabungkan tetapi tidak digunakan. Pertimbangkan untuk memeriksa ekspresi 'Hasil + Nama.str ()'. Symbol.cpp 32
- V655 [CWE-480] Senar digabungkan tetapi tidak digunakan. Pertimbangkan untuk memeriksa ekspresi 'Hasil + "(Kelas ObjC)" + Name.str ()'. Symbol.cpp 35
- V655 [CWE-480] Senar digabungkan tetapi tidak digunakan. Pertimbangkan untuk memeriksa ekspresi 'Hasil + "(ObjC Kelas EH)" + Name.str ()'. Symbol.cpp 38
- V655 [CWE-480] Senar digabungkan tetapi tidak digunakan. Pertimbangkan untuk memeriksa ekspresi 'Hasil + "(ObjC IVar)" + Name.str ()'. Symbol.cpp 41
Secara kebetulan, operator + digunakan alih-alih operator + =. Hasilnya adalah desain yang tidak berarti.
Fragmen N21: Perilaku tidak terdefinisi 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(); } } }
Coba cari sendiri kode yang berbahaya. Dan ini adalah gambar untuk gangguan, agar tidak segera melihat jawabannya:
Peringatan PVS-Studio:
V708 [CWE-758] Konstruksi berbahaya digunakan: 'FeaturesMap [Op] = FeaturesMap.size ()', di mana 'FeaturesMap' adalah dari kelas 'map'. Ini dapat menyebabkan perilaku yang tidak terdefinisi. RISCVCompressInstEmitter.cpp 490
Garis masalah:
FeaturesMap[Op] = FeaturesMap.size();
Jika elemen
Op tidak ditemukan, elemen baru dibuat di peta dan jumlah elemen di peta ini ditulis di sana. Hanya tidak diketahui apakah fungsi
ukuran akan dipanggil sebelum atau setelah menambahkan elemen baru.
Fragmen N22-N24: Penugasan Kembali Error MachOObjectFile::checkSymbolTable() const { .... } else { MachO::nlist STE = getSymbolTableEntry(SymDRI); NType = STE.n_type;
PVS-Studio Warning:
V519 [CWE-563] Variabel 'NType' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 1663, 1664. MachOObjectFile.cpp 1664
Saya pikir tidak ada kesalahan nyata di sini. Hanya mengulangi tugas yang tidak perlu. Tapi masih ada kesalahan.
Demikian pula:
- V519 [CWE-563] Variabel 'B.NDesc' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] Variabel diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 59, 61. coff2yaml.cpp 61
Fragmen N25-N27: Penugasan ulang lainnyaSekarang pertimbangkan opsi penugasan yang sedikit berbeda.
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 Warning: V519 [CWE-563] Variabel 'Alignment' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 1158, 1160. LoadStoreVectorizer.cpp 1160
Ini adalah kode yang sangat aneh yang tampaknya mengandung kesalahan logis. Pada awalnya, variabel
Alignment diberi nilai tergantung pada kondisinya. Dan kemudian penugasan terjadi lagi, tetapi sekarang tanpa verifikasi.
Situasi serupa dapat dilihat di sini:
- V519 [CWE-563] Variabel 'Efek' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] Variabel 'ExpectNoDerefChunk' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 4970, 4973. SemaType.cpp 4973
Fragmen N28: Selalu kondisi yang benar 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)
Peringatan PVS-Studio:
V547 [CWE-571] Ekspresi 'nextByte! = 0x90' selalu benar. X86DisassemblerDecoder.cpp 379
Verifikasi tidak masuk akal. Variabel
nextByte selalu tidak sama dengan
0x90 , yang mengikuti dari pemeriksaan sebelumnya. Ini semacam kesalahan logis.
Fragment N29 - N ...: Selalu benar / kondisi salahPenganalisa memberikan banyak peringatan bahwa seluruh kondisi (
V547 ) atau bagian dari itu (
V560 ) selalu benar atau salah. Seringkali ini bukan kesalahan nyata, tetapi hanya kode yang tidak akurat, hasil dari penyebaran makro dan sejenisnya. Namun demikian, masuk akal untuk melihat semua peringatan ini, karena dari waktu ke waktu ada kesalahan logis yang nyata. Misalnya, kode ini mencurigakan:
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; .... }
Peringatan PVS-Studio:
V560 [CWE-570] Bagian dari ekspresi kondisional selalu salah: RegNo == 0xe. ARMDisassembler.cpp 939
Konstanta 0xE adalah nilai 14 dalam sistem desimal. Memeriksa
RegNo == 0xe tidak masuk akal, karena jika
RegNo> 13 , maka fungsi akan menyelesaikan eksekusi.
Ada banyak peringatan lain dengan pengidentifikasi V547 dan V560, tetapi, seperti dalam kasus
V595 , saya tidak tertarik mempelajari peringatan ini. Sudah jelas bahwa saya punya cukup bahan untuk menulis artikel :). Oleh karena itu, tidak diketahui berapa banyak kesalahan jenis ini dapat dideteksi dalam LLVM menggunakan PVS-Studio.
Saya akan memberikan contoh mengapa membosankan untuk mempelajari respons ini. Penganalisa benar dalam mengeluarkan peringatan untuk kode berikut. Tapi ini bukan kesalahan.
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons, tok::TokenKind ClosingBraceKind) { bool HasError = false; .... HasError = true; if (!ContinueOnSemicolons) return !HasError; .... }
Peringatan PVS-Studio: V547 [CWE-570] Ekspresi '! HasError' selalu salah. UnwrappedLineParser.cpp 1635
Fragmen N30: Pengembalian yang mencurigakan 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(); } .... }
Peringatan PVS-Studio:
V612 [CWE-670] 'pengembalian' tanpa syarat dalam satu lingkaran. R600OptimizeVectorRegisters.cpp 63
Ini adalah kesalahan atau trik khusus yang dimaksudkan untuk menjelaskan sesuatu kepada programmer yang membaca kode. Desain ini tidak menjelaskan apa pun kepada saya dan terlihat sangat mencurigakan. Lebih baik tidak menulis seperti itu :).
Apakah kamu lelah? Lalu saatnya membuat teh atau kopi.
Cacat terdeteksi oleh diagnostik baru
Saya pikir 30 pemicu diagnostik lama sudah cukup. Sekarang mari kita lihat apa yang menarik dapat ditemukan dengan diagnostik baru yang muncul di analisa setelah pemeriksaan
sebelumnya . Secara total, 66 diagnostik tujuan umum ditambahkan ke penganalisa C ++ selama waktu ini.
Fragmen N31: Kode tidak dapat dijangkau 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(); }
Peringatan PVS-Studio:
V779 [CWE-561] Kode tidak dapat dideteksi terdeteksi. Mungkin saja ada kesalahan. ExecutionUtils.cpp 146
Seperti yang Anda lihat, kedua cabang
pernyataan if diakhiri dengan panggilan ke
pernyataan pengembalian . Dengan demikian, wadah
CtorDtorsByPriority tidak
akan pernah
dikosongkan .
Cuplikan N32: Kode Tidak Terjangkau 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);
Peringatan PVS-Studio: V779 [CWE-561] Kode tidak dapat dideteksi terdeteksi. Mungkin saja ada kesalahan. LLParser.cpp 835
Situasi yang menarik. Mari kita lihat awal tempat ini:
return ParseTypeIdEntry(SummaryID); break;
Sekilas, sepertinya tidak ada kesalahan. Tampaknya pernyataan
break berlebihan di sini, dan Anda dapat menghapusnya. Namun, tidak semuanya begitu sederhana.
Penganalisa menghasilkan peringatan pada baris:
Lex.setIgnoreColonInIdentifiers(false); return false;
Memang, kode ini tidak dapat dijangkau. Semua case di akhir
beralih dengan panggilan ke
pernyataan kembali . Dan sekarang
break kesepian tanpa tujuan tidak terlihat begitu berbahaya! Mungkin salah satu cabang harus diakhiri dengan
istirahat , dan bukan dengan
kembali ?
Fragmen N33: Kebetulan bit tinggi 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 Warning:
V784 Ukuran bit mask kurang dari ukuran operan pertama. Ini akan menyebabkan hilangnya bit yang lebih tinggi. RuntimeDyld.cpp 815
Perhatikan bahwa fungsi
getStubAlignment mengembalikan tipe yang
tidak ditandatangani . Kami menghitung nilai ekspresi jika kami menganggap bahwa fungsi mengembalikan nilai 8:
~ (getStubAlignment () - 1)
~ (8u-1)
0xFFFFFFF8u
Sekarang perhatikan bahwa variabel
DataSize memiliki tipe unsigned 64-bit. Ternyata selama operasi DataSize & 0xFFFFFFF88, ketiga puluh dua bit tingkat tinggi akan diatur ulang. Kemungkinan besar, ini bukan yang diinginkan oleh programmer. Saya menduga bahwa dia ingin menghitung: DataSize & 0xFFFFFFFFFFFFFFFFFFF8u.
Untuk memperbaiki kesalahan, Anda harus menulis seperti ini:
DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);
Atau lebih:
DataSize &= ~(getStubAlignment() - 1ULL);
Fragmen N34: Gagal eksplisit yang gagal 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); .... }
Peringatan PVS-Studio:
V1028 [CWE-190] Kemungkinan meluap. Pertimbangkan casting operan dari operator 'NumElts * Scale' ke tipe 'size_t', bukan hasilnya. X86ISelLowering.h 1577
Konversi tipe eksplisit digunakan untuk mencegah overflow ketika mengalikan variabel tipe
int . Namun, casting eksplisit di sini tidak melindungi terhadap overflow.
Pada awalnya, variabel akan dikalikan, dan hanya dengan demikian hasil penggandaan 32-bit akan diperluas ke tipe size_t .Fragmen N35: Salin-Tempel yang gagal 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()));
V778 [CWE-682] Dua fragmen kode serupa ditemukan. Mungkin, ini adalah kesalahan ketik dan variabel 'Op1' harus digunakan alih-alih 'Op0'. InstCombineCompares.cpp 5507 Diagnostikbaru yang menarik ini mengungkapkan situasi ketika sepotong kode disalin dan beberapa nama mulai berubah di dalamnya, tetapi tidak diperbaiki di satu tempat.Perhatikan bahwa dalam perubahan blok kedua Op0 di OP1 . Tetapi di satu tempat mereka tidak memperbaikinya. Kemungkinan besar, seharusnya ditulis seperti ini: if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) { I.setOperand(1, ConstantFP::getNullValue(Op1->getType())); return &I; }
Fragmen N36: Kebingungan dalam variabel struct Status { unsigned Mask; unsigned Mode; Status() : Mask(0), Mode(0){}; Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) { Mode &= Mask; }; .... };
PVS-Studio Warning: V1001 [CWE-563] Variabel 'Mode' ditugaskan tetapi tidak digunakan pada akhir fungsi. SIModeRegister.cpp 48Sangat berbahaya untuk memberikan argumen fungsi dengan nama yang sama dengan anggota kelas. Sangat mudah bingung. Di hadapan kita adalah kasus seperti itu. Ungkapan ini tidak masuk akal: Mode &= Mask;
Argumen fungsi berubah. Dan itu dia. Argumen ini tidak lagi digunakan. Kemungkinan besar, perlu menulis seperti ini: Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) { this->Mode &= Mask; };
Fragmen N37: Kebingungan dalam variabel 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; }
Peringatan PVS-Studio: V1001 [CWE-563] Variabel 'Ukuran' ditetapkan tetapi tidak digunakan pada akhir fungsi. Object.cpp 424Situasinya mirip dengan yang sebelumnya. Itu harus ditulis: this->Size += this->EntrySize;
Fragmen N38-N47: Pointer lupa untuk memeriksa.Sebelumnya, kami memeriksa contoh pemicu diagnostik V595 . Esensinya adalah bahwa pointer dereferenced pada awalnya, dan hanya kemudian diperiksa. Diagnostik muda V1004 adalah kebalikan dari maknanya, tetapi juga mendeteksi banyak kesalahan. Ini mengidentifikasi situasi di mana pointer diperiksa di awal, dan kemudian lupa melakukannya. Pertimbangkan kasus-kasus seperti yang ditemukan di dalam LLVM. int getGEPCost(Type *PointeeType, const Value *Ptr, ArrayRef<const Value *> Operands) { .... if (Ptr != nullptr) {
Peringatan PVS-Studio: V1004 [CWE-476] Pointer 'Ptr' digunakan secara tidak aman setelah diverifikasi terhadap nullptr. Periksa baris: 729, 738. TargetTransformInfoImpl.h 738Variabel Ptr dapat berupa nullptr , sebagaimana dibuktikan oleh centang: if (Ptr != nullptr)
Namun, di bawah penunjuk ini dereferensi tanpa verifikasi sebelumnya: auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());
Pertimbangkan kasus serupa lainnya. llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD, bool Stub) { .... auto *FD = dyn_cast<FunctionDecl>(GD.getDecl()); SmallVector<QualType, 16> ArgTypes; if (FD)
Peringatan PVS-Studio: V1004 [CWE-476] Pointer 'FD' digunakan secara tidak aman setelah diverifikasi terhadap nullptr. Periksa baris: 3228, 3231. CGDebugInfo.cpp 3231Perhatikan pointer FD . Saya yakin masalahnya jelas terlihat, dan tidak diperlukan penjelasan khusus.Dan juga: static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result, Value *&BasePtr, const DataLayout &DL) { PointerType *PtrTy = dyn_cast<PointerType>(Ptr.getType()); if (!PtrTy) {
Peringatan PVS-Studio: V1004 [CWE-476] Pointer 'PtrTy' digunakan secara tidak aman setelah diverifikasi terhadap nullptr. Periksa baris: 960, 965. InterleavedLoadCombinePass.cpp 965Bagaimana melindungi diri Anda dari kesalahan seperti itu? Berhati-hatilah pada Code-Review dan gunakan analisa statis PVS-Studio untuk secara teratur memeriksa kode Anda.Tidak masuk akal untuk membawa fragmen kode lain dengan kesalahan jenis ini. Saya hanya akan meninggalkan daftar peringatan di artikel:- V1004 [CWE-476] Pointer 'Expr' digunakan secara tidak aman setelah diverifikasi terhadap nullptr. Periksa baris: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] Pointer 'PI' digunakan secara tidak aman setelah diverifikasi terhadap nullptr. Periksa baris: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] The 'StatepointCall' pointer was used unsafely after it was verified against nullptr. Check lines: 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] The 'RV' pointer was used unsafely after it was verified against nullptr. Check lines: 2263, 2268. TGParser.cpp 2268
- V1004 [CWE-476] The 'CalleeFn' pointer was used unsafely after it was verified against nullptr. Check lines: 1081, 1096. SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] The 'TC' pointer was used unsafely after it was verified against nullptr. Check lines: 1819, 1824. Driver.cpp 1824
N48-N60: , ( ) std::unique_ptr<IRMutator> createISelMutator() { .... std::vector<std::unique_ptr<IRMutationStrategy>> Strategies; Strategies.emplace_back( new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps())); .... }
Peringatan PVS-Studio: V1023 [CWE-460] Pointer tanpa pemilik ditambahkan ke wadah 'Strategi' dengan metode 'emplace_back'. Kebocoran memori akan terjadi jika ada pengecualian. llvm-isel-fuzzer.cpp 58Untuk menambahkan item ke akhir wadah seperti std :: vector <std :: unique_ptr <X>> Anda tidak dapat menulis xxx.push_back (X baru) , karena tidak ada konversi implisit dari X * ke std: : unique_ptr <X> .Solusi umum adalah menulis xxx.emplace_back (X baru) , karena ia mengkompilasi: metode emplace_back membangun elemen langsung dari argumen dan karenanya dapat menggunakan konstruktor eksplisit.Ini tidak aman. Jika vektor penuh, maka memori dialokasikan. Operasi realokasi memori mungkin gagal, menghasilkan pengecualian std :: bad_alloc dilemparkan . Dalam hal ini, penunjuk akan hilang, dan objek yang dibuat tidak akan pernah dihapus.Solusi yang aman adalah membuat unique_ptr , yang akan memiliki pointer sebelum vektor mencoba mengalokasikan kembali memori: xxx.push_back(std::unique_ptr<X>(new X))
Dimulai dengan C ++ 14, Anda dapat menggunakan 'std :: make_unique': xxx.push_back(std::make_unique<X>())
Jenis cacat ini tidak penting untuk LLVM. Jika memori tidak dapat dialokasikan, maka kompiler hanya akan berhenti bekerja. Namun, untuk aplikasi dengan waktu aktif yang lama yang tidak dapat berakhir begitu saja jika alokasi memori gagal, ini bisa menjadi bug yang jahat.Jadi, meskipun kode ini tidak menimbulkan bahaya praktis untuk LLVM, saya merasa berguna untuk berbicara tentang pola kesalahan ini dan bahwa penganalisa PVS-Studio belajar untuk mendeteksinya.Peringatan lain dari jenis ini:- V1023 [CWE-460] Pointer tanpa pemilik ditambahkan ke wadah 'Lulus' dengan metode 'emplace_back'. Kebocoran memori akan terjadi jika ada pengecualian. PassManager.h 546
- V1023 [CWE-460] A pointer without owner is added to the 'AAs' container by the 'emplace_back' method. A memory leak will occur in case of an exception. AliasAnalysis.h 324
- V1023 [CWE-460] A pointer without owner is added to the 'Entries' container by the 'emplace_back' method. A memory leak will occur in case of an exception. DWARFDebugFrame.cpp 519
- V1023 [CWE-460] A pointer without owner is added to the 'AllEdges' container by the 'emplace_back' method. A memory leak will occur in case of an exception. CFGMST.h 268
- V1023 [CWE-460] A pointer without owner is added to the 'VMaps' container by the 'emplace_back' method. A memory leak will occur in case of an exception. SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] A pointer without owner is added to the 'Records' container by the 'emplace_back' method. A memory leak will occur in case of an exception. FDRLogBuilder.h 30
- V1023 [CWE-460] A pointer without owner is added to the 'PendingSubmodules' container by the 'emplace_back' method. A memory leak will occur in case of an exception. ModuleMap.cpp 810
- V1023 [CWE-460] A pointer without owner is added to the 'Objects' container by the 'emplace_back' method. A memory leak will occur in case of an exception. DebugMap.cpp 88
- V1023 [CWE-460] A pointer without owner is added to the 'Strategies' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 685
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 686
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 688
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 689
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 690
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 691
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 692
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 693
- V1023 [CWE-460] A pointer without owner is added to the 'Modifiers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. llvm-stress.cpp 694
- V1023 [CWE-460] A pointer without owner is added to the 'Operands' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] A pointer without owner is added to the 'Stash' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] A pointer without owner is added to the 'Matchers' container by the 'emplace_back' method. A memory leak will occur in case of an exception. GlobalISelEmitter.cpp 2702
Kesimpulan
Secara total, saya menulis 60 peringatan, setelah itu saya berhenti. Apakah ada cacat lain yang terdeteksi oleh analyzer PVS-Studio di LLVM? Ya ada. Namun, ketika saya menulis cuplikan kode untuk artikel itu, sudah larut malam, atau lebih tepatnya, bahkan malam, dan saya memutuskan sudah waktunya untuk mengakhiri.Saya harap Anda tertarik, dan Anda ingin mencoba alat analisa PVS-Studio.Anda dapat mengunduh penganalisa dan mendapatkan kunci percobaan di halaman ini .Yang paling penting, gunakan analisis statis secara teratur. Pemeriksaan satu kali yang kami lakukan untuk mempopulerkan metodologi analisis statis dan PVS-Studio bukanlah skenario normal.Semoga berhasil dalam meningkatkan kualitas dan keandalan kode!
Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Andrey Karpov. Menemukan Bug di LLVM 8 dengan PVS-Studio .