எங்கள் PVS-ஸ்டுடியோ பகுப்பாய்வியைப் பயன்படுத்தி LLVM திட்டத்தின் கடைசி குறியீடு சரிபார்ப்பில் இருந்து இரண்டு ஆண்டுகளுக்கும் மேலாகிவிட்டது. பிவிஎஸ்-ஸ்டுடியோ பகுப்பாய்வி பிழைகள் மற்றும் சாத்தியமான பாதிப்புகளைக் கண்டறிவதற்கான முன்னணி கருவியாக இருப்பதை உறுதி செய்வோம். இதைச் செய்ய, LLVM 8.0.0 வெளியீட்டில் புதிய பிழைகளைச் சரிபார்த்து கண்டுபிடிப்போம்.
எழுத வேண்டிய கட்டுரை
உண்மையைச் சொல்வதானால், நான் இந்தக் கட்டுரையை எழுத விரும்பவில்லை. நாங்கள் ஏற்கனவே பலமுறை சரிபார்த்த ஒரு திட்டத்தைப் பற்றி எழுதுவது சுவாரஸ்யமானது அல்ல (
ஒவ்வொரு முறையும் LLVM இன் புதிய பதிப்பு வெளியிடப்படும் அல்லது புதுப்பிக்கப்படும்
பாருங்கள், க்ளாங் ஸ்டேடிக் அனலைசரின் புதிய பதிப்பு புதிய பிழைகளைக் கண்டறிய கற்றுக்கொண்டது! பிவிஎஸ்-ஸ்டுடியோவைப் பயன்படுத்துவதன் பொருத்தம் குறைந்து வருவதாக எனக்குத் தோன்றுகிறது. க்ளாங் முன்பை விட அதிகமான பிழைகளைக் கண்டறிந்து, பிவிஎஸ்-ஸ்டுடியோவின் திறன்களைப் பிடிக்கிறது. இதை பற்றி நீங்கள் என்ன நினைக்கின்றீர்கள்?
இதற்கு நான் எப்பொழுதும் பதில் சொல்ல விரும்புகிறேன்:
நாங்களும் சும்மா உட்கார மாட்டோம்! PVS-Studio பகுப்பாய்வியின் திறன்களை நாங்கள் கணிசமாக மேம்படுத்தியுள்ளோம். எனவே கவலைப்பட வேண்டாம், நாங்கள் முன்பு போலவே தொடர்ந்து செல்கிறோம்.
துரதிர்ஷ்டவசமாக, இது ஒரு மோசமான பதில். அதில் எந்த ஆதாரமும் இல்லை. அதனால்தான் இப்போது இந்தக் கட்டுரையை எழுதுகிறேன். எனவே, LLVM திட்டம் மீண்டும் ஒருமுறை சரிபார்க்கப்பட்டு அதில் பல்வேறு பிழைகள் கண்டறியப்பட்டுள்ளன. எனக்கு சுவாரஸ்யமாகத் தோன்றியவற்றை இப்போது காண்பிப்பேன். க்ளாங் ஸ்டேடிக் அனலைசர் இந்த பிழைகளை கண்டுபிடிக்க முடியாது (அல்லது அதன் உதவியுடன் அவ்வாறு செய்வது மிகவும் சிரமமாக உள்ளது). ஆனால் நம்மால் முடியும். மேலும், இந்த பிழைகள் அனைத்தையும் ஒரே மாலையில் கண்டுபிடித்து எழுதினேன்.
ஆனால் கட்டுரை எழுத பல வாரங்கள் ஆனது. இதையெல்லாம் உரையாக எழுத என்னால் முடியவில்லை :).
மூலம், பிழைகள் மற்றும் சாத்தியமான பாதிப்புகளை அடையாளம் காண பிவிஎஸ்-ஸ்டுடியோ பகுப்பாய்வியில் என்ன தொழில்நுட்பங்கள் பயன்படுத்தப்படுகின்றன என்பதில் நீங்கள் ஆர்வமாக இருந்தால், இதைப் பற்றி தெரிந்துகொள்ள நான் பரிந்துரைக்கிறேன்.
புதிய மற்றும் பழைய நோயறிதல்
ஏற்கனவே குறிப்பிட்டுள்ளபடி, சுமார் இரண்டு ஆண்டுகளுக்கு முன்பு LLVM திட்டம் மீண்டும் சரிபார்க்கப்பட்டது, மேலும் கண்டறியப்பட்ட பிழைகள் சரி செய்யப்பட்டன. இப்போது இந்த கட்டுரை ஒரு புதிய தொகுதி பிழைகளை வழங்கும். புதிய பிழைகள் ஏன் கண்டுபிடிக்கப்பட்டன? இதற்கு 3 காரணங்கள் உள்ளன:
- LLVM திட்டம் உருவாகி, பழைய குறியீட்டை மாற்றி புதிய குறியீட்டைச் சேர்க்கிறது. இயற்கையாகவே, மாற்றியமைக்கப்பட்ட மற்றும் எழுதப்பட்ட குறியீட்டில் புதிய பிழைகள் உள்ளன. நிலையான பகுப்பாய்வைத் தவறாமல் பயன்படுத்த வேண்டும், எப்போதாவது அல்ல என்பதை இது தெளிவாக நிரூபிக்கிறது. எங்கள் கட்டுரைகள் பிவிஎஸ்-ஸ்டுடியோ பகுப்பாய்வியின் திறன்களைக் காட்டுகின்றன, ஆனால் இது குறியீட்டின் தரத்தை மேம்படுத்துவதற்கும் பிழைகளை சரிசெய்வதற்கான செலவைக் குறைப்பதற்கும் எந்த தொடர்பும் இல்லை. நிலையான குறியீடு பகுப்பாய்வியை தவறாமல் பயன்படுத்தவும்!
- தற்போதுள்ள நோய் கண்டறிதல்களை இறுதி செய்து மேம்படுத்தி வருகிறோம். எனவே, பகுப்பாய்வி முந்தைய ஸ்கேன்களின் போது கவனிக்காத பிழைகளை அடையாளம் காண முடியும்.
- PVS-ஸ்டுடியோவில் 2 ஆண்டுகளுக்கு முன்பு இல்லாத புதிய கண்டறிதல்கள் தோன்றியுள்ளன. பிவிஎஸ்-ஸ்டுடியோவின் வளர்ச்சியை தெளிவாகக் காட்ட தனிப் பிரிவில் அவற்றை முன்னிலைப்படுத்த முடிவு செய்தேன்.
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-Studio: 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().
ஒரு பிழை கையாளுபவரில் ஒரு பிழை மறைந்திருக்கும் போது இது மிகவும் பொதுவான சூழ்நிலையாகும், ஏனெனில் அவற்றை யாரும் சோதிக்க மாட்டார்கள். நிலையான பகுப்பாய்விகள் எவ்வளவு அடிக்கடி பயன்படுத்தப்பட்டாலும், அணுகக்கூடிய அனைத்து குறியீடுகளையும் சரிபார்க்கும். நிலையான பகுப்பாய்வு மற்ற சோதனை மற்றும் பிழை பாதுகாப்பு நுட்பங்களை எவ்வாறு பூர்த்தி செய்கிறது என்பதற்கு இது ஒரு சிறந்த எடுத்துக்காட்டு.
இதேபோன்ற சுட்டி கையாளுதல் பிழை RHS கீழே உள்ள குறியீட்டில் அனுமதிக்கப்பட்டுள்ளது: V522 [CWE-476] பூஜ்ய சுட்டி 'RHS' ஐ நீக்குதல் நடைபெறலாம். TGPparser.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] பூஜ்ய சுட்டிக்காட்டி 'ProgClone' ஐ நீக்குதல் நடைபெறலாம். Miscompilation.cpp 601
ஆரம்பத்தில் ஒரு ஸ்மார்ட் பாயிண்டர் ProgClone பொருளை சொந்தமாக்குவதை நிறுத்துகிறது:
BD.setNewProgram(std::move(ProgClone));
உண்மையில், இப்போது ProgClone ஒரு பூஜ்ய சுட்டி ஆகும். எனவே, ஒரு பூஜ்ய சுட்டி dereference கீழே நிகழ வேண்டும்:
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-Studio எச்சரிக்கை: 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-Studio எச்சரிக்கை: 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-ஸ்டுடியோ எச்சரிக்கை:
ஆரம்பத்தில் மிகவும் சந்தேகத்திற்குரிய ஆபரேட்டர் இருக்கிறார் இடைவெளி. இங்கே வேறு ஏதாவது எழுத மறந்துவிட்டீர்களா?
Fragment N12: 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-ஸ்டுடியோ எச்சரிக்கை:
சுட்டிக்காட்டி கால்லீ தொடக்கத்தில் செயல்பாடு அழைக்கப்படும் நேரத்தில் dereference செய்யப்படுகிறது getTTI.
பின்னர் இந்த சுட்டிக்காட்டி சமத்துவத்திற்காக சரிபார்க்கப்பட வேண்டும் என்று மாறிவிடும் nullptr:
if (!Callee || Callee->isDeclaration())
ஆனால் அது மிகவும் தாமதமானது…
Fragment N13 - N...: dereferens செய்த பிறகு ஒரு சுட்டியைச் சரிபார்க்கிறது
முந்தைய குறியீடு துண்டில் விவாதிக்கப்பட்ட நிலைமை தனித்துவமானது அல்ல. இது இங்கே தோன்றும்:
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 எச்சரிக்கை: 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-Studio எச்சரிக்கை: V595 [CWE-476] 'ND' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்படுவதற்கு முன்பு பயன்படுத்தப்பட்டது. வரிகளை சரிபார்க்கவும்: 532, 534. SemaTemplateInstantiateDecl.cpp 532
மற்றும் இங்கே:
- V595 [CWE-476] 'U' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்படுவதற்கு முன்பு பயன்படுத்தப்பட்டது. வரிகளை சரிபார்க்கவும்: 404, 407. DWARFormValue.cpp 404
- V595 [CWE-476] 'ND' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்படுவதற்கு முன்பு பயன்படுத்தப்பட்டது. வரிகளை சரிபார்க்கவும்: 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-Studio எச்சரிக்கை: 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, பின்னர் செயல்பாடு அதன் செயல்பாட்டை நிறைவு செய்யும்.
ஐடிகள் 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 முன்னுரிமை ஒருபோதும் அழிக்கப்படாது.
துண்டு 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 மதிப்பை வழங்கும் என்று வைத்துக் கொண்டு, வெளிப்பாட்டின் மதிப்பைக் கணக்கிடுவோம்:
~(getStubAlinment() - 1)
~(8u-1)
0xFFFFFFFF8u
மாறி என்பதை இப்போது கவனியுங்கள் தரவு அளவு 64-பிட் கையொப்பமிடப்படாத வகையைக் கொண்டுள்ளது. DataSize & 0xFFFFFFF8u செயல்பாட்டைச் செய்யும்போது, அனைத்து முப்பத்திரண்டு உயர்-வரிசை பிட்களும் பூஜ்ஜியத்திற்கு மீட்டமைக்கப்படும். பெரும்பாலும், இது புரோகிராமர் விரும்பியதல்ல. DataSize & 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-Studio: 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
மாறி பி.டி.ஆர் சமமாக இருக்கலாம் 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-Studio எச்சரிக்கை: 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-Studio எச்சரிக்கை: V1004 [CWE-476] 'PtrTy' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்பட்ட பிறகு பாதுகாப்பற்ற முறையில் பயன்படுத்தப்பட்டது. வரிகளை சரிபார்க்கவும்: 960, 965. InterleavedLoadCombinePass.cpp 965
இத்தகைய தவறுகளிலிருந்து உங்களை எவ்வாறு பாதுகாத்துக் கொள்வது? கோட்-ரிவியூவில் அதிக கவனத்துடன் இருக்கவும், உங்கள் குறியீட்டை தொடர்ந்து சரிபார்க்க PVS-Studio ஸ்டேடிக் அனலைசரைப் பயன்படுத்தவும்.
இந்த வகை பிழைகளுடன் மற்ற குறியீடு துண்டுகளை மேற்கோள் காட்டுவதில் எந்த அர்த்தமும் இல்லை. கட்டுரையில் எச்சரிக்கைகளின் பட்டியலை மட்டும் விட்டுவிடுகிறேன்:
- V1004 [CWE-476] 'Expr' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்பட்ட பிறகு பாதுகாப்பற்ற முறையில் பயன்படுத்தப்பட்டது. வரிகளை சரிபார்க்கவும்: 1049, 1078. DebugInfoMetadata.cpp 1078
- V1004 [CWE-476] 'PI' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்பட்ட பிறகு பாதுகாப்பற்ற முறையில் பயன்படுத்தப்பட்டது. வரிகளை சரிபார்க்கவும்: 733, 753. LegacyPassManager.cpp 753
- V1004 [CWE-476] 'StatepointCall' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்பட்ட பிறகு பாதுகாப்பற்ற முறையில் பயன்படுத்தப்பட்டது. வரிகளை சரிபார்க்கவும்: 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] 'TC' சுட்டிக்காட்டி nullptr க்கு எதிராக சரிபார்க்கப்பட்ட பிறகு பாதுகாப்பற்ற முறையில் பயன்படுத்தப்பட்டது. சோதனை வரிகள்: 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] உரிமையாளர் இல்லாத ஒரு சுட்டி 'ஏஏஸ்' கொள்கலனில் 'எம்ப்ளேஸ்_பேக்' முறையில் சேர்க்கப்படுகிறது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். AliasAnalysis.h 324
- V1023 [CWE-460] உரிமையாளர் இல்லாத ஒரு சுட்டி, 'Emplace_back' முறையில் 'Entries' கொள்கலனில் சேர்க்கப்பட்டது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். DWARFDebugFrame.cpp 519
- V1023 [CWE-460] உரிமையாளர் இல்லாத ஒரு சுட்டி 'Emplace_back' முறையில் 'AllEdges' கொள்கலனில் சேர்க்கப்பட்டது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். CFGMST.h 268
- V1023 [CWE-460] உரிமையாளர் இல்லாத ஒரு சுட்டி 'VMaps' கொள்கலனில் 'emplace_back' முறையில் சேர்க்கப்படுகிறது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். SimpleLoopUnswitch.cpp 2012
- V1023 [CWE-460] உரிமையாளர் இல்லாத ஒரு சுட்டி 'Records' கொள்கலனில் 'emplace_back' முறையில் சேர்க்கப்படுகிறது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். FDRLogBuilder.h 30
- V1023 [CWE-460] உரிமையாளர் இல்லாத ஒரு சுட்டி 'எம்ப்ளேஸ்_பேக்' முறையில் 'PendingSubmodules' கண்டெய்னரில் சேர்க்கப்பட்டது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். 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' கண்டெய்னரில் 'emplace_back' முறையில் சேர்க்கப்படுகிறது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். GlobalISelEmitter.cpp 1911
- V1023 [CWE-460] 'எம்ப்ளேஸ்_பேக்' முறையில் 'ஸ்டாஷ்' கொள்கலனில் உரிமையாளர் இல்லாத ஒரு சுட்டி சேர்க்கப்படுகிறது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். GlobalISelEmitter.cpp 2100
- V1023 [CWE-460] உரிமையாளர் இல்லாத ஒரு சுட்டி 'எம்ப்ளேஸ்_பேக்' முறையில் 'மேட்சர்ஸ்' கொள்கலனில் சேர்க்கப்பட்டது. விதிவிலக்கு ஏற்பட்டால் நினைவக கசிவு ஏற்படும். GlobalISelEmitter.cpp 2702
முடிவுக்கு
நான் மொத்தம் 60 எச்சரிக்கைகளை விடுத்து, பிறகு நிறுத்தினேன். LLVM இல் PVS-ஸ்டுடியோ பகுப்பாய்வி கண்டறியும் பிற குறைபாடுகள் உள்ளதா? ஆமாம் என்னிடம் இருக்கிறது. இருப்பினும், நான் கட்டுரைக்கான குறியீட்டு துண்டுகளை எழுதும் போது, அது மாலை தாமதமாக இருந்தது, அல்லது இரவில் கூட இருந்தது, மேலும் அதை ஒரு நாள் என்று அழைக்க வேண்டிய நேரம் இது என்று முடிவு செய்தேன்.
நீங்கள் அதை சுவாரஸ்யமாகக் கண்டீர்கள் என்று நம்புகிறேன் மற்றும் PVS-ஸ்டுடியோ பகுப்பாய்வியை முயற்சிக்க விரும்புவீர்கள்.
நீங்கள் பகுப்பாய்வியைப் பதிவிறக்கம் செய்து, மைன்ஸ்வீப்பர் விசையைப் பெறலாம்
மிக முக்கியமாக, நிலையான பகுப்பாய்வைப் பயன்படுத்தவும். ஒரு முறை சோதனைகள், நிலையான பகுப்பாய்வு மற்றும் PVS-ஸ்டுடியோவின் முறையைப் பிரபலப்படுத்த எங்களால் மேற்கொள்ளப்பட்டது ஒரு சாதாரண சூழ்நிலை அல்ல.
உங்கள் குறியீட்டின் தரம் மற்றும் நம்பகத்தன்மையை மேம்படுத்துவதில் நல்ல அதிர்ஷ்டம்!
ஆங்கிலம் பேசும் பார்வையாளர்களுடன் இந்தக் கட்டுரையைப் பகிர விரும்பினால், மொழிபெயர்ப்பு இணைப்பைப் பயன்படுத்தவும்: Andrey Karpov.
ஆதாரம்: www.habr.com