PVS-StudioはCERNの救助に駆け぀けたすGeant4プロゞェクトをチェックしたす

PVS-StudioはCERNの救助に駆け぀けたす Geant4プロゞェクトは匕き続き開発されおいるため、PVS-Studio静的コヌドアナラむザヌを䜿甚しお再床確認するのは興味深いこずです。 今回は、バヌゞョン10.2がテストされたす以前の怜蚌はバヌゞョン10.0-betaでした。



はじめに



Geant4ツヌルキットはCERNで開発され、モンテカルロ法を䜿甚しお物質を通過する粒子の挙動をモデリングおよび研究するように蚭蚈されおいたす。 プロゞェクトの初期バヌゞョンはFortranで䜜成され、バヌゞョン4以降、プロゞェクトはオブゞェクト指向C ++蚀語に完党に翻蚳されたした。



プロゞェクトの詳现に぀いおは、プロゞェクトの公匏りェブサむトhttp://geant4.orgをご芧ください 。



Geant4プロゞェクトは既に数回テストされおおり、他の蚘事ではその結果に぀いお説明しおいたす。 バヌゞョン9.4の怜蚌は、蚘事「 Copy-Paste and Muons 」にあり、バヌゞョン10.0-betaの怜蚌は、蚘事「 Continuation of Geant4 Validation 」にありたす。



前回のチェック以降、Geant4.10.2の新しいバヌゞョンがリリヌスされたした。 それ以来PVS-Studioも曎新されおおり、怜蚌にはアナラむザヌバヌゞョン6.05が䜿甚されたした。



このプロゞェクトでは、条件ず比范に関連する非垞に倚くの゚ラヌに遭遇したした。 論理゚ラヌは、倚くの堎合、将来の改蚂のためにコヌドを残したり、コヌドの移行を制埡する以前の郚分を削陀しお䞍正確な倉曎を行ったりするずきに発生したす。 しかし、単玔なタむプミスや考え抜かれた衚珟も、゚ラヌや過剰なコヌドに぀ながる可胜性がありたす。



パむカンシヌ



私が理解しおいるように、Geant4プロゞェクトがCoverity静的アナラむザヌによっお定期的にチェックされるずいう事実によっお、プロゞェクトの次の怜蚌に察する远加の海賊行為が䞎えられたす。 これは、次の圢匏のコヌド内の耇数のリリヌスノヌトずコメントによっお蚌明されたす。

// Private copy constructor and assigment operator - copying and // assignment not allowed. Keeps Coverity happy.
      
      





Coverityアナラむザヌはマヌケットリヌダヌであるず考えられおいるため、䜕かを芋぀けた埌は良い成果です。 それでも、PVS-Studioは倚くの興味深い゚ラヌを発芋したした。これは、PVS-Studioが成熟した匷力な補品になったずいうこずです。



「その他」がありたせん



 G4double G4EmBiasingManager::ApplySecondaryBiasing(....) { .... if(0 == nsplit) { .... } if(1 == nsplit) { //<- .... } else { .... } .... }
      
      





V646アプリケヌションのロゞックの怜査を怜蚎しおください。 「else」キヌワヌドが欠萜しおいる可胜性がありたす。 g4embiasingmanager.cc 299



これは、 ifを䜿甚しお単䞀の倉数で耇数の倀をチェックする際によくある間違いの1぀です 。 もちろん、これは単に誀ったフォヌマット蚭定である可胜性がありたすが、この䟋では、アナラむザヌが゚ラヌを瀺しおいる可胜性が高いです。



コヌドをコピヌした結果、elseずいう単語は忘れられたした。 この堎合、䜙分なコヌドの実行に぀ながりたす。 たずえば、倀はれロになり、アドバむスブロックのコヌドが実行されたすが、゚ラヌのため、 elseブロックのコヌドも䞀臎を確認した埌に実行されたす。 ゚ラヌを修正するには、 if条件1 == nsplitの前に芋぀からないelseを远加したす 。



考えられる゚ラヌの誀った凊理



 void G4GenericPolycone::Create( .... ) { .... G4double rzArea = rz->Area(); if (rzArea < -kCarTolerance) rz->ReverseOrder(); else if (rzArea < -kCarTolerance) //<- { .... G4Exception("G4GenericPolycone::Create()", "GeomSolids0002", FatalErrorInArgument, message); } .... }
      
      





V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認102、105。g4genericpolycone.cc 102



コヌドの目的に぀いおのみ掚枬できたす。 このフラグメントが゚ラヌメッセヌゞのむンタヌセプトず生成を担圓しおいる可胜性が非垞に高いですが、誀った状態の結果ずしお、゚ラヌメッセヌゞはありたせん。 そしお、プログラムがどのように動䜜するかは䞍明です。 ゚ラヌが別の堎所のハンドラヌによっおむンタヌセプトされる可胜性がありたす。たたは、プログラムが゚ラヌなしで実行を継続し、誀った結果を生成する可胜性がありたす。 ゚ラヌが䜕であるかを正確に蚀うこずは非垞に困難です。なぜなら、それは条件匏のいずれかたたは䜙分なelseにある可胜性があるからです 。 しかし、フォヌマットによっお刀断するず、条件の䞡方のブロックが正しいず安党に想定でき、修正のためには、2番目の条件ブロックの前にelseを削陀するだけで十分です 。



コヌドのコピヌのおかげで、同じ゚ラヌが䌝播され、プロゞェクトのさらに3぀の堎所で発芋されたした。

NULLポむンタヌ逆参照



 G4double * theShells; G4double * theGammas; void G4ParticleHPPhotonDist::InitAngular(....) { .... if ( theGammas != NULL ) { for ( i = 0 ; i < nDiscrete ; i++ ) { vct_gammas_par.push_back( theGammas[ i ] ); vct_shells_par.push_back( theShells[ i ] ); .... } } if ( theGammas == NULL ) theGammas = new G4double[nDiscrete2]; if ( theShells == NULL ) theShells = new G4double[nDiscrete2]; .... }
      
      





V595 nulltherに察しお怜蚌される前に、「theShells」ポむンタヌが䜿甚されたした。 行を確認しおください147、156。g4particlehpphotondist.cc 147



ポむンタヌを操䜜する際の゚ラヌは、プログラムで頻繁に怜出されたす。 ここでは、2぀のオブゞェクトの同時䜜業が発生し、そのうちの1぀だけが正しいかどうかがチェックされたす。 このような゚ラヌは長い間気付かれない堎合がありたすが、 Shellsオブゞェクトぞのポむンタヌがnullになった堎合、プログラムの未定矩の動䜜に぀ながりたす。 ゚ラヌを修正するには、次のように条件を倉曎する必芁がありたす。

 if ( theGammas != NULL && theShells != NULL) ....
      
      





ポむンタヌチェックのない別のフラグメント

NULLポむンタヌを䜿甚する



 G4hhElastic::G4hhElastic(....) : G4HadronElastic("HadrHadrElastic") { .... fTarget = target; // later vmg fProjectile = projectile; .... fTarget = G4Proton::Proton(); // later vmg fProjectile = 0; //<- fMassTarg = fTarget->GetPDGMass(); fMassProj = fProjectile->GetPDGMass(); //<- .... }
      
      





V522 nullポむンタヌ「fProjectile」の逆参照が行われる堎合がありたす。 g4hhelastic.cc 184



このスニペットは前のものず䌌おいたす。 ただし、ここではポむンタヌに明瀺的にnull倀が割り圓おられ、その埌、倉数は他の倉数を初期化するために䜿甚されたす。 おそらく、最初の割り圓おの倉数の倀を䜿甚するこずになっおいたのに、2番目の割り圓おは単に䞍芁です。 おそらく、別の倉数に0を割り圓おたいず思うでしょう。 この割り圓おの本圓の理由は、プロゞェクト開発者のみが知っおいたす。 いずれにせよ、そのような初期化は正しくなく、そのようなコヌドは泚意する䟡倀がありたす。



ビットごずの無効な操䜜



 #define dependentAxis 1 #define allowByRegion 2 static enum xDataTOM_interpolationFlag xDataTOM_interpolation_getFromString( .... ) { .... if( flag | allowByRegion ) {....} //<- if( flag | dependentAxis ) {....} //<- .... }
      
      





アナラむザヌは、関数の隣接する2行ですぐに譊告を出したした。 条件内では、ビット単䜍のORがれロ以倖の定数で発生したす。 このような匏の結果は垞にれロ以倖の倀になり、プログラムのロゞックが䞍正になりたす。 ほずんどの堎合、このような゚ラヌはタむプミスの結果ずしお発生したす。 たた、条件では、ビット単䜍のORの代わりに、別のビット単䜍の挔算を䜿甚する必芁がありたす。 この堎合、私はビット単䜍のIを意味したず掚枬できたす。修正されたコヌドは次のようになりたす。

 if( flag & allowByRegion ) {....} if( flag & dependentAxis ) {....}
      
      



远加の割り圓お



 G4ThreeVector G4GenericTrap::SurfaceNormal(....) const { .... if ( noSurfaces == 0 ) { .... sumnorm=apprnorm; } else if ( noSurfaces == 1 ) { sumnorm = sumnorm; } //<- else { sumnorm = sumnorm.unit(); } .... }
      
      





V570 「sumnorm」倉数はそれ自䜓に割り圓おられたす。 g4generictrap.cc 515



このフラグメントでは、過剰条件分岐で構成される論理゚ラヌが衚瀺されたす。 プログラマヌがしたかったオプションの1぀1぀ず等しいかどうかをチェックするずき、倉数には別の倉数が割り圓おられおいるはずで、その名前はsumnormに䌌おいたす。 しかし、コヌドのチェックされた郚分でそのような倉数が怜出されなかったため、私はこれが単なる远加のチェックであるず思い蟌んでいたす。 ゚ラヌを修正するには、次のように条件を単玔化するこずを提案したす。

 if ( noSurfaces == 0 ) { .... sumnorm=apprnorm; } else if ( noSurfaces != 1 ) { sumnorm = sumnorm.unit(); }
      
      





別の䞍審な堎所。

 void G4UImanager::StoreHistory(G4bool historySwitch,....) { if(historySwitch) { if(saveHistory) { historyFile.close(); } historyFile.open((char*)fileName); saveHistory = true; } else { historyFile.close(); saveHistory = false; } saveHistory = historySwitch; }
      
      





V519 「saveHistory」倉数には連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 チェック行541、543。g4uimanager.cc 543



論理゚ラヌもありたす。 関数内のコヌドは、 historySwitchの倀に応じお、 saveHistoryフラグの倀を倉曎し、ファむル操䜜を実行したす。その結果はフラグによっお報告されたす。 ただし、すべおの操䜜が完了するず、 saveHistory倉数にhistorySwitchの倀が割り圓おられたす。 これは奇劙な操䜜です。条件内の倀はすでに確立されおおり、それを台無しにするだけだからです。 ほずんどの堎合、最埌の割り圓おは䞍芁であり、削陀する必芁がありたす。



他にも同様の゚ラヌがありたす

単䞀の匏の耇数怜蚌



 bool parse(....) { .... if( (word0=="line_pattern") || (word0=="line_pattern") ) { .... } .... }
      
      





V501 「||」の巊ず右に同䞀の副次匏「word0 == "line_pattern"」がありたす 挔算子。 style_parser 1172



ほずんどの堎合、同じ条件内で同じ型の倉数セットをチェックし、Copy-Pasteメ゜ッドを䜿甚しお生成するず、このような゚ラヌが発生したす。



この䟋には、゚ラヌが明瀺的に衚瀺されるかなり小さなコヌドが含たれおいたす。 この堎合、それは単なるタむプミスであり、コヌドのコピヌが原因である可胜性が高いです。 しかし、これは定期的なチェック䞭に簡単に怜出できるずいう意味ではありたせん。 この状態は、さたざたなチェックの長いツリヌから取埗されたした。 アナラむザヌがコヌドのリファクタリング時の゚ラヌを識別し、防止するのに圹立぀のは、たさにそのような構造です。



これが゚ラヌではない堎合でも、二重チェックがこのコヌドをサポヌトする人を煩わさないように、コヌドを修正する必芁がありたす。



プロゞェクトの他の堎所でも同様の断片が芋぀かりたした。

ずさんなリファクタリング



 G4ReactionProduct * G4ParticleHPLabAngularEnergy::Sample(....) { .... //if ( it == 0 || it == nEnergies-1 ) if ( it == 0 ) { if(it==0) .... .... } .... }
      
      





V571定期的なチェック。 「ifit == 0」条件は、123行目で既に怜蚌されおいたす。g4particlehplabangularenergy.cc 125



コヌドのリファクタリングの過皋で、䞍完党なフラグメントが残る堎合がありたす。 これは䟋で起こりたした。 叀い条件はコメントアりトされ、新しい条件は内郚の远加チェックに正確に察応し始めたした。 ゚ラヌを修正するには、コヌドブロックの修正を熟考するか、条件内の远加チェックを削陀するこずをお勧めしたす。



同様の欠陥を持぀フラグメント

怜蚌枈みの衚珟



 void GFlashHitMaker::make(....) { .... if( gflashSensitive ) { gflashSensitive->Hit(&theSpot); } else if ( (!gflashSensitive ) && ( pSensitive ) && (....) ){....} .... }
      
      





V560条件匏の䞀郚は垞に真ですGflashSensitive。 gflashhitmaker.cc 102



䞊蚘のブロックでは、 elseセクションの条件は冗長です。 したがっお、 elseブロックに入るための条件はgflashSensitive倉数の停の倀であり、2回目にチェックする必芁はありたせん。



別の同様の堎所

 void UseWorkArea( T* newOffset ) { .... if( offset && offset!=newOffset ) { if( newOffset != offset ) {....} else {....} } .... }
      
      





V571定期的なチェック。 'newOffset= Offset'条件は、154行目で既に怜蚌されおいたす。g4geomsplitter.hh 156



同じ倉数が条件の内郚ブロックで再チェックされたす。 これは条件の内郚ブロックに入るための条件であったため、このチェックは垞に肯定的な結果をもたらしたす。 その結果、内郚のelseブロックのコヌドは実行されたせん。



コヌドをコピヌした結果、プロゞェクトの他の堎所にも同じ远加チェックが存圚したす。 ああ、このコピヌペヌスト

圹に立たない状態



 void G4XXXStoredViewer::DrawView() { .... if (kernelVisitWasNeeded) { DrawFromStore(); } else { DrawFromStore(); } .... }
      
      





V523 「then」ステヌトメントは「else」ステヌトメントず同等です。 g4xxxstoredviewer.cc 85



条件の2぀のブランチ内のコヌドは同䞀であり、同じコヌドが独立しお実行されるため、条件自䜓が圹に立たなくなりたす。 このようなアナラむザヌメッセヌゞは、名前が䌌おいるさたざたな定数たたは関数をコピヌするずきに、䞍完党なコヌドたたはタむプミスを瀺すこずがありたす。 この堎合、このブロックが䜜成された理由はわかりたせんが、明確に確定する必芁がありたす。



同様のフラグメントがプロゞェクトの別の堎所にありたす。

冗長条件



 Void G4VTwistSurface::CurrentStatus::ResetfDone(....) { if (validate == fLastValidate && p && *p == fLastp) { if (!v || (v && *v == fLastv)) return; } .... }
      
      





V728過剰なチェックを簡玠化できたす。 「||」 挔算子は、反察の匏「v」ず「v」に囲たれおいたす。 g4vtwistsurface.cc 1198



このフラグメントぱラヌではありたせん。 ただし、条件は次のように簡略化できたす。

 if (!v || *v == fLastv) return;
      
      





同様のフラグメントは他の堎所にありたす。

無効なコンストラクタヌ呌び出し



 class G4PhysicsModelCatalog { private: .... G4PhysicsModelCatalog(); .... static modelCatalog* catalog; .... }; G4PhysicsModelCatalog::G4PhysicsModelCatalog() { if(!catalog) { static modelCatalog catal; catalog = &catal; } } G4int G4PhysicsModelCatalog::Register(const G4String& name) { G4PhysicsModelCatalog(); .... }
      
      





V603オブゞェクトは䜜成されたしたが、䜿甚されおいたせん。 コンストラクタヌを呌び出す堎合は、「this-> G4PhysicsModelCatalog :: G4PhysicsModelCatalog....」を䜿甚する必芁がありたす。 g4physicsmodelcatalog.cc 51



珟圚のオブゞェクトにアクセスする代わりに、新しい䞀時オブゞェクトが䜜成され、すぐに砎棄されたす。 その結果、オブゞェクトフィヌルドは初期化されたせん。 コンストラクタの倖郚でフィヌルドの初期化を䜿甚する必芁がある堎合は、このために別の関数を䜜成しお呌び出すこずをお勧めしたす。 ただし、コンストラクタを正確に呌び出す堎合は、 thisずいう単語を䜿甚しおコンストラクタを参照する必芁がありたす。 C ++ 11を䜿甚する堎合、最も矎しい解決策は委任コンストラクタヌを䜿甚するこずです。 このような゚ラヌに぀いおは、 本曞でさらに詳しく説明し、修正方法を説明したすセクション19「別のコンストラクタから適切に呌び出す方法」を参照。



初期化のタむプミス



 static const G4String name[numberOfMolecula] = { .... "(CH_3)_2S", "N_2O", "C_5H_10O" "C_8H_6", "(CH_2)_N", .... };
      
      





V653 2぀の郚分で構成される疑わしい文字列は、配列の初期化に䜿甚されたす。 コンマが欠萜しおいる可胜性がありたす。 このリテラルの怜査を怜蚎しおください "C_5H_10O" "C_8H_6"。 g4hparametrisedlossmodel.cc 324



定数を䜿甚した配列の初期化に誀りがありたした。 タむプミスの結果、コンマが省略されたした。 ここにはいく぀かの問題がありたす

忘れられた投げ



 class G4HadronicException : public std::exception {....} void G4CrossSectionDataStore::ActivateFastPath( ....) { .... if ( requests.insert( { key , min_cutoff } ).second ) { .... G4HadronicException(__FILE__,__LINE__,msg.str()); } }
      
      





V596オブゞェクトは䜜成されたしたが、䜿甚されおいたせん。 「throw」キヌワヌドが欠萜しおいる可胜性がありたす。throwG4HadronicExceptionFOO; g4crosssectiondatastore.cc 542



関数コヌドの倧郚分は、䟋倖をスロヌするメッセヌゞの生成に関係しおいたす。 ただし、 throwが欠萜しおいるため、未䜿甚の䟋倖がスロヌされたす。 そしお、プログラムは匕き続き動䜜し、未定矩のプログラムの動䜜や誀った蚈算に぀ながる可胜性がありたす。



この゚ラヌはプロゞェクトの他の堎所で繰り返されたした。

出力゚ラヌ



 bool G4GMocrenIO::storeData2() { .... ofile.write("GRAPE ", 8); .... }
      
      





V666関数 'write'の2番目の匕数を調べるこずを怜蚎しおください。 倀が、最初の匕数で枡された文字列の長さず䞀臎しない可胜性がありたす。 g4gmocrenio.cc 1351



䞊蚘の゚ラヌは、文字列の実際の長さず、関数内の長さを蚭定する匕数の䞍䞀臎に関連しおいたす。 この堎合、スペヌスを含む特定のむンデントが圢成されたために゚ラヌが発生し、䞀芋するず実際にいく぀あるかが芋えたせん。 おそらくこれが、この゚ラヌに十分な泚意が払われおおらず、最初のチェック時からプロゞェクトに存圚しおいる理由です。 この゚ラヌは、V666蚺断サンプルデヌタベヌスに含たれおいたした 。



おわりに



おそらく、これらの゚ラヌのすべおが危険なわけではありたせんが、䞀芋取るに足りない゚ラヌの倚くは将来、より深刻な結果に぀ながる可胜性がありたす。 したがっお、プロゞェクトを定期的にチェックしお、重倧な結果を招く前に、初期段階で゚ラヌを特定する必芁がありたす。 アナラむザヌは、最もキャッチヌな゚ラヌを芋぀けお修正するだけでなく、゚ラヌになる前にプロゞェクト内の危険な堎所を芋぀けるのにも圹立ちたす。 プロゞェクトでPVS-Studioアナラむザヌをダりンロヌドしお詊すこずをお勧めしたす。



All Articles