Menganalisa Kode ROOT, Kerangka Analisis Data Ilmiah

Gambar 3
Sementara Stockholm mengadakan Pekan Nobel ke-118, saya duduk di kantor kami, tempat kami mengembangkan penganalisa statis PVS-Studio, mengerjakan kajian analisis proyek ROOT, kerangka kerja pemrosesan data besar yang digunakan dalam penelitian ilmiah. Kode ini tidak akan memenangkan hadiah, tentu saja, tetapi penulis pasti dapat mengandalkan ulasan terperinci dari cacat yang paling menarik plus lisensi gratis untuk memeriksa sendiri proyek tersebut secara menyeluruh.

Pendahuluan


Gambar 1

ROOT adalah perangkat lunak perangkat lunak ilmiah modular. Ini menyediakan semua fungsi yang diperlukan untuk menangani pemrosesan data besar, analisis statistik, visualisasi, dan penyimpanan. Ini terutama ditulis dalam C ++. ROOT lahir di CERN , di jantung penelitian tentang fisika energi tinggi. Setiap hari, ribuan fisikawan menggunakan aplikasi ROOT untuk menganalisis data mereka atau melakukan simulasi.

PVS-Studio adalah alat untuk mendeteksi bug perangkat lunak dan kerentanan potensial dalam kode sumber program yang ditulis dalam C, C ++, C #, dan Java. Ini berjalan pada Windows 64-bit, Linux, dan macOS dan dapat menganalisis kode sumber yang ditulis untuk platform ARM 32-bit, 64-bit, dan tertanam.

Debut diagnostik baru


V1046 Penggunaan bool 'dan' int 'yang tidak aman secara bersamaan dalam operasi' & = '. GSLMultiRootFinder.h 175

int AddFunction(const ROOT::Math::IMultiGenFunction & func) { ROOT::Math::IMultiGenFunction * f = func.Clone(); if (!f) return 0; fFunctions.push_back(f); return fFunctions.size(); } template<class FuncIterator> bool SetFunctionList( FuncIterator begin, FuncIterator end) { bool ret = true; for (FuncIterator itr = begin; itr != end; ++itr) { const ROOT::Math::IMultiGenFunction * f = *itr; ret &= AddFunction(*f); } return ret; } 

Pertama, ini adalah bug luar biasa yang ditemukan oleh versi beta PVS-Studio, yang saya gunakan untuk ulasan ini.

Harapan Fungsi SetFunctionList melintasi daftar iterator. Jika setidaknya satu iterator tidak valid, fungsi mengembalikan false , atau true sebaliknya.

Realita Fungsi SetFunctionList dapat mengembalikan false bahkan untuk iterator yang valid. Mari kita cari tahu alasannya. Fungsi AddFunction mengembalikan jumlah iterator yang valid pada daftar fFunctions . Artinya, menambahkan iterator non-nol akan menyebabkan daftar semakin bertambah ukurannya: 1, 2, 3, 4, dan seterusnya. Di sinilah bug berperan:

 ret &= AddFunction(*f); 

Karena fungsi mengembalikan nilai tipe int daripada bool , operasi '& =' akan mengembalikan false untuk nilai genap karena bit paling tidak signifikan dari angka genap selalu disetel ke nol. Ini adalah bagaimana salah satu bug halus dapat mematahkan nilai kembali dari SetFunctionsList bahkan ketika argumen yang valid.

Gambar 2

Kesalahan dalam ekspresi bersyarat


V501 Ada sub-ekspresi identik ke kiri dan ke kanan operator '&&': module && module rootcling_impl.cxx 3650

 virtual void HandleDiagnostic(....) override { .... bool isROOTSystemModuleDiag = module && ....; bool isSystemModuleDiag = module && module && module->IsSystem; if (!isROOTSystemModuleDiag && !isSystemModuleDiag) fChild->HandleDiagnostic(DiagLevel, Info); .... } 

Mari kita mulai dengan bug yang paling tidak berbahaya. Penunjuk modul diperiksa dua kali. Salah satu pemeriksaan mungkin berlebihan, namun masih akan bijaksana untuk memperbaikinya untuk menghindari kebingungan di masa depan.

V501 Ada sub-ekspresi identik 'strchr (fHostAuth-> GetHost (),' * ')' di sebelah kiri dan di sebelah kanan '||' operator. TAuthenticate.cxx 300

 TAuthenticate::TAuthenticate(TSocket *sock, const char *remote, const char *proto, const char *user) { .... // If generic THostAuth (ie with wild card or user == any) // make a personalized memory copy of this THostAuth if (strchr(fHostAuth->GetHost(),'*') || strchr(fHostAuth->GetHost(),'*') || fHostAuth->GetServer() == -1 ) { fHostAuth = new THostAuth(*fHostAuth); fHostAuth->SetHost(fqdn); fHostAuth->SetUser(checkUser); fHostAuth->SetServer(servtype); } .... } 

String fHostAuth-> GetHost () dipindai untuk karakter '*' dua kali. Salah satu cek ini mungkin dimaksudkan untuk mencari '?' karakter seperti dua karakter ini biasanya yang digunakan untuk menentukan berbagai topeng karakter.

V517 Penggunaan pola 'jika (A) {...} else jika (A) {...}' terdeteksi. Ada kemungkinan kehadiran kesalahan logis. Periksa baris: 163, 165. TProofMonSenderML.cxx 163

 Int_t TProofMonSenderML::SendSummary(TList *recs, const char *id) { .... if (fSummaryVrs == 0) { if ((dsn = recs->FindObject("dataset"))) recs->Remove(dsn); } else if (fSummaryVrs == 0) { // Only the first records xrecs = new TList; xrecs->SetOwner(kFALSE); TIter nxr(recs); TObject *o = 0; while ((o = nxr())) { if (!strcmp(o->GetName(), "vmemmxw")) break; xrecs->Add(o); } } .... } 

Variabel fSummaryVrs dibandingkan dengan nol dua kali, jadi eksekusi tidak pernah mencapai kode di cabang else-if . Dan ada sedikit kode di sana ...

V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. TKDTree.cxx 805

 template <typename Index, typename Value> void TKDTree<Index, Value>::UpdateRange(....) { .... if (point[fAxis[inode]]<=fValue[inode]){ //first examine the node that contains the point UpdateRange(GetLeft(inode),point, range, res); UpdateRange(GetRight(inode),point, range, res); } else { UpdateRange(GetLeft(inode),point, range, res); UpdateRange(GetRight(inode),point, range, res); } .... } 

Blok kode yang sama, yang merupakan tiruan salin-tempel, dijalankan apa pun kondisinya. Saya kira ada kebingungan antara kata-kata kiri dan kanan .

Proyek ini penuh dengan bintik-bintik yang mencurigakan seperti itu:

  • V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. TContainerConverters.cxx 51
  • V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. TWebFile.cxx 1310
  • V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. MethodMLP.cxx 423
  • V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. RooAbsCategory.cxx 394

Ekspresi V547 '! File_name_value.empty ()' selalu salah. SelectionRules.cxx 1423

 bool SelectionRules::AreAllSelectionRulesUsed() const { for(auto&& rule : fClassSelectionRules){ .... std::string file_name_value; if (!rule.GetAttributeValue("file_name", file_name_value)) file_name_value.clear(); if (!file_name_value.empty()) { // <= // don't complain about defined_in rules continue; } const char* attrName = nullptr; const char* attrVal = nullptr; if (!file_name_value.empty()) { // <= attrName = "file name"; attrVal = file_name_value.c_str(); } else { attrName = "class"; if (!name.empty()) attrVal = name.c_str(); } ROOT::TMetaUtils::Warning(0,"Unused %s rule: %s\n", attrName, attrVal); } .... } 

Ini mungkin bukan bug; penganalisa baru saja menemukan beberapa kode yang dapat disederhanakan. Karena nilai pengembalian file_name_value.empty () sudah diperiksa pada awal loop, pemeriksaan duplikat kedua dapat dihapus, sehingga membuang sejumlah besar kode yang tidak perlu.

V590 Pertimbangkan untuk memeriksa '! File1 || c <= 0 || c == '*' || c! = '(' 'ekspresi. Ekspresi ini berlebihan atau mengandung salah cetak. TTabCom.cxx 840

 TString TTabCom::DetermineClass(const char varName[]) { .... c = file1.get(); if (!file1 || c <= 0 || c == '*' || c != '(') { Error("TTabCom::DetermineClass", "variable \"%s\" not defined?", varName); goto cleanup; } .... } 

Inilah bagian masalah dari ekspresi kondisional yang dilaporkan oleh penganalisa:

 if (.... || c == '*' || c != '(') { .... } 

Pemeriksaan untuk karakter tanda bintang tidak akan mempengaruhi hasil kondisi. Bagian ini akan selalu berlaku untuk karakter apa pun selain '('. Anda dapat dengan mudah memeriksanya sendiri dengan menggambar tabel kebenaran.

Dua peringatan lagi tentang kondisi dengan logika aneh:

  • V590 Pertimbangkan untuk memeriksa ungkapan ini. Ekspresi berlebihan atau mengandung kesalahan cetak. TFile.cxx 3963
  • V590 Pertimbangkan untuk memeriksa ungkapan ini. Ekspresi berlebihan atau mengandung kesalahan cetak. TStreamerInfoActions.cxx 3084

V593 Pertimbangkan untuk meninjau ekspresi dari jenis 'A = B <C'. Ekspresi dihitung sebagai berikut: 'A = (B <C)'. TProofServ.cxx 1903

 Int_t TProofServ::HandleSocketInput(TMessage *mess, Bool_t all) { .... if (Int_t ret = fProof->AddWorkers(workerList) < 0) { Error("HandleSocketInput:kPROOF_GETSLAVEINFO", "adding a list of worker nodes returned: %d", ret); } .... } 

Bug ini mengungkapkan dirinya hanya dalam kasus perilaku program yang salah. Variabel ret seharusnya menyimpan kode pengembalian fungsi AddWorkers dan menulis nilai itu ke log jika ada kondisi kesalahan. Tetapi itu tidak berfungsi sebagaimana dimaksud. Kondisi ini tidak memiliki tanda kurung tambahan yang memaksa urutan evaluasi yang diinginkan. Apa yang sebenarnya disimpan oleh variabel ret bukanlah kode pengembalian tetapi hasil dari perbandingan logis, yaitu 0 atau 1.

Masalah serupa lainnya:

  • V593 Pertimbangkan untuk meninjau ekspresi dari jenis 'A = B <C'. Ekspresi dihitung sebagai berikut: 'A = (B <C)'. TProofServ.cxx 3897

V768 Konstanta enumerasi 'kCostComplexityPruning' digunakan sebagai variabel tipe-Boolean. MethodDT.cxx 283
 enum EPruneMethod {kExpectedErrorPruning=0, kCostComplexityPruning, kNoPruning}; void TMVA::MethodDT::ProcessOptions() { .... if (fPruneStrength < 0) fAutomatic = kTRUE; else fAutomatic = kFALSE; if (fAutomatic && fPruneMethod==!DecisionTree::kCostComplexityPruning){ Log() << kFATAL << "Sorry automatic pruning strength determination is ...." << Endl; } .... } 

Hm ... Kenapa meniadakan nilai konstan kCostComplexityPruning ? Saya menduga karakter negasi adalah kesalahan ketik, yang sekarang mendistorsi logika eksekusi.

Pointer menangani kesalahan


V522 Dereferencing dari pointer nol 'pre' mungkin terjadi. TSynapse.cxx 61

 void TSynapse::SetPre(TNeuron * pre) { if (pre) { Error("SetPre","this synapse is already assigned to a pre-neuron."); return; } fpre = pre; pre->AddPost(this); } 

Saya melakukan yang terbaik untuk mencoba memahami kode aneh ini, dan sepertinya idenya adalah untuk menghindari pemberian nilai baru ke bidang fpre . Jika demikian, programmer secara tidak sengaja memeriksa pointer yang salah. Implementasi saat ini mengarah ke mendereferensi pointer nol jika Anda melewatkan nilai nullptr ke fungsi SetPre .

Saya pikir cuplikan ini harus diperbaiki sebagai berikut:

 void TSynapse::SetPre(TNeuron * pre) { if (fpre) { Error("SetPre","this synapse is already assigned to a pre-neuron."); return; } fpre = pre; pre->AddPost(this); } 

Ini, bagaimanapun, tidak akan mencegah lewat dari null pointer ke fungsi, tapi setidaknya versi ini lebih logis konsisten daripada yang asli.

Klon yang sedikit dimodifikasi dari kode ini dapat ditemukan di tempat lain:

  • V522 Dereferencing dari 'pointer' pointer nol mungkin terjadi. TSynapse.cxx 74

V595 Pointer 'N' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 484, 488. Scanner.cxx 484

 bool RScanner::shouldVisitDecl(clang::NamedDecl *D) { if (auto M = D->getOwningModule()) { // <= 2 return fInterpreter.getSema().isModuleVisible(M); } return true; } bool RScanner::VisitNamespaceDecl(clang::NamespaceDecl* N) { if (fScanType == EScanType::kOnePCM) return true; if (!shouldVisitDecl(N)) // <= 1 return true; if((N && N->isImplicit()) || !N){ // <= 3 return true; } .... } 

Ini adalah kode yang sangat berbahaya! Pointer N tidak dicentang untuk null sebelum direferensikan pertama kali. Terlebih lagi, Anda tidak dapat melihat itu terjadi di sini karena dereference terjadi di dalam fungsi shouldVisitDecl .

Diagnosis ini secara tradisional menghasilkan banyak peringatan yang relevan. Berikut ini beberapa contohnya:

  • V595 Penunjuk 'file' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 141, 153. TFileCacheRead.cxx 141
  • V595 Pointer 'fFree' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 2029, 2038. TFile.cxx 2029
  • V595 Pointer 'tbuf' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 586, 591. TGText.cxx 586
  • V595 Pointer 'fPlayer' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 3425, 3430. TProof.cxx 3425
  • V595 Pointer 'gProofServ' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 1192, 1194. TProofPlayer.cxx 1192
  • V595 Pointer 'projDataTmp' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 791, 804. RooSimultaneous.cxx 791

Yang berikutnya bukan bug, tapi itu contoh lain bagaimana makro mendorong penulisan kode yang salah atau berlebihan.

V571 Cek berulang. Kondisi 'jika (fCanvasImp)' sudah diverifikasi di baris 799. TCanvas.cxx 800

 #define SafeDelete(p) { if (p) { delete p; p = 0; } } void TCanvas::Close(Option_t *option) { .... if (fCanvasImp) SafeDelete(fCanvasImp); .... } 

Pointer fCanvasImp diperiksa dua kali, dengan salah satu pemeriksaan sudah diterapkan di makro SafeDelete . Salah satu masalah dengan makro adalah bahwa mereka sulit dinavigasi dari dalam kode, yang merupakan alasan mengapa banyak programmer tidak memeriksa isinya sebelum digunakan.

Array menangani kesalahan


V519 Variabel 'Baris [Kursor]' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 352, 353. Editor.cpp 353

 size_t find_last_non_alnum(const std::string &str, std::string::size_type index = std::string::npos) { .... char tmp = Line.GetText()[Cursor]; Line[Cursor] = Line[Cursor - 1]; Line[Cursor] = tmp; .... } ]; size_t find_last_non_alnum(const std::string &str, std::string::size_type index = std::string::npos) { .... char tmp = Line.GetText()[Cursor]; Line[Cursor] = Line[Cursor - 1]; Line[Cursor] = tmp; .... } 

Baris Elemen [Kursor] diberi nilai baru, yang kemudian segera ditimpa. Itu tidak terlihat benar ...

V557 Array overrun dimungkinkan. Indeks 'ivar' menunjuk di luar batas array. BasicMinimizer.cxx 130

 bool BasicMinimizer::SetVariableValue(unsigned int ivar, double val) { if (ivar > fValues.size() ) return false; fValues[ivar] = val; return true; } 

Membuat kesalahan ini saat memeriksa indeks array adalah tren terbaru; kami melihatnya di hampir setiap proyek ketiga. Meskipun pengindeksan ke dalam array di dalam loop mudah - Anda biasanya menggunakan operator '<' untuk membandingkan indeks dengan ukuran array - memeriksa seperti yang ditunjukkan di atas memerlukan operator '> =', bukan '>'. Kalau tidak, Anda berisiko mengindeks satu elemen di luar batas array.

Bug ini telah dikloning di seluruh kode beberapa kali:

  • V557 Array overrun dimungkinkan. Indeks 'ivar' menunjuk di luar batas array. BasicMinimizer.cxx 186
  • V557 Array overrun dimungkinkan. Indeks 'ivar' menunjuk di luar batas array. BasicMinimizer.cxx 194
  • V557 Array overrun dimungkinkan. Indeks 'ivar' menunjuk di luar batas array. BasicMinimizer.cxx 209
  • V557 Array overrun dimungkinkan. Indeks 'ivar' menunjuk di luar batas array. BasicMinimizer.cxx 215
  • V557 Array overrun dimungkinkan. Indeks 'ivar' menunjuk di luar batas array. BasicMinimizer.cxx 230

V621 Pertimbangkan untuk memeriksa operator 'untuk'. Ada kemungkinan bahwa loop akan dieksekusi secara tidak benar atau tidak akan dieksekusi sama sekali. TDataMember.cxx 554

 Int_t TDataMember::GetArrayDim() const { if (fArrayDim<0 && fInfo) { R__LOCKGUARD(gInterpreterMutex); TDataMember *dm = const_cast<TDataMember*>(this); dm->fArrayDim = gCling->DataMemberInfo_ArrayDim(fInfo); // fArrayMaxIndex should be zero if (dm->fArrayDim) { dm->fArrayMaxIndex = new Int_t[fArrayDim]; for(Int_t dim = 0; dim < fArrayDim; ++dim) { dm->fArrayMaxIndex[dim] = gCling->DataMemberInfo_MaxIndex(fInfo,dim); } } } return fArrayDim; } 

Dalam for loop, para pengembang tampaknya bermaksud untuk membandingkan variabel redup dengan dm-> fArrayDim daripada fArrayDim . Nilai fArrayDim negatif, yang dijamin oleh kondisi di awal fungsi. Akibatnya, loop ini tidak akan pernah dijalankan.

V767 Akses mencurigakan ke elemen array 'saat ini' dengan indeks konstan di dalam satu loop. TClingUtils.cxx 3082

 llvm::StringRef ROOT::TMetaUtils::DataMemberInfo__ValidArrayIndex(....) { .... while (current!=0) { // Check the token if (isdigit(current[0])) { for(i=0;i<strlen(current);i++) { if (!isdigit(current[0])) { if (errstr) *errstr = current; if (errnum) *errnum = NOT_INT; return llvm::StringRef(); } } } else { // current token is not a digit .... } .... } .... } 

Kode ini parsing dan memeriksa beberapa string. Jika karakter pertama string saat ini (yaitu pada indeks 0) telah dikenali sebagai angka, loop akan melintasi semua karakter lainnya untuk memastikan semuanya adalah angka. Yah, setidaknya itulah idenya. Masalahnya adalah, penghitung i tidak digunakan dalam loop. Kondisi harus ditulis ulang sehingga memeriksa [i] saat ini daripada saat ini [0] .

Gambar 4

Kebocoran memori


V773 Fungsi itu keluar tanpa melepaskan pointer 'daftar pilihan'. Kebocoran memori dimungkinkan. TDataMember.cxx 355

 void TDataMember::Init(bool afterReading) { .... TList *optionlist = new TList(); //storage for options strings for (i=0;i<token_cnt;i++) { if (strstr(tokens[i],"Items")) { ptr1 = R__STRTOK_R(tokens[i], "()", &rest); if (ptr1 == 0) { Fatal("TDataMember","Internal error, found \"Items....",GetTitle()); return; } ptr1 = R__STRTOK_R(nullptr, "()", &rest); if (ptr1 == 0) { Fatal("TDataMember","Internal error, found \"Items....",GetTitle()); return; } .... } .... } .... // dispose of temporary option list... delete optionlist; .... } 

Pointer optionList tidak dibebaskan sebelum kembali dari fungsi. Saya tidak tahu apakah pembebasan seperti itu diperlukan dalam kasus khusus ini, tetapi ketika kami melaporkan kesalahan seperti itu, pengembang biasanya memperbaikinya. Itu semua tergantung pada apakah Anda ingin program Anda tetap berjalan jika ada kondisi kesalahan. ROOT memiliki banyak cacat seperti itu, jadi saya akan menyarankan penulis untuk memeriksa kembali proyek itu sendiri.

memset lagi


V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'x'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. TMD5.cxx 366

 void TMD5::Transform(UInt_t buf[4], const UChar_t in[64]) { UInt_t a, b, c, d, x[16]; .... // Zero out sensitive information memset(x, 0, sizeof(x)); } 

Banyak yang mengira komentar tidak akan sampai ke file biner setelah kompilasi, dan mereka benar sekali: D. Apa yang mungkin tidak diketahui beberapa orang adalah bahwa kompiler akan menghapus fungsi memset juga. Dan ini pasti akan terjadi. Jika buffer yang dimaksud tidak lagi digunakan lebih lanjut dalam kode, kompiler akan mengoptimalkan pemanggilan fungsi. Secara teknis, ini adalah keputusan yang masuk akal, tetapi jika buffer menyimpan data pribadi apa pun, data itu akan tetap ada. Ini adalah kelemahan keamanan klasik CWE-14 .

Lain-lain


Fungsi V591 Non-void harus mengembalikan nilai. LogLikelihoodFCN.h 108

 LogLikelihoodFCN & operator = (const LogLikelihoodFCN & rhs) { SetData(rhs.DataPtr() ); SetModelFunction(rhs.ModelFunctionPtr() ); fNEffPoints = rhs.fNEffPoints; fGrad = rhs.fGrad; fIsExtended = rhs.fIsExtended; fWeight = rhs.fWeight; fExecutionPolicy = rhs.fExecutionPolicy; } 

Operator kelebihan beban tidak memiliki nilai balik. Ini adalah tren terbaru lainnya.

V596 Objek telah dibuat tetapi tidak sedang digunakan. Kata kunci 'throw' dapat hilang: throw runtime_error (FOO); RTensor.hxx 363

 template <typename Value_t, typename Container_t> inline RTensor<Value_t, Container_t> RTensor<Value_t, Container_t>::Transpose() { if (fLayout == MemoryLayout::RowMajor) { fLayout = MemoryLayout::ColumnMajor; } else if (fLayout == MemoryLayout::ColumnMajor) { fLayout = MemoryLayout::RowMajor; } else { std::runtime_error("Memory layout is not known."); } .... } 

Masalahnya adalah bahwa programmer tidak sengaja meninggalkan kata kunci throw , sehingga mencegah pelemparan pengecualian jika terjadi kesalahan.

Hanya ada dua peringatan dari jenis ini. Inilah yang kedua:

  • V596 Objek telah dibuat tetapi tidak sedang digunakan. Kata kunci 'throw' dapat hilang: throw runtime_error (FOO); Forest.hxx 137

V609 Bagilah dengan nol. Kisaran penyebut [0..100]. TGHtmlImage.cxx 340

 const char *TGHtml::GetPctWidth(TGHtmlElement *p, char *opt, char *ret) { int n, m, val; .... if (n < 0 || n > 100) return z; if (opt[0] == 'h') { val = fCanvas->GetHeight() * 100; } else { val = fCanvas->GetWidth() * 100; } if (!fInTd) { snprintf(ret, 15, "%d", val / n); // <= } else { .... } .... } 

Yang ini mirip dengan contoh penanganan array yang dibahas sebelumnya. Variabel n terbatas pada rentang dari 0 hingga 100. Tapi kemudian ada cabang yang melakukan pembagian oleh variabel n yang mungkin memiliki nilai 0. Saya pikir batas kisaran n harus diperbaiki sebagai berikut:

 if (n <= 0 || n > 100) return z; 

V646 Pertimbangkan Memeriksa logika aplikasi. Mungkin kata kunci 'lain' tidak ada. TProofServ.cxx 729

 TProofServ::TProofServ(Int_t *argc, char **argv, FILE *flog) : TApplication("proofserv", argc, argv, 0, -1) { .... if (!logmx.IsDigit()) { if (logmx.EndsWith("K")) { xf = 1024; logmx.Remove(TString::kTrailing, 'K'); } else if (logmx.EndsWith("M")) { xf = 1024*1024; logmx.Remove(TString::kTrailing, 'M'); } if (logmx.EndsWith("G")) { xf = 1024*1024*1024; logmx.Remove(TString::kTrailing, 'G'); } } .... } , argv, TProofServ::TProofServ(Int_t *argc, char **argv, FILE *flog) : TApplication("proofserv", argc, argv, 0, -1) { .... if (!logmx.IsDigit()) { if (logmx.EndsWith("K")) { xf = 1024; logmx.Remove(TString::kTrailing, 'K'); } else if (logmx.EndsWith("M")) { xf = 1024*1024; logmx.Remove(TString::kTrailing, 'M'); } if (logmx.EndsWith("G")) { xf = 1024*1024*1024; logmx.Remove(TString::kTrailing, 'G'); } } .... } 

Penganalisa melaporkan pernyataan if yang diformat aneh dengan kata kunci lain yang hilang. Tampilan kode ini menunjukkan bahwa kode itu perlu diperbaiki.

Beberapa lagi peringatan dari jenis ini:

  • V646 Pertimbangkan untuk memeriksa logika aplikasi. Mungkin kata kunci 'lain' tidak ada. TFormula_v5.cxx 3702
  • V646 Pertimbangkan untuk memeriksa logika aplikasi. Mungkin kata kunci 'lain' tidak ada. RooAbsCategory.cxx 604

V663 Infinite loop dimungkinkan. Kondisi 'cin.eof ()' tidak cukup untuk memutuskan dari loop. Pertimbangkan untuk menambahkan pemanggilan fungsi 'cin.fail ()' ke ekspresi kondisional. MethodKNN.cxx 602

 void TMVA::MethodKNN::ReadWeightsFromStream(std::istream& is) { .... while (!is.eof()) { std::string line; std::getline(is, line); if (line.empty() || line.find("#") != std::string::npos) { continue; } .... } .... } 

Ketika bekerja dengan kelas std :: istream , memanggil fungsi eof () tidak cukup untuk mengakhiri loop. The eof () fungsi akan selalu kembali false jika data tidak dapat dibaca, dan tidak ada titik terminasi lain dalam kode ini. Untuk menjamin berakhirnya loop, diperlukan pemeriksaan tambahan untuk nilai yang dikembalikan oleh fungsi gagal () :

 while (!is.eof() && !is.fail()) { .... } 

Sebagai alternatif, dapat ditulis ulang sebagai berikut:

 while (is) { .... } 

V678 Objek digunakan sebagai argumen untuk metode sendiri. Pertimbangkan untuk memeriksa argumen aktual pertama dari fungsi 'Salin'. TFormLeafInfo.cxx 2414

 TFormLeafInfoMultiVarDim::TFormLeafInfoMultiVarDim( const TFormLeafInfoMultiVarDim& orig) : TFormLeafInfo(orig) { fNsize = orig.fNsize; fSizes.Copy(fSizes); // <= fCounter2 = orig.fCounter2?orig.fCounter2->DeepCopy():0; fSumOfSizes = orig.fSumOfSizes; fDim = orig.fDim; fVirtDim = orig.fVirtDim; fPrimaryIndex = orig.fPrimaryIndex; fSecondaryIndex = orig.fSecondaryIndex; } orig) TFormLeafInfoMultiVarDim::TFormLeafInfoMultiVarDim( const TFormLeafInfoMultiVarDim& orig) : TFormLeafInfo(orig) { fNsize = orig.fNsize; fSizes.Copy(fSizes); // <= fCounter2 = orig.fCounter2?orig.fCounter2->DeepCopy():0; fSumOfSizes = orig.fSumOfSizes; fDim = orig.fDim; fVirtDim = orig.fVirtDim; fPrimaryIndex = orig.fPrimaryIndex; fSecondaryIndex = orig.fSecondaryIndex; } 

Mari kita selesaikan artikel dengan kesalahan ketik kecil yang menyenangkan ini. Fungsi Salin harus dipanggil dengan orig.fSizes , bukan fSizes .

Kesimpulan


Satu tahun yang lalu Tentang, Situs kami memeriksa Hanya NCBI Workbench Genome proyek, yang merupakan program lain Digunakan dalam penelitian ilmiah yang berkaitan dengan analisis genom. Saya menyebutkan ini karena kualitas perangkat lunak ilmiah sangat penting, namun pengembang cenderung meremehkannya.

Omong-omong, macOS 10.15 Catalina dirilis kemarin, di mana mereka berhenti mendukung aplikasi 32-bit. Untungnya, PVS-Studio menawarkan serangkaian besar diagnostik yang dirancang khusus untuk mendeteksi bug yang menyertai porting program ke sistem 64-bit. Pelajari lebih lanjut di pos ini oleh tim PVS-Studio.

Source: https://habr.com/ru/post/id472492/


All Articles