మా PVS-స్టూడియో ఎనలైజర్ని ఉపయోగించి LLVM ప్రాజెక్ట్ యొక్క చివరి కోడ్ తనిఖీ నుండి రెండు సంవత్సరాలకు పైగా గడిచాయి. PVS-స్టూడియో ఎనలైజర్ ఇప్పటికీ లోపాలు మరియు సంభావ్య దుర్బలత్వాలను గుర్తించడానికి ఒక ప్రముఖ సాధనంగా ఉందని నిర్ధారించుకోండి. దీన్ని చేయడానికి, మేము LLVM 8.0.0 విడుదలలో కొత్త లోపాలను తనిఖీ చేసి కనుగొంటాము.
వ్రాయవలసిన వ్యాసం
నిజం చెప్పాలంటే, నేను ఈ వ్యాసం రాయాలనుకోలేదు. మేము ఇప్పటికే చాలాసార్లు తనిఖీ చేసిన ప్రాజెక్ట్ గురించి వ్రాయడం ఆసక్తికరంగా లేదు (
ప్రతిసారీ LLVM యొక్క కొత్త వెర్షన్ విడుదల చేయబడిన లేదా నవీకరించబడినప్పుడు
చూడండి, క్లాంగ్ స్టాటిక్ ఎనలైజర్ యొక్క కొత్త వెర్షన్ కొత్త ఎర్రర్లను కనుగొనడం నేర్చుకుంది! PVS-Studioని ఉపయోగించడం యొక్క ఔచిత్యం తగ్గుతున్నట్లు నాకు అనిపిస్తోంది. క్లాంగ్ మునుపటి కంటే ఎక్కువ ఎర్రర్లను కనుగొంది మరియు PVS-స్టూడియో సామర్థ్యాలను గుర్తించింది. దీని గురించి మీరు ఏమనుకుంటున్నారు?
దీనికి నేను ఎల్లప్పుడూ ఇలాంటి వాటికి సమాధానం చెప్పాలనుకుంటున్నాను:
మేము కూడా ఖాళీగా కూర్చోము! మేము PVS-స్టూడియో ఎనలైజర్ సామర్థ్యాలను గణనీయంగా మెరుగుపరిచాము. కాబట్టి చింతించకండి, మేము మునుపటిలా లీడ్ చేస్తూనే ఉన్నాము.
దురదృష్టవశాత్తు, ఇది చెడ్డ సమాధానం. అందులో ఎలాంటి ఆధారాలు లేవు. అందుకే ఇప్పుడు ఈ వ్యాసం రాస్తున్నాను. కాబట్టి, LLVM ప్రాజెక్ట్ మరోసారి తనిఖీ చేయబడింది మరియు దానిలో అనేక రకాల లోపాలు కనుగొనబడ్డాయి. నాకు ఆసక్తికరంగా అనిపించిన వాటిని ఇప్పుడు నేను ప్రదర్శిస్తాను. క్లాంగ్ స్టాటిక్ ఎనలైజర్ ఈ లోపాలను కనుగొనలేదు (లేదా దాని సహాయంతో అలా చేయడం చాలా అసౌకర్యంగా ఉంటుంది). కానీ మనం చేయగలం. అంతేకాదు ఈ లోపాలను ఒక్క సాయంత్రంలో కనిపెట్టి రాసుకున్నాను.
కానీ వ్యాసం రాయడానికి చాలా వారాలు పట్టింది. వీటన్నింటినీ వచనంలోకి తీసుకురావడానికి నేను ప్రయత్నించలేకపోయాను :).
మార్గం ద్వారా, లోపాలు మరియు సంభావ్య దుర్బలత్వాలను గుర్తించడానికి PVS-స్టూడియో ఎనలైజర్లో ఏ సాంకేతికతలు ఉపయోగించబడుతున్నాయనే దానిపై మీకు ఆసక్తి ఉంటే, దీనితో పరిచయం పొందడానికి నేను సూచిస్తున్నాను.
కొత్త మరియు పాత డయాగ్నస్టిక్స్
ఇప్పటికే గుర్తించినట్లుగా, సుమారు రెండు సంవత్సరాల క్రితం LLVM ప్రాజెక్ట్ మరోసారి తనిఖీ చేయబడింది మరియు కనుగొనబడిన లోపాలు సరిదిద్దబడ్డాయి. ఇప్పుడు ఈ కథనం కొత్త బ్యాచ్ లోపాలను ప్రదర్శిస్తుంది. కొత్త బగ్లు ఎందుకు కనుగొనబడ్డాయి? దీనికి 3 కారణాలు ఉన్నాయి:
- LLVM ప్రాజెక్ట్ అభివృద్ధి చెందుతోంది, పాత కోడ్ని మారుస్తుంది మరియు కొత్త కోడ్ని జోడిస్తోంది. సహజంగానే, సవరించిన మరియు వ్రాసిన కోడ్లో కొత్త లోపాలు ఉన్నాయి. స్టాటిక్ అనాలిసిస్ను క్రమం తప్పకుండా ఉపయోగించాలని మరియు అప్పుడప్పుడు కాదు అని ఇది స్పష్టంగా చూపిస్తుంది. మా కథనాలు PVS-స్టూడియో ఎనలైజర్ యొక్క సామర్థ్యాలను బాగా చూపుతాయి, అయితే ఇది కోడ్ నాణ్యతను మెరుగుపరచడానికి మరియు లోపాలను పరిష్కరించే ఖర్చును తగ్గించడానికి ఏమీ లేదు. స్టాటిక్ కోడ్ ఎనలైజర్ని క్రమం తప్పకుండా ఉపయోగించండి!
- మేము ఇప్పటికే ఉన్న డయాగ్నస్టిక్లను ఖరారు చేస్తున్నాము మరియు మెరుగుపరుస్తున్నాము. అందువల్ల, ఎనలైజర్ మునుపటి స్కాన్ల సమయంలో గమనించని లోపాలను గుర్తించగలదు.
- 2 సంవత్సరాల క్రితం లేని కొత్త డయాగ్నస్టిక్స్ PVS-స్టూడియోలో కనిపించాయి. PVS-స్టూడియో అభివృద్ధిని స్పష్టంగా చూపించడానికి నేను వాటిని ప్రత్యేక విభాగంలో హైలైట్ చేయాలని నిర్ణయించుకున్నాను.
2 సంవత్సరాల క్రితం ఉన్న డయాగ్నస్టిక్స్ ద్వారా గుర్తించబడిన లోపాలు
భాగం N1: కాపీ-పేస్ట్
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-స్టూడియో హెచ్చరిక:
పేరు "avx512.mask.permvar" అనే సబ్స్ట్రింగ్తో ప్రారంభమవుతుందని రెండుసార్లు తనిఖీ చేయబడింది. రెండవ చెక్లో, వారు స్పష్టంగా వేరే ఏదైనా రాయాలని కోరుకున్నారు, కానీ కాపీ చేసిన వచనాన్ని సరిచేయడం మర్చిపోయారు.
ఫ్రాగ్మెంట్ N2: అక్షర దోషం
enum CXNameRefFlags {
CXNameRange_WantQualifier = 0x1,
CXNameRange_WantTemplateArgs = 0x2,
CXNameRange_WantSinglePiece = 0x4
};
void AnnotateTokensWorker::HandlePostPonedChildCursor(
CXCursor Cursor, unsigned StartTokenIndex) {
const auto flags = CXNameRange_WantQualifier | CXNameRange_WantQualifier;
....
}
హెచ్చరిక PVS-స్టూడియో: V501 '|'కి ఎడమ మరియు కుడి వైపున 'CXNameRange_WantQualifier' ఒకేలాంటి ఉప-వ్యక్తీకరణలు ఉన్నాయి. ఆపరేటర్. CIndex.cpp 7245
అక్షర దోషం కారణంగా, అదే పేరు గల స్థిరాంకం రెండుసార్లు ఉపయోగించబడుతుంది CXNameRange_WantQualifier.
ఫ్రాగ్మెంట్ N3: ఆపరేటర్ ప్రాధాన్యతతో గందరగోళం
int PPCTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
....
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian() ? 1 : 0)
return 0;
....
}
PVS-స్టూడియో హెచ్చరిక:
నా అభిప్రాయం ప్రకారం, ఇది చాలా అందమైన తప్పు. అవును, నాకు అందం గురించి వింత ఆలోచనలు ఉన్నాయని నాకు తెలుసు :).
ఇప్పుడు, ప్రకారం
(ISD == ISD::EXTRACT_VECTOR_ELT && (Index == ST->isLittleEndian())) ? 1 : 0
ఆచరణాత్మక దృక్కోణం నుండి, అటువంటి పరిస్థితి అర్ధవంతం కాదు, ఎందుకంటే దీనిని తగ్గించవచ్చు:
(ISD == ISD::EXTRACT_VECTOR_ELT && Index == ST->isLittleEndian())
ఇది స్పష్టమైన తప్పు. చాలా మటుకు, వారు 0/1ని వేరియబుల్తో పోల్చాలని కోరుకున్నారు ఇండెక్స్. కోడ్ని పరిష్కరించడానికి మీరు టెర్నరీ ఆపరేటర్ చుట్టూ కుండలీకరణాలను జోడించాలి:
if (ISD == ISD::EXTRACT_VECTOR_ELT && Index == (ST->isLittleEndian() ? 1 : 0))
మార్గం ద్వారా, టెర్నరీ ఆపరేటర్ చాలా ప్రమాదకరమైనది మరియు తార్కిక లోపాలను రేకెత్తిస్తుంది. దానితో చాలా జాగ్రత్తగా ఉండండి మరియు కుండలీకరణాలతో అత్యాశతో ఉండకండి. నేను ఈ అంశాన్ని మరింత వివరంగా చూశాను
ఫ్రాగ్మెంట్ N4, N5: శూన్య పాయింటర్
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-స్టూడియో హెచ్చరిక:
పాయింటర్ ఉంటే LHS శూన్యం, హెచ్చరిక జారీ చేయాలి. అయితే, బదులుగా, ఇదే శూన్య పాయింటర్ విస్మరించబడుతుంది: LHS->getAsString().
ఎర్రర్ హ్యాండ్లర్లో లోపం దాగి ఉన్నప్పుడు ఇది చాలా సాధారణ పరిస్థితి, ఎందుకంటే ఎవరూ వాటిని పరీక్షించరు. స్టాటిక్ ఎనలైజర్లు ఎంత తరచుగా ఉపయోగించబడినా, చేరుకోగల అన్ని కోడ్లను తనిఖీ చేస్తాయి. స్టాటిక్ అనాలిసిస్ ఇతర టెస్టింగ్ మరియు ఎర్రర్ ప్రొటెక్షన్ టెక్నిక్లను ఎలా పూరిస్తుంది అనేదానికి ఇది చాలా మంచి ఉదాహరణ.
ఇలాంటి పాయింటర్ నిర్వహణ లోపం HRH దిగువ కోడ్లో అనుమతించబడింది: V522 [CWE-476] శూన్య పాయింటర్ 'RHS' యొక్క డిఫరెన్సింగ్ జరగవచ్చు. TGParser.cpp 2186
ఫ్రాగ్మెంట్ N6: కదిలిన తర్వాత పాయింటర్ని ఉపయోగించడం
static Expected<bool>
ExtractBlocks(....)
{
....
std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap);
....
BD.setNewProgram(std::move(ProgClone)); // <=
MiscompiledFunctions.clear();
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first); // <=
assert(NewF && "Function not found??");
MiscompiledFunctions.push_back(NewF);
}
....
}
PVS-స్టూడియో హెచ్చరిక: V522 [CWE-476] శూన్య పాయింటర్ 'ప్రోగ్క్లోన్' యొక్క డిఫరెన్సింగ్ జరగవచ్చు. Miscompilation.cpp 601
ప్రారంభంలో ఒక స్మార్ట్ పాయింటర్ ప్రోగ్క్లోన్ వస్తువును స్వంతం చేసుకోవడం మానేస్తుంది:
BD.setNewProgram(std::move(ProgClone));
నిజానికి, ఇప్పుడు ప్రోగ్క్లోన్ శూన్య పాయింటర్. కాబట్టి, ఒక శూన్య పాయింటర్ డెరిఫరెన్స్ దిగువన జరగాలి:
Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
కానీ, వాస్తవానికి, ఇది జరగదు! లూప్ వాస్తవానికి అమలు చేయబడలేదని గమనించండి.
కంటైనర్ ప్రారంభంలో తప్పుగా సంకలనం చేయబడిన విధులు క్లియర్ చేయబడింది:
MiscompiledFunctions.clear();
తరువాత, ఈ కంటైనర్ పరిమాణం లూప్ కండిషన్లో ఉపయోగించబడుతుంది:
for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
లూప్ ప్రారంభం కాలేదని చూడటం సులభం. ఇది కూడా బగ్ అని నేను అనుకుంటున్నాను మరియు కోడ్ వేరే విధంగా వ్రాయాలి.
దోషాల యొక్క ప్రసిద్ధ సమానత్వాన్ని మేము ఎదుర్కొన్నట్లు అనిపిస్తుంది! ఒక తప్పు మరొకటి కప్పివేస్తుంది :).
ఫ్రాగ్మెంట్ N7: కదిలిన తర్వాత పాయింటర్ని ఉపయోగించడం
static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
std::unique_ptr<Module> Safe) {
outs() << " Optimizing functions being tested: ";
std::unique_ptr<Module> Optimized =
BD.runPassesOn(Test.get(), BD.getPassesToRun());
if (!Optimized) {
errs() << " Error running this sequence of passes"
<< " on the input program!n";
BD.setNewProgram(std::move(Test)); // <=
BD.EmitProgressBitcode(*Test, "pass-error", false); // <=
if (Error E = BD.debugOptimizerCrash())
return std::move(E);
return false;
}
....
}
PVS-స్టూడియో హెచ్చరిక: V522 [CWE-476] శూన్య పాయింటర్ 'టెస్ట్' యొక్క డిఫరెన్సింగ్ జరగవచ్చు. Miscompilation.cpp 709
మళ్లీ అదే పరిస్థితి. మొదట, వస్తువు యొక్క కంటెంట్లు తరలించబడతాయి, ఆపై అది ఏమీ జరగనట్లుగా ఉపయోగించబడుతుంది. C++లో కదలిక సెమాంటిక్స్ కనిపించిన తర్వాత ప్రోగ్రామ్ కోడ్లో నేను ఈ పరిస్థితిని ఎక్కువగా చూస్తాను. అందుకే నాకు C++ భాష అంటే చాలా ఇష్టం! మీ స్వంత కాలును కాల్చడానికి మరిన్ని కొత్త మార్గాలు ఉన్నాయి. PVS-స్టూడియో ఎనలైజర్కి ఎల్లప్పుడూ పని ఉంటుంది :).
ఫ్రాగ్మెంట్ N8: శూన్య పాయింటర్
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-స్టూడియో హెచ్చరిక: V522 [CWE-476] శూన్య పాయింటర్ 'టైప్' యొక్క డిఫరెన్సింగ్ జరగవచ్చు. PrettyFunctionDumper.cpp 233
ఎర్రర్ హ్యాండ్లర్లతో పాటు, డీబగ్గింగ్ ప్రింటౌట్ ఫంక్షన్లు సాధారణంగా పరీక్షించబడవు. మన ముందు అలాంటి కేసు ఉంది. ఫంక్షన్ వినియోగదారు కోసం వేచి ఉంది, అతను తన సమస్యలను పరిష్కరించడానికి బదులుగా, దాన్ని పరిష్కరించడానికి బలవంతం చేయబడతాడు.
సరైన:
if (Type)
Type->dump(*this);
else
Printer << "<unknown-type>";
ఫ్రాగ్మెంట్ N9: శూన్య పాయింటర్
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-స్టూడియో హెచ్చరిక: V522 [CWE-476] శూన్య పాయింటర్ 'Ty' యొక్క డిఫరెన్సింగ్ జరగవచ్చు. SearchableTableEmitter.cpp 614
ప్రతిదీ స్పష్టంగా ఉందని మరియు వివరణ అవసరం లేదని నేను భావిస్తున్నాను.
ఫ్రాగ్మెంట్ N10: అక్షర దోషం
bool FormatTokenLexer::tryMergeCSharpNullConditionals() {
....
auto &Identifier = *(Tokens.end() - 2);
auto &Question = *(Tokens.end() - 1);
....
Identifier->ColumnWidth += Question->ColumnWidth;
Identifier->Type = Identifier->Type; // <=
Tokens.erase(Tokens.end() - 1);
return true;
}
PVS-స్టూడియో హెచ్చరిక:
దానికదే వేరియబుల్ కేటాయించడంలో అర్థం లేదు. వారు ఎక్కువగా వ్రాయాలనుకుంటున్నారు:
Identifier->Type = Question->Type;
ఫ్రాగ్మెంట్ N11: అనుమానాస్పద విరామం
void SystemZOperand::print(raw_ostream &OS) const {
switch (Kind) {
break;
case KindToken:
OS << "Token:" << getToken();
break;
case KindReg:
OS << "Reg:" << SystemZInstPrinter::getRegisterName(getReg());
break;
....
}
PVS-స్టూడియో హెచ్చరిక:
ప్రారంభంలో చాలా అనుమానాస్పద ఆపరేటర్ ఉన్నారు విరామం. మీరు ఇక్కడ ఇంకేదైనా రాయడం మర్చిపోయారా?
ఫ్రాగ్మెంట్ N12: డిఫెరెన్సింగ్ తర్వాత పాయింటర్ను తనిఖీ చేస్తోంది
InlineCost AMDGPUInliner::getInlineCost(CallSite CS) {
Function *Callee = CS.getCalledFunction();
Function *Caller = CS.getCaller();
TargetTransformInfo &TTI = TTIWP->getTTI(*Callee);
if (!Callee || Callee->isDeclaration())
return llvm::InlineCost::getNever("undefined callee");
....
}
PVS-స్టూడియో హెచ్చరిక:
పాయింటర్ కాల్లీ ఫంక్షన్ అని పిలిచే సమయంలో ప్రారంభంలో dereference చేయబడుతుంది getTTI.
ఆపై ఈ పాయింటర్ సమానత్వం కోసం తనిఖీ చేయబడాలని మారుతుంది nullptr:
if (!Callee || Callee->isDeclaration())
కానీ ఇది చాలా ఆలస్యం…
ఫ్రాగ్మెంట్ N13 - N...: డిఫరెన్సింగ్ తర్వాత పాయింటర్ని తనిఖీ చేస్తోంది
మునుపటి కోడ్ ఫ్రాగ్మెంట్లో చర్చించిన పరిస్థితి ప్రత్యేకమైనది కాదు. ఇది ఇక్కడ కనిపిస్తుంది:
static Value *optimizeDoubleFP(CallInst *CI, IRBuilder<> &B,
bool isBinary, bool isPrecise = false) {
....
Function *CalleeFn = CI->getCalledFunction();
StringRef CalleeNm = CalleeFn->getName(); // <=
AttributeList CalleeAt = CalleeFn->getAttributes();
if (CalleeFn && !CalleeFn->isIntrinsic()) { // <=
....
}
PVS-స్టూడియో హెచ్చరిక: V595 [CWE-476] 'CalleeFn' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడటానికి ముందు ఉపయోగించబడింది. చెక్ లైన్లు: 1079, 1081. SimplifyLibCalls.cpp 1079
మరియు ఇక్కడ:
void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
const Decl *Tmpl, Decl *New,
LateInstantiatedAttrVec *LateAttrs,
LocalInstantiationScope *OuterMostScope) {
....
NamedDecl *ND = dyn_cast<NamedDecl>(New);
CXXRecordDecl *ThisContext =
dyn_cast_or_null<CXXRecordDecl>(ND->getDeclContext()); // <=
CXXThisScopeRAII ThisScope(*this, ThisContext, Qualifiers(),
ND && ND->isCXXInstanceMember()); // <=
....
}
PVS-స్టూడియో హెచ్చరిక: V595 [CWE-476] 'ND' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడటానికి ముందు ఉపయోగించబడింది. చెక్ లైన్లు: 532, 534. SemaTemplateInstantiateDecl.cpp 532
మరియు ఇక్కడ:
- V595 [CWE-476] 'U' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడటానికి ముందు ఉపయోగించబడింది. చెక్ లైన్లు: 404, 407. DWARFormValue.cpp 404
- V595 [CWE-476] nullptrకి వ్యతిరేకంగా ధృవీకరించబడటానికి ముందు 'ND' పాయింటర్ ఉపయోగించబడింది. చెక్ లైన్లు: 2149, 2151. SemaTemplateInstantiate.cpp 2149
ఆపై నేను V595 నంబర్తో హెచ్చరికలను అధ్యయనం చేయడంలో ఆసక్తి చూపలేదు. కాబట్టి ఇక్కడ జాబితా చేయబడినవి కాకుండా ఇలాంటి లోపాలు మరిన్ని ఉన్నాయో లేదో నాకు తెలియదు. చాలా మటుకు ఉంది.
ఫ్రాగ్మెంట్ N17, N18: అనుమానాస్పద మార్పు
static inline bool processLogicalImmediate(uint64_t Imm, unsigned RegSize,
uint64_t &Encoding) {
....
unsigned Size = RegSize;
....
uint64_t NImms = ~(Size-1) << 1;
....
}
PVS-స్టూడియో హెచ్చరిక:
ఇది బగ్ కాకపోవచ్చు మరియు కోడ్ ఉద్దేశించిన విధంగానే పని చేస్తుంది. కానీ ఇది స్పష్టంగా చాలా అనుమానాస్పద ప్రదేశం మరియు తనిఖీ చేయవలసిన అవసరం ఉంది.
వేరియబుల్ అనుకుందాం పరిమాణం 16కి సమానం, ఆపై కోడ్ యొక్క రచయిత దానిని వేరియబుల్లో పొందాలని ప్లాన్ చేశాడు నిమ్స్ అర్థం:
1111111111111111111111111111111111111111111111111111111111100000
అయితే, వాస్తవానికి ఫలితం ఇలా ఉంటుంది:
0000000000000000000000000000000011111111111111111111111111100000
వాస్తవం ఏమిటంటే అన్ని గణనలు 32-బిట్ సంతకం చేయని రకాన్ని ఉపయోగించి జరుగుతాయి. ఆపై మాత్రమే, ఈ 32-బిట్ సంతకం చేయని రకం పరోక్షంగా విస్తరించబడుతుంది uint64_t. ఈ సందర్భంలో, అత్యంత ముఖ్యమైన బిట్లు సున్నాగా ఉంటాయి.
మీరు ఈ విధంగా పరిస్థితిని పరిష్కరించవచ్చు:
uint64_t NImms = ~static_cast<uint64_t>(Size-1) << 1;
ఇలాంటి పరిస్థితి: V629 [CWE-190] 'Immr << 6' వ్యక్తీకరణను పరిశీలించడాన్ని పరిగణించండి. 32-బిట్ రకానికి తదుపరి విస్తరణతో 64-బిట్ విలువ యొక్క బిట్ బదిలీ. AArch64AddressingModes.h 269
ఫ్రాగ్మెంట్ N19: కీవర్డ్ లేదు వేరే?
void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) {
....
if (Op.isReg() && Op.Reg.RegNo == AMDGPU::VCC) {
// VOP2b (v_add_u32, v_sub_u32 ...) dpp use "vcc" token.
// Skip it.
continue;
} if (isRegOrImmWithInputMods(Desc, Inst.getNumOperands())) { // <=
Op.addRegWithFPInputModsOperands(Inst, 2);
} else if (Op.isDPPCtrl()) {
Op.addImmOperands(Inst, 1);
} else if (Op.isImm()) {
// Handle optional arguments
OptionalIdx[Op.getImmTy()] = I;
} else {
llvm_unreachable("Invalid operand type");
}
....
}
PVS-స్టూడియో హెచ్చరిక:
ఇక్కడ తప్పు లేదు. మొదటి బ్లాక్ అప్పటి నుండి if తో ముగుస్తుంది కొనసాగించడానికి, అప్పుడు పర్వాలేదు, కీవర్డ్ ఉంది వేరే లేదా. ఎలాగైనా కోడ్ అదే పని చేస్తుంది. ఇంకా మిస్సయింది వేరే కోడ్ను మరింత అస్పష్టంగా మరియు ప్రమాదకరంగా చేస్తుంది. భవిష్యత్తులో ఉంటే కొనసాగించడానికి అదృశ్యమవుతుంది, కోడ్ పూర్తిగా భిన్నంగా పనిచేయడం ప్రారంభిస్తుంది. జోడించడం మంచిదని నా అభిప్రాయం వేరే.
ఫ్రాగ్మెంట్ N20: ఒకే రకమైన నాలుగు అక్షరదోషాలు
LLVM_DUMP_METHOD void Symbol::dump(raw_ostream &OS) const {
std::string Result;
if (isUndefined())
Result += "(undef) ";
if (isWeakDefined())
Result += "(weak-def) ";
if (isWeakReferenced())
Result += "(weak-ref) ";
if (isThreadLocalValue())
Result += "(tlv) ";
switch (Kind) {
case SymbolKind::GlobalSymbol:
Result + Name.str(); // <=
break;
case SymbolKind::ObjectiveCClass:
Result + "(ObjC Class) " + Name.str(); // <=
break;
case SymbolKind::ObjectiveCClassEHType:
Result + "(ObjC Class EH) " + Name.str(); // <=
break;
case SymbolKind::ObjectiveCInstanceVariable:
Result + "(ObjC IVar) " + Name.str(); // <=
break;
}
OS << Result;
}
PVS-స్టూడియో హెచ్చరికలు:
- V655 [CWE-480] స్ట్రింగ్లు సంగ్రహించబడ్డాయి కానీ ఉపయోగించబడలేదు. 'ఫలితం + పేరు.str()' వ్యక్తీకరణను పరిశీలించడాన్ని పరిగణించండి. Symbol.cpp 32
- V655 [CWE-480] స్ట్రింగ్లు సంగ్రహించబడ్డాయి కానీ ఉపయోగించబడలేదు. 'ఫలితం + "(ObjC క్లాస్)" + Name.str()' వ్యక్తీకరణను పరిశీలించడాన్ని పరిగణించండి. Symbol.cpp 35
- V655 [CWE-480] స్ట్రింగ్లు సంగ్రహించబడ్డాయి కానీ ఉపయోగించబడలేదు. 'ఫలితం + "(ObjC క్లాస్ EH) " + Name.str()' వ్యక్తీకరణను పరిశీలించడాన్ని పరిగణించండి. Symbol.cpp 38
- V655 [CWE-480] స్ట్రింగ్లు సంగ్రహించబడ్డాయి కానీ ఉపయోగించబడలేదు. 'ఫలితం + "(ObjC IVar)" + Name.str()' వ్యక్తీకరణను పరిశీలించడాన్ని పరిగణించండి. Symbol.cpp 41
ప్రమాదవశాత్తు, += ఆపరేటర్కు బదులుగా + ఆపరేటర్ ఉపయోగించబడుతుంది. ఫలితం అర్థం లేని డిజైన్లు.
ఫ్రాగ్మెంట్ N21: నిర్వచించబడని ప్రవర్తన
static void getReqFeatures(std::map<StringRef, int> &FeaturesMap,
const std::vector<Record *> &ReqFeatures) {
for (auto &R : ReqFeatures) {
StringRef AsmCondString = R->getValueAsString("AssemblerCondString");
SmallVector<StringRef, 4> Ops;
SplitString(AsmCondString, Ops, ",");
assert(!Ops.empty() && "AssemblerCondString cannot be empty");
for (auto &Op : Ops) {
assert(!Op.empty() && "Empty operator");
if (FeaturesMap.find(Op) == FeaturesMap.end())
FeaturesMap[Op] = FeaturesMap.size();
}
}
}
ప్రమాదకరమైన కోడ్ను మీరే కనుగొనడానికి ప్రయత్నించండి. మరియు సమాధానాన్ని వెంటనే చూడకుండా దృష్టిని మరల్చడానికి ఇది ఒక చిత్రం:
PVS-స్టూడియో హెచ్చరిక:
సమస్య లైన్:
FeaturesMap[Op] = FeaturesMap.size();
మూలకం ఉంటే Op కనుగొనబడలేదు, ఆపై మ్యాప్లో కొత్త మూలకం సృష్టించబడుతుంది మరియు ఈ మ్యాప్లోని మూలకాల సంఖ్య అక్కడ వ్రాయబడుతుంది. ఫంక్షన్కు పిలుస్తారో లేదో తెలియదు పరిమాణం కొత్త మూలకాన్ని జోడించే ముందు లేదా తర్వాత.
ఫ్రాగ్మెంట్ N22-N24: పునరావృత అసైన్మెంట్లు
Error MachOObjectFile::checkSymbolTable() const {
....
} else {
MachO::nlist STE = getSymbolTableEntry(SymDRI);
NType = STE.n_type; // <=
NType = STE.n_type; // <=
NSect = STE.n_sect;
NDesc = STE.n_desc;
NStrx = STE.n_strx;
NValue = STE.n_value;
}
....
}
PVS-స్టూడియో హెచ్చరిక:
ఇక్కడ నిజమైన తప్పు ఉందని నేను అనుకోను. కేవలం అనవసరమైన పదే పదే అప్పగించడం. కానీ ఇప్పటికీ ఒక తప్పు.
అదేవిధంగా:
- V519 [CWE-563] 'B.NDesc' వేరియబుల్ వరుసగా రెండుసార్లు విలువలను కేటాయించింది. బహుశా ఇది పొరపాటు. చెక్ లైన్లు: 1488, 1489. llvm-nm.cpp 1489
- V519 [CWE-563] వేరియబుల్కు వరుసగా రెండుసార్లు విలువలు కేటాయించబడతాయి. బహుశా ఇది పొరపాటు. చెక్ లైన్లు: 59, 61. coff2yaml.cpp 61
ఫ్రాగ్మెంట్ N25-N27: మరిన్ని రీఅసైన్మెంట్లు
ఇప్పుడు రీఅసైన్మెంట్ యొక్క కొద్దిగా భిన్నమైన సంస్కరణను చూద్దాం.
bool Vectorizer::vectorizeLoadChain(
ArrayRef<Instruction *> Chain,
SmallPtrSet<Instruction *, 16> *InstructionsProcessed) {
....
unsigned Alignment = getAlignment(L0);
....
unsigned NewAlign = getOrEnforceKnownAlignment(L0->getPointerOperand(),
StackAdjustedAlignment,
DL, L0, nullptr, &DT);
if (NewAlign != 0)
Alignment = NewAlign;
Alignment = NewAlign;
....
}
PVS-స్టూడియో హెచ్చరిక: V519 [CWE-563] 'అలైన్మెంట్' వేరియబుల్కు వరుసగా రెండుసార్లు విలువలు కేటాయించబడ్డాయి. బహుశా ఇది పొరపాటు. చెక్ లైన్లు: 1158, 1160. LoadStoreVectorizer.cpp 1160
ఇది చాలా విచిత్రమైన కోడ్, ఇది స్పష్టంగా తార్కిక లోపాన్ని కలిగి ఉంది. ప్రారంభంలో, వేరియబుల్ అమరిక పరిస్థితిని బట్టి విలువ కేటాయించబడుతుంది. ఆపై అసైన్మెంట్ మళ్లీ జరుగుతుంది, కానీ ఇప్పుడు ఎలాంటి తనిఖీ లేకుండా.
ఇలాంటి పరిస్థితులను ఇక్కడ చూడవచ్చు:
- V519 [CWE-563] 'ఎఫెక్ట్స్' వేరియబుల్ వరుసగా రెండుసార్లు విలువలను కేటాయించింది. బహుశా ఇది పొరపాటు. చెక్ లైన్లు: 152, 165. WebAssemblyRegStackify.cpp 165
- V519 [CWE-563] 'ExpectNoDerefChunk' వేరియబుల్ వరుసగా రెండుసార్లు విలువలను కేటాయించింది. బహుశా ఇది పొరపాటు. చెక్ లైన్లు: 4970, 4973. SemaType.cpp 4973
ఫ్రాగ్మెంట్ N28: ఎల్లప్పుడూ నిజమైన స్థితి
static int readPrefixes(struct InternalInstruction* insn) {
....
uint8_t byte = 0;
uint8_t nextByte;
....
if (byte == 0xf3 && (nextByte == 0x88 || nextByte == 0x89 ||
nextByte == 0xc6 || nextByte == 0xc7)) {
insn->xAcquireRelease = true;
if (nextByte != 0x90) // PAUSE instruction support // <=
break;
}
....
}
PVS-స్టూడియో హెచ్చరిక:
తనిఖీ చేయడం సమంజసం కాదు. వేరియబుల్ తదుపరి బైట్ ఎల్లప్పుడూ విలువతో సమానంగా ఉండదు 0x90, ఇది మునుపటి చెక్ నుండి అనుసరిస్తుంది. ఇది ఒక రకమైన తార్కిక లోపం.
ఫ్రాగ్మెంట్ N29 - N...: ఎల్లప్పుడూ నిజం/తప్పు పరిస్థితులు
ఎనలైజర్ మొత్తం పరిస్థితికి అనేక హెచ్చరికలను జారీ చేస్తుంది (
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-స్టూడియో హెచ్చరిక:
స్థిరాంకం 0xE అనేది దశాంశంలో విలువ 14. పరీక్ష RegNo == 0xe అర్థం కాదు ఎందుకంటే ఉంటే RegNo > 13, అప్పుడు ఫంక్షన్ దాని అమలును పూర్తి చేస్తుంది.
IDలు V547 మరియు V560తో అనేక ఇతర హెచ్చరికలు ఉన్నాయి, కానీ అలాగే
ఈ ట్రిగ్గర్లను అధ్యయనం చేయడం ఎందుకు విసుగు చెందుతుందో నేను మీకు ఒక ఉదాహరణ ఇస్తాను. కింది కోడ్కు హెచ్చరికను జారీ చేయడంలో ఎనలైజర్ ఖచ్చితంగా సరైనది. అయితే ఇది పొరపాటు కాదు.
bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
tok::TokenKind ClosingBraceKind) {
bool HasError = false;
....
HasError = true;
if (!ContinueOnSemicolons)
return !HasError;
....
}
PVS-స్టూడియో హెచ్చరిక: V547 [CWE-570] వ్యక్తీకరణ '!HasError' ఎల్లప్పుడూ తప్పు. UnwrappedLineParser.cpp 1635
ఫ్రాగ్మెంట్ N30: అనుమానాస్పద రిటర్న్
static bool
isImplicitlyDef(MachineRegisterInfo &MRI, unsigned Reg) {
for (MachineRegisterInfo::def_instr_iterator It = MRI.def_instr_begin(Reg),
E = MRI.def_instr_end(); It != E; ++It) {
return (*It).isImplicitDef();
}
....
}
PVS-స్టూడియో హెచ్చరిక:
ఇది లోపం లేదా కోడ్ని చదివే ప్రోగ్రామర్లకు ఏదైనా వివరించడానికి ఉద్దేశించిన నిర్దిష్ట సాంకేతికత. ఈ డిజైన్ నాకు ఏమీ వివరించలేదు మరియు చాలా అనుమానాస్పదంగా ఉంది. అలా రాయకపోవడమే మంచిది :).
అలసిన? అప్పుడు టీ లేదా కాఫీ చేయడానికి సమయం ఆసన్నమైంది.
కొత్త డయాగ్నస్టిక్స్ ద్వారా గుర్తించబడిన లోపాలు
పాత డయాగ్నస్టిక్స్ యొక్క 30 యాక్టివేషన్లు సరిపోతాయని నేను భావిస్తున్నాను. తర్వాత ఎనలైజర్లో కనిపించిన కొత్త డయాగ్నస్టిక్స్తో ఎలాంటి ఆసక్తికరమైన విషయాలు కనుగొనవచ్చో ఇప్పుడు చూద్దాం
ఫ్రాగ్మెంట్ N31: చేరుకోలేని కోడ్
Error CtorDtorRunner::run() {
....
if (auto CtorDtorMap =
ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
NoDependenciesToRegister, true))
{
....
return Error::success();
} else
return CtorDtorMap.takeError();
CtorDtorsByPriority.clear();
return Error::success();
}
PVS-స్టూడియో హెచ్చరిక:
మీరు చూడగలిగినట్లుగా, ఆపరేటర్ యొక్క రెండు శాఖలు if ఆపరేటర్కు కాల్తో ముగుస్తుంది తిరిగి. దీని ప్రకారం, కంటైనర్ CtorDtorsBy Priority ఎప్పటికీ క్లియర్ చేయబడదు.
ఫ్రాగ్మెంట్ N32: చేరుకోలేని కోడ్
bool LLParser::ParseSummaryEntry() {
....
switch (Lex.getKind()) {
case lltok::kw_gv:
return ParseGVEntry(SummaryID);
case lltok::kw_module:
return ParseModuleEntry(SummaryID);
case lltok::kw_typeid:
return ParseTypeIdEntry(SummaryID); // <=
break; // <=
default:
return Error(Lex.getLoc(), "unexpected summary kind");
}
Lex.setIgnoreColonInIdentifiers(false); // <=
return false;
}
PVS-స్టూడియో హెచ్చరిక: V779 [CWE-561] చేరుకోలేని కోడ్ కనుగొనబడింది. లోపం ఉన్న అవకాశం ఉంది. LLParser.cpp 835
ఆసక్తికరమైన పరిస్థితి. ముందుగా ఈ స్థలాన్ని చూద్దాం:
return ParseTypeIdEntry(SummaryID);
break;
మొదటి చూపులో, ఇక్కడ లోపం లేదని అనిపిస్తుంది. ఇది ఆపరేటర్ లాగా ఉంది విరామం ఇక్కడ అదనపు ఒకటి ఉంది మరియు మీరు దానిని తొలగించవచ్చు. అయితే, ప్రతిదీ అంత సులభం కాదు.
ఎనలైజర్ లైన్లలో హెచ్చరికను జారీ చేస్తుంది:
Lex.setIgnoreColonInIdentifiers(false);
return false;
మరియు నిజానికి, ఈ కోడ్ అందుబాటులో లేదు. అన్ని కేసులు స్విచ్ ఆపరేటర్ నుండి కాల్తో ముగుస్తుంది తిరిగి. మరియు ఇప్పుడు తెలివిలేని ఒంటరిగా విరామం అంత ప్రమాదకరం అనిపించడం లేదు! బహుశా శాఖలలో ఒకదానితో ముగుస్తుంది విరామంలేదు తిరిగి?
ఫ్రాగ్మెంట్ N33: అధిక బిట్ల యాదృచ్ఛిక రీసెట్
unsigned getStubAlignment() override {
if (Arch == Triple::systemz)
return 8;
else
return 1;
}
Expected<unsigned>
RuntimeDyldImpl::emitSection(const ObjectFile &Obj,
const SectionRef &Section,
bool IsCode) {
....
uint64_t DataSize = Section.getSize();
....
if (StubBufSize > 0)
DataSize &= ~(getStubAlignment() - 1);
....
}
PVS-స్టూడియో హెచ్చరిక:
దయచేసి ఫంక్షన్ గమనించండి getStubAlinment రిటర్న్స్ రకం సైన్ చేయని. ఫంక్షన్ విలువ 8ని అందిస్తుంది అని ఊహిస్తూ, వ్యక్తీకరణ యొక్క విలువను గణిద్దాం:
~(getStubAlignment() - 1)
~(8u-1)
0xFFFFFFFF8u
ఇప్పుడు వేరియబుల్ గమనించండి డేటా సైజు 64-బిట్ సంతకం చేయని రకాన్ని కలిగి ఉంది. డేటాసైజ్ & 0xFFFFFFF8u ఆపరేషన్ చేస్తున్నప్పుడు, మొత్తం ముప్పై-రెండు అధిక-ఆర్డర్ బిట్లు సున్నాకి రీసెట్ చేయబడతాయి. చాలా మటుకు, ఇది ప్రోగ్రామర్ కోరుకున్నది కాదు. అతను లెక్కించాలనుకుంటున్నాడని నేను అనుమానిస్తున్నాను: డేటాసైజ్ & 0xFFFFFFFFFFFFFFF8u.
లోపాన్ని పరిష్కరించడానికి, మీరు దీన్ని వ్రాయాలి:
DataSize &= ~(static_cast<uint64_t>(getStubAlignment()) - 1);
లేదా అలా:
DataSize &= ~(getStubAlignment() - 1ULL);
ఫ్రాగ్మెంట్ N34: విఫలమైన స్పష్టమైన రకం తారాగణం
template <typename T>
void scaleShuffleMask(int Scale, ArrayRef<T> Mask,
SmallVectorImpl<T> &ScaledMask) {
assert(0 < Scale && "Unexpected scaling factor");
int NumElts = Mask.size();
ScaledMask.assign(static_cast<size_t>(NumElts * Scale), -1);
....
}
PVS-స్టూడియో హెచ్చరిక:
రకం వేరియబుల్లను గుణించేటప్పుడు ఓవర్ఫ్లోను నివారించడానికి స్పష్టమైన రకం కాస్టింగ్ ఉపయోగించబడుతుంది పూర్ణాంకానికి. అయితే, ఇక్కడ స్పష్టమైన రకం కాస్టింగ్ ఓవర్ఫ్లో నుండి రక్షించదు. మొదట, వేరియబుల్స్ గుణించబడతాయి మరియు అప్పుడు మాత్రమే గుణకారం యొక్క 32-బిట్ ఫలితం రకానికి విస్తరించబడుతుంది
భాగం N35: కాపీ-పేస్ట్ చేయడం విఫలమైంది
Instruction *InstCombiner::visitFCmpInst(FCmpInst &I) {
....
if (!match(Op0, m_PosZeroFP()) && isKnownNeverNaN(Op0, &TLI)) {
I.setOperand(0, ConstantFP::getNullValue(Op0->getType()));
return &I;
}
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
I.setOperand(1, ConstantFP::getNullValue(Op0->getType())); // <=
return &I;
}
....
}
ఈ కొత్త ఆసక్తికరమైన డయాగ్నస్టిక్ కోడ్ ముక్క కాపీ చేయబడిన మరియు దానిలోని కొన్ని పేర్లను మార్చడం ప్రారంభించబడిన పరిస్థితులను గుర్తిస్తుంది, కానీ ఒక చోట వారు దానిని సరిదిద్దలేదు.
రెండవ బ్లాక్లో అవి మారాయని దయచేసి గమనించండి Op0 న Op1. కానీ ఒక చోట వాటిని సరిచేయలేదు. చాలా మటుకు ఇది ఇలా వ్రాయబడి ఉండవచ్చు:
if (!match(Op1, m_PosZeroFP()) && isKnownNeverNaN(Op1, &TLI)) {
I.setOperand(1, ConstantFP::getNullValue(Op1->getType()));
return &I;
}
ఫ్రాగ్మెంట్ N36: వేరియబుల్ గందరగోళం
struct Status {
unsigned Mask;
unsigned Mode;
Status() : Mask(0), Mode(0){};
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
Mode &= Mask;
};
....
};
PVS-స్టూడియో హెచ్చరిక:
ఫంక్షన్ ఆర్గ్యుమెంట్లకు క్లాస్ మెంబర్ల పేర్లనే ఇవ్వడం చాలా ప్రమాదకరం. గందరగోళం చెందడం చాలా సులభం. మన ముందు అలాంటి కేసు ఉంది. ఈ వ్యక్తీకరణ అర్ధవంతం కాదు:
Mode &= Mask;
ఫంక్షన్ వాదన మారుతుంది. అంతే. ఈ వాదన ఇప్పుడు ఉపయోగించబడదు. చాలా మటుకు మీరు దీన్ని ఇలా వ్రాసి ఉండాలి:
Status(unsigned Mask, unsigned Mode) : Mask(Mask), Mode(Mode) {
this->Mode &= Mask;
};
ఫ్రాగ్మెంట్ N37: వేరియబుల్ గందరగోళం
class SectionBase {
....
uint64_t Size = 0;
....
};
class SymbolTableSection : public SectionBase {
....
};
void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
SectionBase *DefinedIn, uint64_t Value,
uint8_t Visibility, uint16_t Shndx,
uint64_t Size) {
....
Sym.Value = Value;
Sym.Visibility = Visibility;
Sym.Size = Size;
Sym.Index = Symbols.size();
Symbols.emplace_back(llvm::make_unique<Symbol>(Sym));
Size += this->EntrySize;
}
హెచ్చరిక PVS-స్టూడియో: V1001 [CWE-563] 'సైజ్' వేరియబుల్ కేటాయించబడింది కానీ ఫంక్షన్ ముగిసే సమయానికి ఉపయోగించబడదు. Object.cpp 424
పరిస్థితి మునుపటి మాదిరిగానే ఉంది. ఇది వ్రాయబడాలి:
this->Size += this->EntrySize;
ఫ్రాగ్మెంట్ N38-N47: వారు సూచికను తనిఖీ చేయడం మర్చిపోయారు
మునుపు, మేము డయాగ్నస్టిక్ ట్రిగ్గరింగ్ యొక్క ఉదాహరణలను చూశాము
int getGEPCost(Type *PointeeType, const Value *Ptr,
ArrayRef<const Value *> Operands) {
....
if (Ptr != nullptr) { // <=
assert(....);
BaseGV = dyn_cast<GlobalValue>(Ptr->stripPointerCasts());
}
bool HasBaseReg = (BaseGV == nullptr);
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType()); // <=
....
}
PVS-Studio హెచ్చరిక: V1004 [CWE-476] 'Ptr' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 729, 738. TargetTransformInfoImpl.h 738
వేరియబుల్ Ptr సమానంగా ఉండవచ్చు nullptr, చెక్ ద్వారా రుజువు చేయబడింది:
if (Ptr != nullptr)
అయితే, ఈ పాయింటర్ క్రింద ప్రాథమిక తనిఖీ లేకుండానే సూచించబడింది:
auto PtrSizeBits = DL.getPointerTypeSizeInBits(Ptr->getType());
ఇలాంటిదే మరొక సందర్భాన్ని పరిశీలిద్దాం.
llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
bool Stub) {
....
auto *FD = dyn_cast<FunctionDecl>(GD.getDecl());
SmallVector<QualType, 16> ArgTypes;
if (FD) // <=
for (const ParmVarDecl *Parm : FD->parameters())
ArgTypes.push_back(Parm->getType());
CallingConv CC = FD->getType()->castAs<FunctionType>()->getCallConv(); // <=
....
}
PVS-స్టూడియో హెచ్చరిక: V1004 [CWE-476] 'FD' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 3228, 3231. CGDebugInfo.cpp 3231
గుర్తుపై శ్రద్ధ వహించండి FD. సమస్య స్పష్టంగా కనిపిస్తుందని మరియు ప్రత్యేక వివరణ అవసరం లేదని నేను ఖచ్చితంగా అనుకుంటున్నాను.
మరియు ఇంకా:
static void computePolynomialFromPointer(Value &Ptr, Polynomial &Result,
Value *&BasePtr,
const DataLayout &DL) {
PointerType *PtrTy = dyn_cast<PointerType>(Ptr.getType());
if (!PtrTy) { // <=
Result = Polynomial();
BasePtr = nullptr;
}
unsigned PointerBits =
DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace()); // <=
....
}
PVS-స్టూడియో హెచ్చరిక: V1004 [CWE-476] nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత 'PtrTy' పాయింటర్ సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 960, 965. InterleavedLoadCombinePass.cpp 965
అటువంటి లోపాల నుండి మిమ్మల్ని మీరు ఎలా రక్షించుకోవాలి? కోడ్-రివ్యూపై మరింత శ్రద్ధ వహించండి మరియు మీ కోడ్ని క్రమం తప్పకుండా తనిఖీ చేయడానికి PVS-స్టూడియో స్టాటిక్ ఎనలైజర్ని ఉపయోగించండి.
ఈ రకమైన లోపాలతో ఇతర కోడ్ శకలాలు ఉదహరించడంలో అర్థం లేదు. నేను వ్యాసంలో హెచ్చరికల జాబితాను మాత్రమే వదిలివేస్తాను:
- V1004 [CWE-476] 'Expr' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత 'PI' పాయింటర్ సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత 'StatepointCall' పాయింటర్ సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 4371, 4379. Verifier.cpp 4379
- V1004 [CWE-476] 'RV' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 2263, 2268. TGParser.cpp 2268
- V1004 [CWE-476] 'CalleeFn' పాయింటర్ nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 1081, 1096. SimplifyLibCalls.cpp 1096
- V1004 [CWE-476] nullptrకి వ్యతిరేకంగా ధృవీకరించబడిన తర్వాత 'TC' పాయింటర్ సురక్షితంగా ఉపయోగించబడింది. చెక్ లైన్లు: 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()));
....
}
PVS-స్టూడియో హెచ్చరిక:
వంటి కంటైనర్ చివర మూలకాన్ని జోడించడానికి std:: వెక్టర్ > మీరు కేవలం వ్రాయలేరు xxx.push_back(కొత్త X), నుండి ఎటువంటి అవ్యక్త మార్పిడి లేనందున X* в std::unique_ptr.
ఒక సాధారణ పరిష్కారం వ్రాయడం xxx.emplace_back(కొత్త X)ఇది కంపైల్ చేసినందున: పద్ధతి emplace_back ఒక మూలకాన్ని దాని వాదనల నుండి నేరుగా నిర్మిస్తుంది మరియు అందువల్ల స్పష్టమైన కన్స్ట్రక్టర్లను ఉపయోగించవచ్చు.
ఇది సురక్షితం కాదు. వెక్టార్ నిండినట్లయితే, మెమరీ మళ్లీ కేటాయించబడుతుంది. మెమరీ రీలొకేషన్ ఆపరేషన్ విఫలం కావచ్చు, దీని ఫలితంగా మినహాయింపు విసిరివేయబడుతుంది std::bad_alloc. ఈ సందర్భంలో, పాయింటర్ పోతుంది మరియు సృష్టించబడిన వస్తువు ఎప్పటికీ తొలగించబడదు.
సురక్షితమైన పరిష్కారం సృష్టించడం ఏకైక_ptrవెక్టర్ మెమరీని మళ్లీ కేటాయించడానికి ప్రయత్నించే ముందు ఇది పాయింటర్ను కలిగి ఉంటుంది:
xxx.push_back(std::unique_ptr<X>(new X))
C++14 నుండి, మీరు 'std::make_unique'ని ఉపయోగించవచ్చు:
xxx.push_back(std::make_unique<X>())
ఈ రకమైన లోపం LLVMకి క్లిష్టమైనది కాదు. మెమరీని కేటాయించలేకపోతే, కంపైలర్ ఆగిపోతుంది. అయితే, దీర్ఘకాలం ఉన్న అప్లికేషన్లకు
కాబట్టి, ఈ కోడ్ LLVMకి ఆచరణాత్మక ముప్పును కలిగి ఉండనప్పటికీ, ఈ లోపం నమూనా గురించి మాట్లాడటం ఉపయోగకరంగా ఉందని మరియు PVS-స్టూడియో ఎనలైజర్ దానిని గుర్తించడం నేర్చుకున్నదని నేను కనుగొన్నాను.
ఈ రకమైన ఇతర హెచ్చరికలు:
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'Emplace_back' పద్ధతి ద్వారా 'Passes' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. PassManager.h 546
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'AAs' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. AliasAnalysis.h 324
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎంప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'ఎంట్రీస్' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. DWARFDebugFrame.cpp 519
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'Emplace_back' పద్ధతి ద్వారా 'AllEdges' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. CFGMST.h 268
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'VMaps' కంటైనర్కు 'emplace_back' పద్ధతి ద్వారా జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'emplace_back' పద్ధతి ద్వారా 'Records' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. FDRLogBuilder.h 30
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎంప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'పెండింగ్ సబ్మాడ్యూల్స్' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. ModuleMap.cpp 810
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'ఆబ్జెక్ట్స్' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. DebugMap.cpp 88
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'స్ట్రాటజీస్' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-isel-fuzzer.cpp 60
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 685
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 686
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 688
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 689
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 690
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 691
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 692
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 693
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మోడిఫైయర్లు' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. llvm-stress.cpp 694
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎంప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'Operands' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'emplace_back' పద్ధతి ద్వారా 'Stash' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] యజమాని లేని పాయింటర్ 'ఎమ్ప్లేస్_బ్యాక్' పద్ధతి ద్వారా 'మ్యాచర్స్' కంటైనర్కు జోడించబడింది. మినహాయింపు విషయంలో మెమరీ లీక్ జరుగుతుంది. GlobalISelEmitter.cpp 2702
తీర్మానం
నేను మొత్తం 60 హెచ్చరికలు జారీ చేసి, ఆపివేసాను. LLVMలో PVS-స్టూడియో ఎనలైజర్ గుర్తించే ఇతర లోపాలు ఏమైనా ఉన్నాయా? అవును నా దగ్గర వుంది. అయితే, నేను వ్యాసం కోసం కోడ్ శకలాలు వ్రాసేటప్పుడు, అది సాయంత్రం లేదా రాత్రి కూడా, మరియు నేను దానిని ఒక రోజు అని పిలవాలని నిర్ణయించుకున్నాను.
మీరు దీన్ని ఆసక్తికరంగా కనుగొన్నారని మరియు PVS-స్టూడియో ఎనలైజర్ని ప్రయత్నించాలనుకుంటున్నారని నేను ఆశిస్తున్నాను.
మీరు ఎనలైజర్ని డౌన్లోడ్ చేసుకోవచ్చు మరియు మైన్స్వీపర్ కీని ఇక్కడ పొందవచ్చు
ముఖ్యంగా, స్టాటిక్ విశ్లేషణను క్రమం తప్పకుండా ఉపయోగించండి. వన్-టైమ్ తనిఖీలు, స్టాటిక్ అనాలిసిస్ మరియు PVS-స్టూడియో యొక్క మెథడాలజీని ప్రాచుర్యంలోకి తీసుకురావడానికి మాచే నిర్వహించబడినది సాధారణ దృశ్యం కాదు.
మీ కోడ్ నాణ్యత మరియు విశ్వసనీయతను మెరుగుపరచడంలో అదృష్టం!
మీరు ఈ కథనాన్ని ఇంగ్లీష్ మాట్లాడే ప్రేక్షకులతో భాగస్వామ్యం చేయాలనుకుంటే, దయచేసి అనువాద లింక్ని ఉపయోగించండి: Andrey Karpov.
మూలం: www.habr.com