Luwih saka rong taun kepungkur wiwit mriksa kode pungkasan proyek LLVM nggunakake analisa PVS-Studio. Priksa manawa analisa PVS-Studio isih dadi alat utama kanggo ngenali kesalahan lan kerentanan potensial. Kanggo nindakake iki, kita bakal mriksa lan nemokake kasalahan anyar ing release LLVM 8.0.0.
Artikel sing arep ditulis
Jujur, aku ora pengin nulis artikel iki. Ora menarik kanggo nulis babagan proyek sing wis dipriksa kaping pirang-pirang (
Saben-saben versi anyar LLVM dirilis utawa dianyari
Deleng, versi anyar Clang Static Analyzer wis sinau nemokake kesalahan anyar! Iku misale jek kula sing relevansi saka nggunakake PVS-Studio mudun. Clang nemokake luwih akeh kasalahan tinimbang sadurunge lan entuk kemampuan PVS-Studio. Apa sampeyan mikir babagan iki?
Kanggo iki aku tansah pengin mangsuli kaya:
Kita uga ora njagong! Kita wis nambah kemampuan analisa PVS-Studio kanthi signifikan. Dadi aja kuwatir, kita terus mimpin kaya sadurunge.
Sayange, iki jawaban sing ala. Ora ana buktine. Lan mulane aku nulis artikel iki saiki. Dadi, proyek LLVM wis dicenthang maneh lan macem-macem kesalahan wis ditemokake. Saiki aku bakal nduduhake sing katon menarik kanggo aku. Clang Static Analyzer ora bisa nemokake kesalahan kasebut (utawa pancen ora trep kanggo nindakake kanthi bantuan). Nanging kita bisa. Kajaba iku, aku nemokake lan nulis kabeh kesalahan kasebut ing wayah sore.
Nanging nulis artikel njupuk sawetara minggu. Aku mung ora bisa nggawa kabeh iki menyang teks :).
Miturut cara, yen sampeyan kasengsem karo teknologi apa sing digunakake ing penganalisis PVS-Studio kanggo ngenali kesalahan lan kerentanan potensial, mula aku saranake kenal karo iki.
Diagnosa anyar lan lawas
Kaya sing wis dingerteni, udakara rong taun kepungkur proyek LLVM dicenthang maneh, lan kesalahan sing ditemokake didandani. Saiki artikel iki bakal nampilake akeh kesalahan anyar. Kenapa bug anyar ditemokake? Ana 3 alasan kanggo iki:
- Proyek LLVM berkembang, ngganti kode lawas lan nambah kode anyar. Alami, ana kesalahan anyar ing kode sing diowahi lan ditulis. Iki jelas nuduhake yen analisis statis kudu digunakake kanthi rutin, lan ora sok-sok. Artikel kita nuduhake kemampuan analisa PVS-Studio, nanging iki ora ana hubungane karo nambah kualitas kode lan nyuda biaya ndandani kesalahan. Gunakake analisa kode statis kanthi rutin!
- Kita ngrampungake lan nambah diagnostik sing wis ana. Mulane, analisa bisa ngenali kasalahan sing ora diweruhi nalika scan sadurunge.
- Diagnosa anyar wis muncul ing PVS-Studio sing ora ana 2 taun kepungkur. Aku mutusaké kanggo nyorot ing bagean kapisah kanggo cetha nuduhake pangembangan PVS-Studio.
Cacat sing diidentifikasi kanthi diagnostik sing ana 2 taun kepungkur
Fragmen N1: Salin-Tempel
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 warning:
Dipriksa kaping pindho yen jeneng kasebut diwiwiti kanthi substring "avx512.mask.permvar.". Ing mriksa kapindho, padha temenan wanted kanggo nulis liyane, nanging lali kanggo mbenerake teks salinan.
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;
....
}
Warning PVS-Studio: V501 Ana sub-ekspresi sing padha 'CXNameRange_WantQualifier' ing sisih kiwa lan tengen '|' operator. CIindex.cpp 7245
Amarga kesalahan ketik, konstanta jeneng sing padha digunakake kaping pindho CXNameRange_WantQualifier.
Fragmen N3: Kebingungan karo precedence operator
int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
....
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
return 0;
....
}
PVS-Studio warning:
Miturut pendapatku, iki minangka kesalahan sing apik banget. Ya, aku ngerti aku duwe gagasan aneh babagan kaendahan :).
Saiki, miturut
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0
Saka sudut pandang praktis, kondisi kasebut ora ana gunane, amarga bisa dikurangi dadi:
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())
Iki minangka kesalahan sing jelas. Paling kamungkinan, dheweke pengin mbandhingake 0/1 karo variabel Index. Kanggo ndandani kode sampeyan kudu nambah kurung ing operator ternary:
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))
Miturut cara, operator ternary banget mbebayani lan provokes kesalahan logis. Ati-ati banget lan aja srakah nganggo tanda kurung. Aku nyawang topik iki kanthi luwih rinci
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;
}
....
}
PVS-Studio warning:
Yen pitunjuk LHS iku null, bebaya kudu ditanggepi. Nanging, tinimbang, pointer null sing padha iki bakal dibatalake: LHS->getAsString().
Iki minangka kahanan sing khas nalika kesalahan didhelikake ing panangan kesalahan, amarga ora ana sing nyoba. Analisa statis mriksa kabeh kode sing bisa digayuh, sanajan asring digunakake. Iki minangka conto sing apik banget babagan analisis statis nglengkapi teknik uji coba lan proteksi kesalahan liyane.
Kesalahan penanganan pointer sing padha RHS diijini ing kode ing ngisor iki: V522 [CWE-476] Dereferencing saka null pitunjuk 'RHS' bisa njupuk Panggonan. TGParser.cpp 2186
Fragmen N6: Nggunakake pointer sawise obah
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 Warning: V522 [CWE-476] Dereferencing saka null pointer 'ProgClone' bisa uga dumadi. Salah kompilasi.cpp 601
Ing wiwitan pointer pinter ProgClone mandheg duwe obyek kasebut:
BD.setNewProgram(std::move(ProgClone));
Nyatane, saiki ProgClone punika null pointer. Mulane, dereferensi null pointer kudu kedadeyan ing ngisor iki:
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
Nanging, ing kasunyatan, iki ora bakal kelakon! Elinga yen loop ora bener dieksekusi.
Ing wiwitan wadhah Fungsi miscompiled diresiki:
MiscompiledFunctions.clear();
Sabanjure, ukuran wadhah iki digunakake ing kondisi loop:
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
Iku gampang kanggo ndeleng sing daur ulang ora miwiti. Aku iki uga bug lan kode kudu ditulis beda.
Iku misale jek sing kita wis pinanggih sing podo misuwur saka kasalahan! Salah siji topeng liyane :).
Fragmen N7: Nggunakake pointer sawise obah
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 warning: V522 [CWE-476] Dereferencing saka null pointer 'Test' bisa njupuk Panggonan. Salah kompilasi.cpp 709
Kahanan sing padha maneh. Kaping pisanan, isi obyek kasebut dipindhah, banjur digunakake kaya-kaya ora ana kedadeyan. Aku ndeleng kahanan iki luwih asring ing kode program sawise semantik gerakan muncul ing C ++. Iki sebabe aku seneng basa C++! Ana liyane lan liyane cara anyar kanggo njupuk sikil dhewe mati. Analisa PVS-Studio mesthi duwe karya :).
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 saka null pointer 'Tipe' bisa njupuk Panggonan. PrettyFunctionDumper.cpp 233
Saliyane panangan kesalahan, debugging fungsi printout biasane ora dites. Kita duwe kasus kaya ngono sadurunge. Fungsi kasebut nunggu pangguna, sing, tinimbang ngrampungake masalahe, bakal dipeksa kanggo ndandani.
Bener:
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) // <=
PrintFatalError(Twine("Field '") + Field.Name + "' of table '" +
Table.Name + "' has incompatible type: " +
Ty->getAsString() + " vs. " + // <=
TI->getType()->getAsString());
....
}
PVS-Studio warning: V522 [CWE-476] Dereferencing saka null pointer 'Ty' bisa njupuk Panggonan. SearchableTableEmitter.cpp 614
Aku kabeh wis jelas lan ora mbutuhake panjelasan.
Fragmen N10: Typo
bool FormatTokenLexer::tryMergeCSharpNullConditionals() {
....
auto &Identifier = *(Tokens.end() - 2);
auto &Question = *(Tokens.end() - 1);
....
Identifier->ColumnWidth += Question->ColumnWidth;
Identifier->Type = Identifier->Type; // <=
Tokens.erase(Tokens.end() - 1);
return true;
}
PVS-Studio warning:
Ora ana gunane nemtokake variabel kanggo awake dhewe. Paling kamungkinan, dheweke pengin nulis:
Identifier->Type = Question->Type;
Fragmen N11: Putus curiga
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 warning:
Ana operator banget curiga ing wiwitan break. Apa sampeyan lali nulis liyane ing kene?
Fragmen N12: Priksa pointer sawise 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");
....
}
PVS-Studio warning:
Pointer Callee ing wiwitan dereferenced ing wektu fungsi disebut njalukTTI.
Banjur dadi metu sing pointer iki kudu dicenthang kanggo podo nullptr:
if (!Callee || Callee->isDeclaration())
Nanging wis telat…
Fragmen N13 - N...: Priksa pointer sawise dereferencing
Kahanan sing dibahas ing fragmen kode sadurunge ora unik. Iku katon ing kene:
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 warning: V595 [CWE-476] Pointer 'CalleeFn' digunakake sadurunge diverifikasi marang nullptr. Priksa baris: 1079, 1081. SimplifyLibCalls.cpp 1079
Lan ing kene:
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 warning: V595 [CWE-476] Pointer 'ND' digunakake sadurunge diverifikasi marang nullptr. Priksa baris: 532, 534. SemaTemplateInstantiateDecl.cpp 532
Lan ing kene:
- V595 [CWE-476] Pointer 'U' digunakake sadurunge diverifikasi marang nullptr. Priksa baris: 404, 407. DWARFormValue.cpp 404
- V595 [CWE-476] Pointer 'ND' digunakake sadurunge diverifikasi marang nullptr. Priksa baris: 2149, 2151. SemaTemplateInstantiate.cpp 2149
Banjur aku dadi ora kasengsem sinau babagan peringatan kanthi nomer V595. Dadi aku ora ngerti yen ana kesalahan liyane sing padha kajaba sing kadhaptar ing kene. Paling kamungkinan ana.
Fragmen N17, N18: Shift curiga
static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
uint64_t &Encoding) {
....
unsigned Size = RegSize;
....
uint64_t NImms = ~(Size-1) << 1;
....
}
PVS-Studio warning:
Bisa uga dudu bug lan kode kasebut bisa digunakake kanthi bener. Nanging iki jelas papan sing curiga lan kudu dipriksa.
Ayo dadi ngomong variabel Size witjaksono kanggo 16, lan banjur penulis kode ngrancang kanggo njaluk iku ing variabel NImms regane:
1111111111111111111111111111111111111111111111111111111111100000
Nanging, ing kasunyatan, asil bakal:
0000000000000000000000000000000011111111111111111111111111100000
Kasunyatane, kabeh kalkulasi ditindakake nggunakake jinis 32-bit unsigned. Lan mung banjur, jinis unsigned 32-bit iki bakal ditambahi implisit kanggo uint64_t. Ing kasus iki, bit sing paling penting bakal dadi nol.
Sampeyan bisa ndandani kahanan kaya iki:
uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
Kahanan sing padha: V629 [CWE-190] Coba mriksa ekspresi 'Immr << 6'. Pergeseran bit saka nilai 32-bit kanthi ekspansi sabanjure menyang jinis 64-bit. AArch64AddressingModes.h 269
Fragmen N19: Kata kunci sing ilang liya?
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 warning:
Ora ana kesalahan ing kene. Wiwit banjur-blok saka pisanan if mungkasi karo terus, banjur ora masalah, ana tembung kunci liya utawa ora. Salah siji cara kode bakal bisa digunakake padha. Isih kantun liya ndadekake kode luwih cetha lan mbebayani. Yen ing tembe terus ilang, kode bakal miwiti bisa rampung beda. Ing mratelakake panemume iku luwih apik kanggo nambah liya.
Fragmen N20: Sekawan typo saka jinis sing padha
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 bebaya:
- V655 [CWE-480] Senar padha concatenated nanging ora digunakake. Coba mriksa ekspresi 'Hasil + Name.str()'. Simbol.cpp 32
- V655 [CWE-480] Senar padha concatenated nanging ora digunakake. Coba mriksa ekspresi 'Hasil + "(Kelas ObjC)" + Name.str (). Simbol.cpp 35
- V655 [CWE-480] Senar padha concatenated nanging ora digunakake. Coba mriksa ekspresi 'Asil + "(ObjC Class EH) " + Name.str()'. Simbol.cpp 38
- V655 [CWE-480] Senar padha concatenated nanging ora digunakake. Coba mriksa ekspresi 'Asil + "(ObjC IVar)" + Name.str()'. Simbol.cpp 41
Ora sengaja, operator + digunakake tinimbang operator +=. Akibaté, desain sing ora ana makna.
Fragmen N21: Prilaku sing ora ditemtokake
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 golek dhewe kode mbebayani. Lan iki minangka gambar kanggo ngganggu perhatian supaya ora langsung ndeleng jawaban:
PVS-Studio warning:
Garis masalah:
FeaturesMap[Op] = FeaturesMap.size();
Yen unsur Op ora ditemokake, banjur unsur anyar digawe ing peta lan nomer unsur ing peta iki ditulis ana. Iku mung dingerteni apa fungsi bakal disebut ukuran sadurunge utawa sawise nambah unsur anyar.
Fragmen N22-N24: Tugas bola-bali
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 warning:
Aku ora mikir ana kesalahan nyata ing kene. Mung tugas bola-bali sing ora perlu. Nanging isih blunder.
Semono uga:
- V519 [CWE-563] Variabel 'B.NDesc' diwenehi nilai kaping pindho kanthi berturut-turut. Mbok menawa iki salah. Priksa baris: 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] Variabel kasebut diwenehi nilai kaping pindho kanthi berturut-turut. Mbok menawa iki salah. Priksa baris: 59, 61. coff2yaml.cpp 61
Fragmen N25-N27: Reassignments liyane
Saiki ayo goleki versi reassignment sing rada beda.
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' diwenehi nilai kaping pindho kanthi berturut-turut. Mbok menawa iki salah. Priksa baris: 1158, 1160. LoadStoreVectorizer.cpp 1160
Iki kode aneh banget sing ketoke ngandhut kesalahan logis. Ing wiwitan, variabel Alignment nilai diutus gumantung ing kondisi. Banjur tugas kasebut ditindakake maneh, nanging saiki tanpa mriksa.
Kahanan sing padha bisa dideleng ing kene:
- V519 [CWE-563] Variabel 'Efek' diwenehi nilai kaping pindho kanthi berturut-turut. Mbok menawa iki salah. Priksa baris: 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] Variabel 'ExpectNoDerefChunk' diwenehi nilai kaping pindho kanthi berturut-turut. Mbok menawa iki salah. Priksa baris: 4970, 4973. SemaType.cpp 4973
Fragmen N28: Tansah kahanan bener
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 warning:
Priksa ora ana gunane. Variabel sabanjureByte tansah ora padha karo regane 0x90, kang nderek saka mriksa sadurungé. Iki minangka sawetara kesalahan logis.
Fragmen N29 - N...: Kahanan sing tansah bener / salah
Analisa ngetokake akeh bebaya yen kabeh kondisi (
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 warning:
Konstanta 0xE yaiku nilai 14 ing desimal. Ujian RegNo == 0xe ora masuk akal amarga yen Nomer Reg > 13, banjur fungsi bakal ngrampungake eksekusi.
Ana akeh bebaya liyane karo ID V547 lan V560, nanging minangka karo
Aku bakal menehi conto kenapa nyinaoni pemicu kasebut mboseni. Analisa pancen bener nerbitake bebaya kanggo kode ing ngisor iki. Nanging iki ora salah.
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
tok::TokenKind ClosingBraceKind) {
bool HasError = false;
....
HasError = true;
if (!ContinueOnSemicolons)
return !HasError;
....
}
PVS-Studio Warning: V547 [CWE-570] Expression '!HasError' tansah palsu. UnwrappedLineParser.cpp 1635
Fragmen N30: bali curiga
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 warning:
Iki salah siji kesalahan utawa technique tartamtu sing dimaksudaké kanggo nerangake soko kanggo programer maca kode. Desain iki ora nerangake apa-apa kanggo kula lan katon banget curiga. Luwih becik ora nulis kaya ngono :).
kesel? Banjur wektu kanggo nggawe teh utawa kopi.
Cacat sing diidentifikasi kanthi diagnostik anyar
Aku 30 aktifitas diagnostik lawas cukup. Saiki ayo ndeleng apa sing menarik sing bisa ditemokake karo diagnosa anyar sing muncul ing penganalisis
Fragmen N31: Kode ora bisa digayuh
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 warning:
Nalika sampeyan bisa ndeleng, loro cabang saka operator if dipungkasi karo telpon menyang operator bali. Patut, wadhah CtorDtorsByPriority ora bakal diresiki.
Fragmen N32: Kode ora bisa digayuh
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 warning: V779 [CWE-561] kode unreachable dideteksi. Bisa uga ana kesalahan. LLParser.cpp 835
Kahanan sing menarik. Ayo ndeleng panggonan iki dhisik:
return ParseTypeIdEntry(SummaryID);
break;
Sepisanan, kayane ora ana kesalahan ing kene. Iku katon kaya operator break ana tambahan ing kene, lan sampeyan mung bisa mbusak. Nanging, ora kabeh supaya prasaja.
Analisa nerbitake bebaya ing baris:
Lex.setIgnoreColonInIdentifiers(false);
return false;
Lan pancen, kode iki ora bisa digayuh. Kabeh kasus ing ngalih rampung karo telpon saka operator bali. Lan saiki ora duwe akal dhewe break ora katon mbebayani banget! Mbok siji saka cabang kudu mungkasi karo break, ora ing bali?
Fragmen N33: Reset acak saka bit dhuwur
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:
Wigati dimangerteni yen fungsi kasebut getStubAlignment jinis bali ditandatangani. Ayo ngetung nilai ekspresi kasebut, kanthi nganggep yen fungsi kasebut ngasilake nilai 8:
~(getStubAlignment() - 1)
~(8u-1)
0xFFFFFFFF8u
Saiki sok dong mirsani sing variabel Ukuran Data nduweni jinis unsigned 64-bit. Pranyata nalika nindakake operasi DataSize & 0xFFFFFFF8u, kabeh telung puluh loro bit urutan dhuwur bakal direset menyang nol. Paling kamungkinan, iki ora dikarepake programmer. Aku curiga yen dheweke pengin ngetung: DataSize & 0xFFFFFFFFFFFFFFFF8u.
Kanggo ndandani kesalahan, sampeyan kudu nulis iki:
DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);
Utawa:
DataSize &= ~(getStubAlignment() - 1ULL);
Fragmen N34: Gagal jinis eksplisit cast
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 warning:
Casting jinis eksplisit digunakake kanggo ngindhari kebanjiran nalika multiply jinis variabel int. Nanging, casting jinis eksplisit ing kene ora nglindhungi saka kebanjiran. Pisanan, variabel bakal dikalikan, lan mung asil 32-bit saka perkalian bakal ditambahi dadi jinis.
Fragmen N35: Gagal Nyalin-Tempel
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;
}
....
}
Diagnosa menarik anyar iki ngenali kahanan ing ngendi potongan kode wis disalin lan sawetara jeneng ing iku wis wiwit diganti, nanging ing sak panggonan padha ora didandani.
Wigati dimangerteni menawa ing blok kapindho padha diganti Op0 ing Op1. Nanging ing sak panggonan padha ora ndandani. Paling kamungkinan, kudu ditulis kaya iki:
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
return &I;
}
Fragmen N36: Kebingungan 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:
Mbebayani banget kanggo menehi argumen fungsi kanthi jeneng sing padha karo anggota kelas. Gampang banget bingung. Kita duwe kasus kaya ngono sadurunge. Ekspresi iki ora masuk akal:
Mode &= Mask;
Argumen fungsi diganti. Mekaten. Argumentasi iki ora digunakake maneh. Paling kamungkinan, sampeyan kudu nulis kaya iki:
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
this->Mode &= Mask;
};
Fragmen N37: Kebingungan 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;
}
Warning PVS-Studio: V1001 [CWE-563] Variabel 'Ukuran' diutus nanging ora digunakake ing mburi fungsi. Object.cpp 424
Kahanane padha karo sing sadurunge. Iku kudu ditulis:
this->Size += this->EntrySize;
Fragmen N38-N47: Padha kelalen mriksa indeks
Sadurunge, kita ndeleng conto pemicu diagnostik
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 warning: V1004 [CWE-476] Pointer 'Ptr' digunakake ora aman sawise diverifikasi marang nullptr. Priksa baris: 729, 738. TargetTransformInfoImpl.h 738
Variabel Ptr bisa uga padha nullptr, minangka bukti saka mriksa:
if (Ptr != nullptr)
Nanging, ing ngisor pointer iki ora ana referensi tanpa pamriksan awal:
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());
Ayo nimbang kasus liyane sing padha.
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 warning: V1004 [CWE-476] Pointer 'FD' digunakake ora aman sawise diverifikasi marang nullptr. Priksa baris: 3228, 3231. CGDebugInfo.cpp 3231
Pay manungsa waé kanggo tandha FD. Aku yakin masalah kasebut katon jelas lan ora ana panjelasan khusus sing dibutuhake.
Lan luwih:
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 warning: V1004 [CWE-476] Pointer 'PtrTy' digunakake ora aman sawise diverifikasi marang nullptr. Priksa baris: 960, 965. InterleavedLoadCombinePass.cpp 965
Kepiye carane nglindhungi dhewe saka kesalahan kasebut? Dadi luwih ati-ati babagan Code-Review lan gunakake penganalisa statis PVS-Studio kanggo mriksa kode sampeyan kanthi rutin.
Ora ana gunane ngutip fragmen kode liyane kanthi kesalahan jinis iki. Aku mung bakal ninggalake dhaptar bebaya ing artikel:
- V1004 [CWE-476] Pointer 'Expr' digunakake kanthi ora aman sawise diverifikasi marang nullptr. Priksa baris: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] Pitunjuk 'PI' digunakake kanthi ora aman sawise diverifikasi marang nullptr. Priksa baris: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] Pointer 'StatepointCall' digunakake kanthi ora aman sawise diverifikasi marang nullptr. Priksa baris: 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] Pointer 'RV' digunakake ora aman sawise diverifikasi marang nullptr. Priksa baris: 2263, 2268. TGParser.cpp 2268
- V1004 [CWE-476] Pointer 'CalleeFn' digunakake kanthi ora aman sawise diverifikasi marang nullptr. Priksa baris: 1081, 1096. SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] Pointer 'TC' digunakake ora aman sawise diverifikasi marang nullptr. Priksa baris: 1819, 1824. Driver.cpp 1824
Fragmen N48-N60: Ora kritis, nanging cacat (kemungkinan bocor memori)
std::unique_ptr<IRMutator> createISelMutator() {
....
std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
Strategies.emplace_back(
new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
....
}
PVS-Studio warning:
Kanggo nambah unsur menyang mburi wadhah kaya std::vektor > sampeyan ora bisa mung nulis xxx.push_back(X anyar), amarga ora ana konversi implisit saka X* в std::unique_ptr.
Solusi umum yaiku nulis xxx.emplace_back(X anyar)awit iku kompilasi: cara emplace_back mbangun unsur langsung saka argumen lan mulane bisa nggunakake konstruktor eksplisit.
Iku ora aman. Yen vektor kebak, banjur memori diparengake maneh. Operasi realokasi memori bisa uga gagal, nyebabake pangecualian dibuwang std::bad_alloc. Ing kasus iki, pointer bakal ilang lan obyek sing digawe ora bakal dibusak.
Solusi sing aman yaiku nggawe unik_ptrsing bakal duwe pointer sadurunge vektor nyoba relokasi memori:
xxx.push_back(std::unique_ptr<X>(new X))
Wiwit C++ 14, sampeyan bisa nggunakake 'std::make_unique':
xxx.push_back(std::make_unique<X>())
Cacat jinis iki ora kritis kanggo LLVM. Yen memori ora bisa dialokasikan, compiler mung bakal mandheg. Nanging, kanggo aplikasi karo dawa
Dadi, sanajan kode iki ora nuduhke ancaman praktis kanggo LLVM, Aku ketemu iku migunani kanggo pirembagan bab pola kesalahan iki lan analyzer PVS-Studio wis sinau kanggo ngenali.
Bebaya liyane saka jinis iki:
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Passes' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. PassManager.h 546
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'AAs' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. AliasAnalysis.h 324
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Entri' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. DWARFDebugFrame.cpp 519
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'AllEdges' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. CFGMST.h 268
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'VMaps' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Records' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. FDRLogBuilder.h 30
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'PendingSubmodules' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. ModuleMap.cpp 810
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Obyek' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. DebugMap.cpp 88
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Strategi' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 685
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 686
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 688
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 689
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 690
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 691
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 692
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 693
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Modifiers' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. llvm-stress.cpp 694
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Operand' kanthi metode 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] Pointer tanpa pemilik ditambahake menyang wadhah 'Stash' kanthi cara 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] A pointer tanpa pemilik ditambahake menyang wadhah 'Matchers' kanthi cara 'emplace_back'. Kebocoran memori bakal kedadeyan yen ana pangecualian. GlobalISelEmitter.cpp 2702
kesimpulan
Aku ngetokake 60 bebaya kanthi total lan banjur mandheg. Apa ana cacat liyane sing dideteksi PVS-Studio analyzer ing LLVM? Ya wis. Nanging, nalika aku nulis fragmen kode kanggo artikel kasebut, wis sore, utawa malah wengi, lan aku mutusake yen wis wayahe nyebat dina.
Muga-muga sampeyan nemokake iku menarik lan pengin nyoba analisa PVS-Studio.
Sampeyan bisa ngundhuh analyzer lan njaluk tombol mineweeper ing
Sing paling penting, gunakake analisis statis kanthi rutin. Priksa siji-wektu, digawa metu dening kita supaya popularize metodologi analisis statis lan PVS-Studio ora skenario normal.
Good luck kanggo nambah kualitas lan linuwih kode!
Yen sampeyan pengin nuduhake artikel iki karo pamirsa sing nganggo basa Inggris, gunakake tautan terjemahan: Andrey Karpov.
Source: www.habr.com