Analyse de code ROOT - cadre d'analyse des données de recherche

Image 3
Alors que la 118e semaine Nobel se déroulait à Stockholm, un examen du code du projet ROOT utilisé dans la recherche scientifique pour le traitement des mégadonnées était en cours de préparation au bureau de développement de l'analyseur de code statique PVS-Studio. Bien sûr, vous ne donnerez pas de bonus pour un tel code, mais les développeurs recevront un examen détaillé des défauts de code intéressants et une licence pour une vérification complète du projet.

Présentation


Image 1

ROOT - un ensemble d'utilitaires pour travailler avec des données de recherche scientifique. Il fournit toutes les fonctionnalités nécessaires au traitement des mégadonnées, à l'analyse statistique, à la visualisation et au stockage. Il est principalement écrit en C ++. Le développement a commencé au CERN (Organisation européenne pour la recherche nucléaire) pour la recherche en physique des hautes énergies. Chaque jour, des milliers de physiciens utilisent des applications ROOT pour analyser leurs données ou simuler.

PVS-Studio est un outil pour détecter les erreurs et les vulnérabilités potentielles dans le code source des programmes écrits en C, C ++, C # et Java. Il fonctionne sur les systèmes 64 bits sous Windows, Linux et macOS et peut analyser le code conçu pour les plates-formes ARM 32 bits, 64 bits et intégrées.

Début des nouveaux diagnostics


V1046 Utilisation non sûre des types bool 'et' int 'ensemble dans l'opération' & = '. 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; } 

La version bêta de l'analyseur qui a été utilisée lors de la vérification a trouvé une erreur si terrible.

J'attends. La fonction SetFunctionList contourne la liste des itérateurs. Si au moins l'un d'entre eux n'est pas valide, la valeur de retour sera fausse , sinon vraie .

La réalité La fonction SetFunctionList peut retourner false même pour les itérateurs valides. Nous allons examiner la situation: AddFunction renvoie le nombre d'itérateurs valides dans la liste fFunctions . C'est-à-dire lors de l'ajout d'itérateurs non nuls, la taille de cette liste augmentera séquentiellement: 1, 2, 3, 4, etc. C'est là que l'erreur dans le code commence à se manifester:

 ret &= AddFunction(*f); 

Parce que Puisque la fonction retourne un résultat de type int , pas bool , l'opération '& =' avec des nombres pairs donnera la valeur false . Après tout, le bit de nombre pair le moins significatif sera toujours nul. Par conséquent, une telle erreur non évidente gâchera le résultat de la fonction SetFunctionsList même pour des arguments valides.

Image 2

Erreurs dans les expressions conditionnelles


V501 Il existe des sous-expressions identiques à gauche et à droite de l'opérateur '&&': 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); .... } 

Commençons par l'exemple le plus inoffensif. Le pointeur du module est vérifié deux fois. Très probablement, une vérification n'est pas nécessaire. Mais il vaut mieux corriger le code afin qu’il n’y ait pas de questions inutiles.

V501 Il existe des sous-expressions identiques 'strchr (fHostAuth-> GetHost (),' * ')' à gauche et à droite de '||' opérateur. 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); } .... } 

Ici, dans la ligne fHostAuth-> GetHost (), le même caractère est recherché - '*'. Peut-être que l'un d'eux devrait être le symbole «?». Ils sont généralement utilisés pour définir différents masques.

V517 L'utilisation du modèle 'if (A) {...} else if (A) {...}' a été détectée. Il y a une probabilité de présence d'erreur logique. Vérifiez les lignes: 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); } } .... } 

La variable fSummaryVrs est comparée deux fois à zéro. Cela provoque l'exécution du code dans la branche else-if. Beaucoup de code y est écrit ...

V523 L' instruction 'then' est équivalente à l'instruction 'else'. 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); } .... } 

Le même code copier-coller est exécuté dans n'importe quelle condition. Il y a peut-être une faute de frappe dans les mots gauche et droite .

Il y a beaucoup de code suspect similaire dans le projet:

  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. TContainerConverters.cxx 51
  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. TWebFile.cxx 1310
  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. MethodMLP.cxx 423
  • V523 L'instruction 'then' est équivalente à l'instruction 'else'. RooAbsCategory.cxx 394

V547 L' expression '! File_name_value.empty ()' est toujours fausse. 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); } .... } 

Très probablement, il n'y a pas d'erreur. L'analyseur a détecté un code pouvant être raccourci. Parce que Étant donné que la valeur de file_name_value.empty () est vérifiée au début de la boucle, puis plus bas dans le code, cette vérification peut être supprimée, réduisant considérablement la quantité de code inutile.

V590 Envisagez d'inspecter le '! File1 || c <= 0 || c == '*' || c! = '(' 'expression. L'expression est excessive ou contient une erreur d'impression. 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; } .... } 

Considérez la partie abrégée de l'expression conditionnelle pointée par l'analyseur:

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

La condition ne dépend pas de l’égalité ou non du symbole «astérisque». Cette partie de l'expression conditionnelle sera toujours vraie pour tout caractère autre que '('. Ceci est facile à voir si vous construisez une table de vérité.

Deux autres endroits avec une logique étrange dans les expressions conditionnelles:

  • V590 Envisagez d'inspecter cette expression. L'expression est excessive ou contient une erreur d'impression. TFile.cxx 3963
  • V590 Envisagez d'inspecter cette expression. L'expression est excessive ou contient une erreur d'impression. TStreamerInfoActions.cxx 3084

V593 Envisagez de revoir l'expression du type «A = B <C». L'expression est calculée comme suit: «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); } .... } 

L'erreur détectée par l'analyseur ne se manifeste que lorsque le programme ne fonctionne pas correctement. La variable ret doit stocker le code retour de la fonction AddWorkers et, en cas d'urgence, afficher cette valeur dans le journal. Mais le code ne fonctionne pas comme ça. La condition manque de crochets supplémentaires qui spécifient la priorité des opérations. Pas le code retour, mais le résultat d'une comparaison logique est stocké dans la variable ret , c'est-à-dire seulement 0 ou 1.

Il y a un autre endroit avec un défaut similaire:

  • V593 Envisagez de revoir l'expression du type «A = B <C». L'expression est calculée comme suit: «A = (B <C)». TProofServ.cxx 3897

V768 La constante d'énumération 'kCostComplexityPruning' est utilisée comme variable de type booléen. 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; } .... } 

Hmm ... Pourquoi la négation de la valeur constante de kCostComplexityPruning ? Très probablement, le symbole de négation a été accidentellement ajouté et conduit maintenant à une logique incorrecte d'exécution de code.

Code non valide avec des pointeurs


V522 Le déréférencement du pointeur nul «pre» peut avoir lieu. 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); } 

J'ai essayé de comprendre ce code bizarre. Il semble que l'idée ne consiste pas à définir une nouvelle valeur pour le champ fpre . Ensuite, ils ont fait une erreur en confondant le pointeur, qui devrait être vérifié dans l'état. Dans l'implémentation actuelle, si vous transmettez la valeur nullptr à la fonction SetPre , le pointeur null est déréférencé.

Très probablement, c'est correct:

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

Certes, cela ne sauvera toujours pas la fonction de passer un pointeur nul. Mais au moins, ce code semble plus logique que la version originale.

Voici un autre endroit qui a été copié à partir d'ici avec de légères modifications:

  • V522 Le déréférencement du pointeur nul «post» peut avoir lieu. TSynapse.cxx 74

V595 Le pointeur 'N' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 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; } .... } 

L'analyseur a détecté un code très dangereux! Dans le premier cas, le pointeur N est déréférencé sans vérifier une valeur nulle. Et vous ne pouvez même pas voir l'appel au pointeur non vérifié. Cela se produit à l'intérieur de la fonction shouldVisitDecl .

Traditionnellement, cette règle de diagnostic génère de nombreux avertissements utiles. En voici quelques uns:

  • V595 Le pointeur «fichier» a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 141, 153. TFileCacheRead.cxx 141
  • V595 Le pointeur 'fFree' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 2029, 2038. TFile.cxx 2029
  • V595 Le pointeur 'tbuf' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifier les lignes: 586, 591. TGText.cxx 586
  • V595 Le pointeur 'fPlayer' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 3425, 3430. TProof.cxx 3425
  • V595 Le pointeur 'gProofServ' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 1192, 1194. TProofPlayer.cxx 1192
  • V595 Le pointeur 'projDataTmp' a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 791, 804. RooSimultaneous.cxx 791

L'exemple suivant n'est pas une erreur, mais démontre une fois de plus que les macros encouragent l' écriture de code incorrect ou redondant.

V571 Contrôle récurrent. La condition «if (fCanvasImp)» a déjà été vérifiée à la ligne 799. TCanvas.cxx 800

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

Le pointeur fCanvasImp est vérifié deux fois. L'une des vérifications est déjà implémentée dans la macro SafeDelete . Un des problèmes avec les macros est qu'elles sont souvent difficiles à naviguer à partir du code, donc beaucoup n'étudient pas leur contenu avant utilisation.

Erreurs lors de l'utilisation de tableaux


V519 La variable 'Line [Cursor]' reçoit des valeurs successives deux fois. C'est peut-être une erreur. Vérifiez les lignes: 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; .... } 

La nouvelle valeur de l'élément Line [Cursor] est immédiatement remplacée. Quelque chose ne va pas ici ...

V557 Le dépassement de matrice est possible. L'index «ivar» pointe au-delà de la limite du tableau. BasicMinimizer.cxx 130

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

Faire des erreurs dans la vérification de l'index d'un tableau n'est donc qu'un énorme problème ces derniers temps. Presque tous les trois projets sont trouvés. Si tout est simple lors de l'indexation d'un tableau dans une boucle - l'opérateur '<' est presque toujours utilisé pour comparer l'index à la taille du tableau, alors avec une telle vérification, comme ici, vous devez utiliser l'opérateur '> =', et non '>'. Sinon, il est possible de dépasser la limite du tableau par un élément.

Cette erreur s'est propagée plusieurs fois sur le fichier:

  • V557 Le dépassement de matrice est possible. L'index «ivar» pointe au-delà de la limite du tableau. BasicMinimizer.cxx 186
  • V557 Le dépassement de matrice est possible. L'index «ivar» pointe au-delà de la limite du tableau. BasicMinimizer.cxx 194
  • V557 Le dépassement de matrice est possible. L'index «ivar» pointe au-delà de la limite du tableau. BasicMinimizer.cxx 209
  • V557 Le dépassement de matrice est possible. L'index «ivar» pointe au-delà de la limite du tableau. BasicMinimizer.cxx 215
  • V557 Le dépassement de matrice est possible. L'index «ivar» pointe au-delà de la limite du tableau. BasicMinimizer.cxx 230

V621 Envisagez d'inspecter l'opérateur «pour». Il est possible que la boucle soit mal exécutée ou ne soit pas exécutée du tout. 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; } 

Très probablement, dans la boucle for, ils voulaient comparer la variable dim avec dm-> fArrayDim , et non fArrayDim . La valeur de la variable utilisée est négative, en raison de la condition au début de la fonction. Un tel cycle n'est jamais exécuté.

V767 Accès suspect à l'élément du tableau 'courant' par un index constant à l'intérieur d'une boucle. 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 .... } .... } .... } 

Ce fragment de code analyse une certaine ligne et vérifie son exactitude. Une fois que le caractère nul de la chaîne actuelle est défini comme un nombre, tous les autres caractères sont parcourus pour vous assurer que tous les caractères sont des nombres à la fin de la chaîne. Eh bien, comment ça se passe ... le compteur de boucle i n'est pas utilisé dans la boucle. Il était nécessaire d'écrire le courant [i] et non le courant [0] dans la condition.

Image 4

Fuite de mémoire


V773 La fonction a été fermée sans relâcher le pointeur «liste d'options». Une fuite de mémoire est possible. 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; .... } 

Lorsque vous quittez une fonction, la mémoire n'est pas fournie par le pointeur optionList . Il est difficile pour moi de dire si cela est nécessaire dans ce cas particulier. Mais généralement, ces erreurs sont corrigées dans les projets sur les rapports PVS-Studio. Tout dépend si le programme doit essayer de continuer à fonctionner en cas d'urgence ou non. Il existe de nombreux avertissements de ce type dans le projet, il est préférable que les développeurs revérifient le projet par eux-mêmes et voient le rapport complet de l'analyseur.

Encore une fois sur la fonction memset


V597 Le compilateur pourrait supprimer l'appel de fonction 'memset', qui est utilisé pour vider le tampon 'x'. La fonction memset_s () doit être utilisée pour effacer les données privées. 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)); } 

Beaucoup penseront que lorsque le code sera compilé, ce commentaire n'entrera pas dans le fichier binaire, et ils auront raison: D. Mais la fonction memset sera également supprimée par le compilateur, tout le monde ne le devine pas. Et cela arrivera. Si le tampon vidé ne sera plus utilisé dans le code, le compilateur optimisera le code et supprimera l'appel de fonction. Techniquement, il a raison, mais s'il y avait des données secrètes dans le tampon, elles y resteraient. Il s'agit d'une faille de sécurité classique CWE-14 .

Avertissements divers


V591 La fonction non vide doit renvoyer une valeur. 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; } 

L'instruction surchargée n'a pas de valeur de retour. Également un problème très courant ces derniers temps.

V596 L'objet a été créé mais il n'est pas utilisé. Le mot clé «throw» peut être manquant: 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."); } .... } 

L'erreur est que le mot clé throw est accidentellement oublié. Par conséquent, ce code ne lève pas d'exception en cas d'erreur.

Au total, il y avait deux de ces endroits. La seconde:

  • V596 L'objet a été créé mais il n'est pas utilisé. Le mot clé «throw» peut être manquant: throw runtime_error (FOO); Forest.hxx 137

V609 Divisez par zéro. Plage du dénominateur [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 { .... } .... } 

Un cas similaire à ceux décrits précédemment sur les tableaux. Ici, la variable n est limitée à une plage de valeurs de 0 à 100. Dans ce cas, il existe une branche de code dans laquelle la division par la variable n avec une valeur de 0 se produit. Très probablement, la restriction de la valeur de n doit être réécrite de cette façon:

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

V646 Envisagez d'inspecter la logique de l'application. Il est possible que le mot clé "else" soit manquant. 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'); } } .... } 

L'analyseur a détecté un formatage étrange. À un endroit, le mot-clé else est manquant. D'après le code, nous pouvons supposer que le code vaut vraiment la peine d'être corrigé.

Et quelques endroits à réparer en même temps:

  • V646 Envisagez d'inspecter la logique de l'application. Il est possible que le mot clé "else" soit manquant. TFormula_v5.cxx 3702
  • V646 Envisagez d'inspecter la logique de l'application. Il est possible que le mot clé "else" soit manquant. RooAbsCategory.cxx 604

La boucle infinie V663 est possible. La condition «cin.eof ()» est insuffisante pour rompre la boucle. Pensez à ajouter l'appel de fonction 'cin.fail ()' à l'expression conditionnelle. 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; } .... } .... } 

Lorsque vous travaillez avec la classe std :: istream , appeler la fonction eof () ne suffit pas pour terminer la boucle. En cas d'échec lors de la lecture des données, l'appel de la fonction eof () retournera toujours false , et il n'y a aucun autre point de sortie de la boucle dans ce code. Pour terminer la boucle dans ce cas, une vérification supplémentaire de la valeur retournée par la fonction fail () est requise:

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

Ou écrivez simplement:

 while (is) { .... } 

V678 Un objet est utilisé comme argument de sa propre méthode. Pensez à vérifier le premier argument réel de la fonction «Copier». 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; } 

Enfin, voici une telle erreur. Au lieu de fSizes, vous avez dû passer orig.fSizes à la fonction Copy .

Conclusion


Il y a environ un an, un aperçu du code de projet NCBI Genome Workbench a été fourni . Ce projet est également utilisé dans la recherche scientifique, mais dans le génome. Pour ce que je veux dire, les logiciels dans ce domaine sont très importants, mais leur qualité n'est pas dûment prise en compte.

Soit dit en passant, l'autre jour, macOS 10.15 Catalina est sorti, dans lequel ils ont refusé de prendre en charge les applications 32 bits. Et dans PVS-Studio, il existe un large ensemble de règles pour identifier les problèmes lors du portage de programmes vers des systèmes 64 bits. Plus d'informations à ce sujet dans le blog de l'analyseur.



Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Svyatoslav Razmyslov. Analyse du code de ROOT, cadre d'analyse des données scientifiques .

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


All Articles