Đã hơn hai năm trôi qua kể từ lần kiểm tra mã cuối cùng của dự án LLVM bằng máy phân tích PVS-Studio của chúng tôi. Hãy đảm bảo rằng máy phân tích PVS-Studio vẫn là công cụ hàng đầu để xác định lỗi và các lỗ hổng tiềm ẩn. Để thực hiện việc này, chúng tôi sẽ kiểm tra và tìm ra các lỗi mới trong bản phát hành LLVM 8.0.0.
Bài viết sắp viết
Thành thật mà nói, tôi không muốn viết bài này. Thật không thú vị khi viết về một dự án mà chúng tôi đã kiểm tra nhiều lần (
Mỗi khi có phiên bản LLVM mới được phát hành hoặc cập nhật
Hãy nhìn xem, phiên bản mới của Clang Static Phân tích đã học cách tìm ra các lỗi mới! Đối với tôi, có vẻ như mức độ liên quan của việc sử dụng PVS-Studio đang giảm dần. Clang tìm thấy nhiều lỗi hơn trước và bắt kịp khả năng của PVS-Studio. Bạn nghĩ gì về điều này?
Về vấn đề này tôi luôn muốn trả lời một câu như:
Chúng tôi cũng không ngồi yên! Chúng tôi đã cải thiện đáng kể khả năng của máy phân tích PVS-Studio. Vậy nên đừng lo lắng, chúng ta vẫn tiếp tục dẫn đầu như trước.
Thật không may, đây là một câu trả lời tồi. Không có bằng chứng nào trong đó. Và đó là lý do tại sao bây giờ tôi viết bài này. Vì vậy, dự án LLVM một lần nữa đã được kiểm tra và nhiều lỗi đã được tìm thấy trong đó. Bây giờ tôi sẽ chứng minh những điều mà tôi thấy thú vị. Trình phân tích tĩnh Clang không thể tìm thấy những lỗi này (hoặc cực kỳ bất tiện khi làm như vậy với sự trợ giúp của nó). Nhưng chúng ta có thể. Hơn nữa, tôi đã tìm ra và ghi lại tất cả những lỗi này chỉ trong một buổi tối.
Nhưng việc viết bài phải mất vài tuần. Tôi không thể tự mình viết tất cả những điều này thành văn bản :).
Nhân tiện, nếu bạn quan tâm đến công nghệ nào được sử dụng trong máy phân tích PVS-Studio để xác định lỗi và các lỗ hổng tiềm ẩn, thì tôi khuyên bạn nên làm quen với điều này
Chẩn đoán mới và cũ
Như đã lưu ý, khoảng hai năm trước, dự án LLVM đã được kiểm tra một lần nữa và các lỗi tìm thấy đã được sửa chữa. Bây giờ bài viết này sẽ trình bày một loạt lỗi mới. Tại sao lỗi mới được tìm thấy? Có 3 lý do cho việc này:
- Dự án LLVM đang phát triển, thay đổi mã cũ và thêm mã mới. Đương nhiên, có những lỗi mới trong mã được sửa đổi và viết. Điều này chứng tỏ rõ ràng rằng phân tích tĩnh nên được sử dụng thường xuyên chứ không phải thỉnh thoảng. Các bài viết của chúng tôi thể hiện rõ khả năng của máy phân tích PVS-Studio, nhưng điều này không liên quan gì đến việc cải thiện chất lượng mã và giảm chi phí sửa lỗi. Sử dụng máy phân tích mã tĩnh thường xuyên!
- Chúng tôi đang hoàn thiện và cải thiện chẩn đoán hiện có. Do đó, máy phân tích có thể xác định các lỗi mà nó không nhận thấy trong các lần quét trước.
- Chẩn đoán mới đã xuất hiện trong PVS-Studio mà cách đây 2 năm không tồn tại. Tôi quyết định nêu bật chúng trong một phần riêng để thể hiện rõ sự phát triển của PVS-Studio.
Các khiếm khuyết được xác định bằng chẩn đoán đã tồn tại 2 năm trước
Đoạn N1: Sao chép-Dán
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
....
}
Cảnh báo của PVS-Studio:
Kiểm tra kỹ xem tên có bắt đầu bằng chuỗi con "avx512.mask.permvar." hay không. Trong lần kiểm tra thứ hai, rõ ràng họ muốn viết gì đó khác nhưng lại quên sửa văn bản đã sao chép.
Đoạn N2: Lỗi đánh máy
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;
....
}
Cảnh báo PVS-Studio: V501 Có các biểu thức phụ giống hệt nhau 'CXNameRange_WantQualifier' ở bên trái và bên phải của '|' nhà điều hành. CIndex.cpp 7245
Do lỗi đánh máy, hằng số có tên giống nhau được sử dụng hai lần CXNameRange_WantQualifier.
Đoạn N3: Nhầm lẫn về độ ưu tiên của toán tử
int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
....
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
return 0;
....
}
Cảnh báo của PVS-Studio:
Theo tôi, đây là một sai lầm rất đẹp. Vâng, tôi biết tôi có những ý tưởng kỳ lạ về cái đẹp :).
Bây giờ, theo
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0
Từ quan điểm thực tế, điều kiện như vậy không có ý nghĩa vì nó có thể được rút gọn thành:
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())
Đây là một sai lầm rõ ràng. Rất có thể, họ muốn so sánh 0/1 với một biến Chỉ số. Để sửa mã, bạn cần thêm dấu ngoặc đơn xung quanh toán tử ternary:
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))
Nhân tiện, toán tử bậc ba rất nguy hiểm và gây ra các lỗi logic. Hãy thật cẩn thận với nó và đừng tham lam với dấu ngoặc đơn. Tôi đã xem xét chủ đề này chi tiết hơn
Đoạn N4, N5: Con trỏ null
Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
....
TypedInit *LHS = dyn_cast<TypedInit>(Result);
....
LHS = dyn_cast<TypedInit>(
UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get())
->Fold(CurRec));
if (!LHS) {
Error(PasteLoc, Twine("can't cast '") + LHS->getAsString() +
"' to string");
return nullptr;
}
....
}
Cảnh báo của PVS-Studio:
Nếu con trỏ LHS là null, một cảnh báo sẽ được đưa ra. Tuy nhiên, thay vào đó, con trỏ null này sẽ bị hủy đăng ký: LHS->getAsString().
Đây là một tình huống rất điển hình khi một lỗi được ẩn trong trình xử lý lỗi vì không ai kiểm tra chúng. Máy phân tích tĩnh kiểm tra tất cả các mã có thể truy cập được, bất kể nó được sử dụng thường xuyên như thế nào. Đây là một ví dụ rất hay về cách phân tích tĩnh bổ sung cho các kỹ thuật kiểm tra và bảo vệ lỗi khác.
Lỗi xử lý con trỏ tương tự RHS được phép trong mã ngay bên dưới: V522 [CWE-476] Việc hủy tham chiếu con trỏ null 'RHS' có thể diễn ra. TGParser.cpp 2186
Đoạn N6: Sử dụng con trỏ sau khi di chuyển
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);
}
....
}
Cảnh báo PVS-Studio: V522 [CWE-476] Việc hủy tham chiếu con trỏ null 'ProgClone' có thể diễn ra. Biên dịch sai.cpp 601
Lúc đầu một con trỏ thông minh ProgClone ngừng sở hữu đối tượng:
BD.setNewProgram(std::move(ProgClone));
Trên thực tế, bây giờ ProgClone là một con trỏ null. Do đó, việc hủy bỏ tham chiếu con trỏ null sẽ xảy ra ngay bên dưới:
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
Nhưng trên thực tế, điều này sẽ không xảy ra! Lưu ý rằng vòng lặp không thực sự được thực thi.
Ở đầu thùng chứa Hàm biên dịch sai đã xóa:
MiscompiledFunctions.clear();
Tiếp theo, kích thước của vùng chứa này được sử dụng trong điều kiện vòng lặp:
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
Thật dễ dàng để thấy rằng vòng lặp không bắt đầu. Tôi nghĩ đây cũng là một lỗi và mã nên được viết khác đi.
Có vẻ như chúng ta đã gặp phải lỗi tương đương nổi tiếng đó! Một sai lầm che giấu một sai lầm khác :).
Đoạn N7: Sử dụng con trỏ sau khi di chuyển
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;
}
....
}
Cảnh báo của PVS-Studio: V522 [CWE-476] Việc hủy tham chiếu con trỏ null 'Thử nghiệm' có thể diễn ra. Biên dịch sai.cpp 709
Tình huống tương tự một lần nữa. Lúc đầu, nội dung của đối tượng được di chuyển, sau đó nó được sử dụng như không có chuyện gì xảy ra. Tôi thấy tình trạng này ngày càng thường xuyên hơn trong mã chương trình sau khi ngữ nghĩa chuyển động xuất hiện trong C++. Đây là lý do tại sao tôi yêu thích ngôn ngữ C++! Ngày càng có nhiều cách mới để tự bắn đứt chân mình. Máy phân tích PVS-Studio sẽ luôn hoạt động :).
Đoạn N8: Con trỏ null
void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
uint32_t TypeId = Symbol.getTypeId();
auto Type = Symbol.getSession().getSymbolById(TypeId);
if (Type)
Printer << "<unknown-type>";
else
Type->dump(*this);
}
Cảnh báo PVS-Studio: V522 [CWE-476] Việc hủy tham chiếu con trỏ null 'Loại' có thể diễn ra. PrettyFunctionDumper.cpp 233
Ngoài các trình xử lý lỗi, chức năng gỡ lỗi in ra thường không được kiểm tra. Chúng tôi vừa có một trường hợp như vậy trước mắt. Chức năng này đang chờ người dùng, thay vì giải quyết vấn đề của mình, họ sẽ buộc phải sửa nó.
Chính xác:
if (Type)
Type->dump(*this);
else
Printer << "<unknown-type>";
Đoạn N9: Con trỏ null
void SearchableTableEmitter::collectTableEntries(
GenericTable &Table, const std::vector<Record *> &Items) {
....
RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
if (!Ty) // <=
PrintFatalError(Twine("Field '") + Field.Name + "' of table '" +
Table.Name + "' has incompatible type: " +
Ty->getAsString() + " vs. " + // <=
TI->getType()->getAsString());
....
}
Cảnh báo của PVS-Studio: V522 [CWE-476] Việc hủy tham chiếu con trỏ null 'Ty' có thể diễn ra. SearchableTableEmitter.cpp 614
Tôi nghĩ mọi thứ đều rõ ràng và không cần giải thích.
Đoạn N10: Lỗi đánh máy
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;
}
Cảnh báo của PVS-Studio:
Không có ích gì khi gán một biến cho chính nó. Rất có thể họ muốn viết:
Identifier->Type = Question->Type;
Đoạn N11: Đột phá đáng ngờ
void SystemZOperand::print(raw_ostream &OS) const {
switch (Kind) {
break;
case KindToken:
OS << "Token:" << getToken();
break;
case KindReg:
OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
break;
....
}
Cảnh báo của PVS-Studio:
Có một nhà điều hành rất đáng ngờ ngay từ đầu phá vỡ. Bạn quên viết gì khác ở đây à?
Đoạn N12: Kiểm tra con trỏ sau khi hội thảo
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");
....
}
Cảnh báo của PVS-Studio:
Con trỏ callee lúc đầu bị hủy đăng ký tại thời điểm hàm được gọi nhậnTTI.
Và sau đó hóa ra con trỏ này cần được kiểm tra sự bằng nhau nullptr:
if (!Callee || Callee->isDeclaration())
Nhưng quá trễ rồi…
Đoạn N13 - N...: Kiểm tra con trỏ sau khi hội thảo
Tình huống được thảo luận trong đoạn mã trước không phải là duy nhất. Nó xuất hiện ở đây:
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()) { // <=
....
}
Cảnh báo PVS-Studio: V595 [CWE-476] Con trỏ 'CalleeFn' đã được sử dụng trước khi nó được xác minh dựa trên nullptr. Kiểm tra các dòng: 1079, 1081. SimplifyLibCalls.cpp 1079
Và đây:
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()); // <=
....
}
Cảnh báo PVS-Studio: V595 [CWE-476] Con trỏ 'ND' đã được sử dụng trước khi nó được xác minh dựa trên nullptr. Kiểm tra các dòng: 532, 534. SemaTemplateInstantiateDecl.cpp 532
Và đây:
- V595 [CWE-476] Con trỏ 'U' đã được sử dụng trước khi nó được xác minh dựa trên nullptr. Kiểm tra các dòng: 404, 407. DWARFormValue.cpp 404
- V595 [CWE-476] Con trỏ 'ND' đã được sử dụng trước khi nó được xác minh dựa trên nullptr. Kiểm tra các dòng: 2149, 2151. SemaTemplateInstantiate.cpp 2149
Và rồi tôi không còn hứng thú nghiên cứu những cảnh báo mang số V595. Vì vậy, tôi không biết liệu có thêm lỗi tương tự nào ngoài những lỗi được liệt kê ở đây hay không. Nhiều khả năng là có.
Đoạn N17, N18: Chuyển dịch đáng ngờ
static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
uint64_t &Encoding) {
....
unsigned Size = RegSize;
....
uint64_t NImms = ~(Size-1) << 1;
....
}
Cảnh báo của PVS-Studio:
Nó có thể không phải là lỗi và mã hoạt động chính xác như dự định. Nhưng đây rõ ràng là một nơi rất đáng ngờ và cần phải kiểm tra.
Giả sử biến Kích thước máy bằng 16, và sau đó tác giả của đoạn mã đã lên kế hoạch đưa nó vào một biến NImm Ý nghĩa:
1111111111111111111111111111111111111111111111111111111111100000
Tuy nhiên, trên thực tế kết quả sẽ là:
0000000000000000000000000000000011111111111111111111111111100000
Thực tế là tất cả các phép tính đều diễn ra bằng cách sử dụng loại không dấu 32 bit. Và chỉ khi đó, loại không dấu 32 bit này mới được ngầm mở rộng thành uint64_t. Trong trường hợp này, các bit quan trọng nhất sẽ bằng XNUMX.
Bạn có thể khắc phục tình trạng như thế này:
uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
Tình huống tương tự: V629 [CWE-190] Hãy cân nhắc việc kiểm tra biểu thức 'Immr << 6'. Dịch chuyển bit của giá trị 32 bit với sự mở rộng tiếp theo sang loại 64 bit. AArch64AddressingModes.h 269
Đoạn N19: Thiếu từ khóa khác?
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");
}
....
}
Cảnh báo của PVS-Studio:
Không có sai lầm ở đây. Kể từ khối then của đầu tiên if kết thúc bằng tiếp tục, thế thì không sao đâu, có từ khóa mà khác hay không. Dù bằng cách nào thì mã sẽ hoạt động giống nhau. Vẫn nhớ khác làm cho mã trở nên không rõ ràng và nguy hiểm hơn. Nếu trong tương lai tiếp tục biến mất, mã sẽ bắt đầu hoạt động hoàn toàn khác. Theo tôi tốt hơn là thêm khác.
Đoạn N20: Bốn lỗi chính tả cùng loại
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;
}
Cảnh báo của PVS-Studio:
- V655 [CWE-480] Các dây đã được nối nhưng không được sử dụng. Hãy cân nhắc việc kiểm tra biểu thức 'Result + Name.str()'. Symbol.cpp 32
- V655 [CWE-480] Các dây đã được nối nhưng không được sử dụng. Hãy cân nhắc việc kiểm tra biểu thức 'Result + "(ObjC Class)" + Name.str()'. Symbol.cpp 35
- V655 [CWE-480] Các dây đã được nối nhưng không được sử dụng. Hãy cân nhắc việc kiểm tra biểu thức 'Result + "(ObjC Class EH) " + Name.str()'. Symbol.cpp 38
- V655 [CWE-480] Các dây đã được nối nhưng không được sử dụng. Hãy cân nhắc việc kiểm tra biểu thức 'Result + "(ObjC IVar)" + Name.str()'. Symbol.cpp 41
Tình cờ, toán tử + được sử dụng thay vì toán tử +=. Kết quả là những thiết kế không có ý nghĩa.
Đoạn N21: Hành vi không xác định
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();
}
}
}
Hãy cố gắng tự mình tìm ra mã nguy hiểm. Và đây là hình ảnh nhằm đánh lạc hướng sự chú ý để không nhìn ngay vào đáp án:
Cảnh báo của PVS-Studio:
Dòng vấn đề:
FeaturesMap[Op] = FeaturesMap.size();
Nếu phần tử Op không được tìm thấy thì một phần tử mới sẽ được tạo trên bản đồ và số lượng phần tử trong bản đồ này được ghi vào đó. Chỉ là chưa biết liệu hàm này có được gọi hay không kích thước trước hoặc sau khi thêm phần tử mới.
Đoạn N22-N24: Bài tập lặp lại
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;
}
....
}
Cảnh báo của PVS-Studio:
Tôi không nghĩ có sai lầm thực sự ở đây. Chỉ là một nhiệm vụ lặp đi lặp lại không cần thiết. Nhưng vẫn là một sai lầm.
Tương tự:
- V519 [CWE-563] Biến “B.NDesc” được gán giá trị hai lần liên tiếp. Có lẽ đây là một sai lầm. Kiểm tra các dòng: 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] Biến được gán giá trị hai lần liên tiếp. Có lẽ đây là một sai lầm. Kiểm tra các dòng: 59, 61. coff2yaml.cpp 61
Đoạn N25-N27: Thêm sự phân công lại
Bây giờ chúng ta hãy xem xét một phiên bản tái chỉ định hơi khác một chút.
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;
....
}
Cảnh báo PVS-Studio: V519 [CWE-563] Biến 'Alignment' được gán giá trị hai lần liên tiếp. Có lẽ đây là một sai lầm. Kiểm tra các dòng: 1158, 1160. LoadStoreVectorizer.cpp 1160
Đây là mã rất lạ dường như có lỗi logic. Lúc đầu, biến Alignment một giá trị được chỉ định tùy thuộc vào điều kiện. Và sau đó nhiệm vụ lại diễn ra, nhưng bây giờ không có bất kỳ sự kiểm tra nào.
Những tình huống tương tự có thể được nhìn thấy ở đây:
- V519 [CWE-563] Biến 'Effects' được gán giá trị hai lần liên tiếp. Có lẽ đây là một sai lầm. Kiểm tra các dòng: 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] Biến 'ExpectNoDerefChunk' được gán giá trị hai lần liên tiếp. Có lẽ đây là một sai lầm. Kiểm tra các dòng: 4970, 4973. SemaType.cpp 4973
Đoạn N28: Điều kiện luôn đúng
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;
}
....
}
Cảnh báo của PVS-Studio:
Kiểm tra không có ý nghĩa. Biến đổi nextByte luôn không bằng giá trị 0x90, theo sau lần kiểm tra trước. Đây là một số loại lỗi logic.
Đoạn N29 - N...: Luôn có điều kiện đúng/sai
Máy phân tích đưa ra nhiều cảnh báo rằng toàn bộ tình trạng (
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;
....
}
Cảnh báo của PVS-Studio:
Hằng số 0xE là giá trị 14 ở dạng thập phân. Bài kiểm tra RegNo == 0xe không có ý nghĩa gì vì nếu RegNo > 13, thì hàm sẽ hoàn thành việc thực thi của nó.
Có nhiều cảnh báo khác với ID V547 và V560, nhưng cũng như với
Tôi sẽ cho bạn một ví dụ giải thích tại sao việc nghiên cứu những tác nhân này lại nhàm chán. Trình phân tích hoàn toàn đúng khi đưa ra cảnh báo cho đoạn mã sau. Nhưng đây không phải là một sai lầm.
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
tok::TokenKind ClosingBraceKind) {
bool HasError = false;
....
HasError = true;
if (!ContinueOnSemicolons)
return !HasError;
....
}
Cảnh báo PVS-Studio: V547 [CWE-570] Biểu thức '!HasError' luôn sai. UnwrappedLineParser.cpp 1635
Đoạn N30: Sự trở lại đáng ngờ
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();
}
....
}
Cảnh báo của PVS-Studio:
Đây có thể là một lỗi hoặc một kỹ thuật cụ thể nhằm giải thích điều gì đó cho người lập trình đọc mã. Thiết kế này không giải thích được điều gì với tôi và trông rất đáng ngờ. Tốt nhất đừng viết như vậy :).
Mệt? Sau đó là lúc pha trà hoặc cà phê.
Khiếm khuyết được xác định bằng chẩn đoán mới
Tôi nghĩ 30 lần kích hoạt chẩn đoán cũ là đủ. Bây giờ chúng ta hãy xem những điều thú vị có thể được tìm thấy với chẩn đoán mới xuất hiện trong máy phân tích sau
Đoạn N31: Mã không thể truy cập được
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();
}
Cảnh báo của PVS-Studio:
Như bạn có thể thấy, cả hai nhánh của toán tử if kết thúc bằng một cuộc gọi đến nhà điều hành trở lại. Theo đó, thùng chứa CtorDtorsByPriority sẽ không bao giờ được xóa sạch.
Đoạn N32: Mã không thể truy cập được
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;
}
Cảnh báo PVS-Studio: V779 [CWE-561] Đã phát hiện mã không thể truy cập. Có thể có lỗi. LLParser.cpp 835
Tình huống thú vị. Trước tiên chúng ta hãy nhìn vào nơi này:
return ParseTypeIdEntry(SummaryID);
break;
Thoạt nhìn có vẻ như không có sai sót nào ở đây. Có vẻ như người điều hành phá vỡ có một cái bổ sung ở đây và bạn chỉ cần xóa nó đi. Tuy nhiên, không phải tất cả đều đơn giản như vậy.
Máy phân tích đưa ra cảnh báo trên các dòng:
Lex.setIgnoreColonInIdentifiers(false);
return false;
Và thực sự, mã này không thể truy cập được. Tất cả các trường hợp trong chuyển đổi kết thúc bằng cuộc gọi từ nhà điều hành trở lại. Và giờ đây cô đơn vô nghĩa phá vỡ trông không có vẻ vô hại cho lắm! Có lẽ một trong những nhánh nên kết thúc bằng phá vỡnhưng không phải trên trở lại?
Đoạn N33: Thiết lập lại ngẫu nhiên các bit cao
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);
....
}
Cảnh báo của PVS-Studio:
Xin lưu ý rằng chức năng getStubAlignment kiểu trả về unsigned. Hãy tính giá trị của biểu thức, giả sử hàm trả về giá trị 8:
~(getStubAlignment() - 1)
~(8u-1)
0xFFFFFFFF8u
Bây giờ hãy chú ý rằng biến Kích thước dữ liệu có loại không dấu 64-bit. Hóa ra là khi thực hiện thao tác DataSize & 0xFFFFFFF8u, tất cả 0 bit bậc cao sẽ được đặt lại về 8. Rất có thể, đây không phải là điều mà lập trình viên mong muốn. Tôi nghi ngờ rằng anh ấy muốn tính toán: DataSize & XNUMXxFFFFFFFFFFFFFFFFXNUMXu.
Để sửa lỗi, bạn nên viết thế này:
DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);
Hay như vậy:
DataSize &= ~(getStubAlignment() - 1ULL);
Đoạn N34: Truyền kiểu rõ ràng không thành công
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);
....
}
Cảnh báo của PVS-Studio:
Truyền kiểu rõ ràng được sử dụng để tránh tràn khi nhân các biến kiểu int. Tuy nhiên, việc truyền kiểu rõ ràng ở đây không bảo vệ chống tràn. Đầu tiên, các biến sẽ được nhân và chỉ khi đó kết quả 32 bit của phép nhân mới được mở rộng thành loại
Đoạn N35: Sao chép-Dán không thành công
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;
}
....
}
Chẩn đoán thú vị mới này xác định các tình huống trong đó một đoạn mã đã được sao chép và một số tên trong đó đã bắt đầu bị thay đổi, nhưng ở một chỗ họ chưa sửa nó.
Xin lưu ý rằng trong khối thứ hai họ đã thay đổi Op0 trên Op1. Nhưng có một chỗ họ không sửa được. Rất có thể nó phải được viết như thế này:
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
return &I;
}
Đoạn N36: Sự nhầm lẫn có thể thay đổi
struct Status {
unsigned Mask;
unsigned Mode;
Status() : Mask(0), Mode(0){};
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
Mode &= Mask;
};
....
};
Cảnh báo của PVS-Studio:
Sẽ rất nguy hiểm nếu đặt cho các đối số hàm cùng tên với các thành viên của lớp. Rất dễ bị nhầm lẫn. Chúng tôi vừa có một trường hợp như vậy trước mắt. Biểu thức này không có ý nghĩa:
Mode &= Mask;
Đối số chức năng thay đổi. Đó là tất cả. Đối số này không còn được sử dụng. Rất có thể bạn nên viết nó như thế này:
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
this->Mode &= Mask;
};
Đoạn N37: Sự nhầm lẫn có thể thay đổi
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;
}
Cảnh báo PVS-Studio: V1001 [CWE-563] Biến 'Kích thước' được chỉ định nhưng không được sử dụng ở cuối hàm. Object.cpp 424
Tình hình cũng tương tự như lần trước. Nó nên được viết:
this->Size += this->EntrySize;
Đoạn N38-N47: Họ quên kiểm tra chỉ số
Trước đây, chúng ta đã xem xét các ví dụ về kích hoạt chẩn đoán
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()); // <=
....
}
Cảnh báo PVS-Studio: V1004 [CWE-476] Con trỏ 'Ptr' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra dòng: 729, 738. TargetTransformInfoImpl.h 738
Biến ptr có thể bằng nhau nullptr, được chứng minh bằng việc kiểm tra:
if (Ptr != nullptr)
Tuy nhiên, bên dưới con trỏ này bị hủy đăng ký mà không cần kiểm tra sơ bộ:
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());
Hãy xem xét một trường hợp tương tự khác.
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(); // <=
....
}
Cảnh báo PVS-Studio: V1004 [CWE-476] Con trỏ 'FD' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra các dòng: 3228, 3231. CGDebugInfo.cpp 3231
Hãy chú ý đến dấu hiệu FD. Tôi chắc chắn rằng vấn đề đã được nhìn thấy rõ ràng và không cần phải có lời giải thích đặc biệt nào.
Và xa hơn:
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()); // <=
....
}
Cảnh báo PVS-Studio: V1004 [CWE-476] Con trỏ 'PtrTy' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra các dòng: 960, 965. InterleavedLoadCombinePass.cpp 965
Làm thế nào để bảo vệ bản thân khỏi những lỗi như vậy? Hãy chú ý hơn đến Đánh giá mã và sử dụng máy phân tích tĩnh PVS-Studio để thường xuyên kiểm tra mã của bạn.
Không có ích gì khi trích dẫn các đoạn mã khác có lỗi kiểu này. Tôi sẽ chỉ để lại danh sách cảnh báo trong bài viết:
- V1004 [CWE-476] Con trỏ 'Expr' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra các dòng: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] Con trỏ 'PI' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra các dòng: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] Con trỏ 'StatepointCall' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra các dòng: 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] Con trỏ 'RV' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra các dòng: 2263, 2268. TGParser.cpp 2268
- V1004 [CWE-476] Con trỏ 'CalleeFn' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra các dòng: 1081, 1096. SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] Con trỏ 'TC' được sử dụng không an toàn sau khi được xác minh dựa trên nullptr. Kiểm tra dòng: 1819, 1824. Driver.cpp 1824
Đoạn N48-N60: Không nghiêm trọng nhưng có lỗi (có thể bị rò rỉ bộ nhớ)
std::unique_ptr<IRMutator> createISelMutator() {
....
std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
Strategies.emplace_back(
new InjectorIRStrategy(InjectorIRStrategy::getDefaultOps()));
....
}
Cảnh báo của PVS-Studio:
Để thêm một phần tử vào cuối vùng chứa như std::vectơ > bạn không thể chỉ viết xxx.push_back(X mới), vì không có sự chuyển đổi tiềm ẩn từ X* в std::unique_ptr.
Một giải pháp phổ biến là viết xxx.emplace_back(X mới)kể từ khi nó biên dịch: phương thức chỗ_trở lại xây dựng một phần tử trực tiếp từ các đối số của nó và do đó có thể sử dụng các hàm tạo rõ ràng.
Nó không an toàn. Nếu vectơ đầy thì bộ nhớ sẽ được cấp phát lại. Hoạt động phân bổ lại bộ nhớ có thể không thành công, dẫn đến một ngoại lệ được đưa ra std::bad_alloc. Trong trường hợp này, con trỏ sẽ bị mất và đối tượng đã tạo sẽ không bao giờ bị xóa.
Giải pháp an toàn là tạo duy nhất_ptrsẽ sở hữu con trỏ trước khi vectơ cố gắng phân bổ lại bộ nhớ:
xxx.push_back(std::unique_ptr<X>(new X))
Kể từ C++ 14, bạn có thể sử dụng 'std::make_unique':
xxx.push_back(std::make_unique<X>())
Loại lỗi này không nghiêm trọng đối với LLVM. Nếu không thể cấp phát bộ nhớ, trình biên dịch sẽ dừng lại. Tuy nhiên, đối với các ứng dụng có thời gian dài
Vì vậy, mặc dù mã này không gây ra mối đe dọa thực tế cho LLVM, nhưng tôi thấy hữu ích khi nói về mẫu lỗi này và máy phân tích PVS-Studio đã học cách xác định nó.
Các cảnh báo khác thuộc loại này:
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Passes' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. PassManager.h 546
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'AA' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. Bí danhAnalysis.h 324
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Mục' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. DWARFDebugFrame.cpp 519
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'AllEdges' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. CFGMST.h 268
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'VMaps' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Bản ghi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. FDRLogBuilder.h 30
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'PendingSubmodules' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. ModuleMap.cpp 810
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Đối tượng' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. DebugMap.cpp 88
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Chiến lược' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 685
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 686
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 688
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 689
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 690
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 691
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 692
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 693
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Công cụ sửa đổi' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. llvm-stress.cpp 694
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Toán hạng' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Stash' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] Một con trỏ không có chủ sở hữu được thêm vào vùng chứa 'Matchers' bằng phương thức 'emplace_back'. Rò rỉ bộ nhớ sẽ xảy ra trong trường hợp ngoại lệ. GlobalISelEmitter.cpp 2702
Kết luận
Tôi đã đưa ra tổng cộng 60 cảnh báo và sau đó dừng lại. Có khiếm khuyết nào khác mà máy phân tích PVS-Studio phát hiện trong LLVM không? Vâng tôi có. Tuy nhiên, khi tôi đang viết các đoạn mã cho bài báo thì trời đã tối muộn, hay đúng hơn là ban đêm, và tôi quyết định đã đến lúc kết thúc một ngày.
Tôi hy vọng bạn thấy nó thú vị và muốn dùng thử máy phân tích PVS-Studio.
Bạn có thể tải xuống máy phân tích và lấy khóa quét mìn tại
Quan trọng nhất là sử dụng phân tích tĩnh thường xuyên. Kiểm tra một lần, do chúng tôi thực hiện nhằm phổ biến phương pháp phân tích tĩnh và PVS-Studio không phải là một tình huống bình thường.
Chúc may mắn trong việc cải thiện chất lượng và độ tin cậy của mã của bạn!
Nếu bạn muốn chia sẻ bài viết này với khán giả nói tiếng Anh, vui lòng sử dụng liên kết dịch: Andrey Karpov.
Nguồn: www.habr.com