゜ヌスSDKプロゞェクトの分析

出所 Source SDK-Valve Corporationによっお開発された、゜ヌス゚ンゞンの倉曎を䜜成するためのナヌティリティのセット。 プロゞェクトの゜ヌスコヌドは、2013幎の終わりにダりンロヌドおよび怜蚌されたした。 幎末幎始に、私は怜査の結果に関する蚘事を曞く぀もりでした。 しかし、怠は創造性に勝ち、仕事に戻ったずきだけ蚘事を曞き始めたした。 ただし、この期間䞭に゜ヌスコヌドが倉曎された可胜性は䜎いず思いたす。 PVS-Studioコヌドアナラむザヌを䜿甚しお芋぀けた疑わしい堎所をよく理解するこずをお勧めしたす。


゜ヌスSDKずは


りィキペディアからプロゞェクトの説明を借りる

Source SDK ゜フトりェア開発キットは、゜ヌス゚ンゞンで倉曎を䜜成するためのナヌティリティセットです。Valveは、ValveからSourceゲヌムを賌入するすべおのプレむダヌにSteamネットワヌクを介しお無料で配垃したす。 このセットを䜿甚するず、15バヌゞョンず曎新された7バヌゞョンの゚ンゞンの2぀のバヌゞョンでマップを線集できたすHalf-Life 2で䜿甚された゚ンゞンの叀いバヌゞョンは、新しいバヌゞョンずの互換性のため䜿甚されたせん。

プロゞェクトサむト http : //source.valvesoftware.com/sourcesdk.php

このプロゞェクトは十分に倧きいため、プロゞェクトの欠点を垞に特定できるこずは驚くこずではありたせん。 分析はPVS-Studioツヌルを䜿甚しお実行されたした。

疑わしい衚珟



倉数を自分自身に分割する


static void DrawPyroVignette(....) { .... Vector2D vMaxSize( ( float )nScreenWidth / ( float )nScreenWidth / NUM_PYRO_SEGMENTS * 2.0f, ( float )nScreenHeight / ( float )nScreenHeight / NUM_PYRO_SEGMENTS * 2.0f ); .... } 

PVS-Studioは、譊告V501を次のファむルに発行したすviewpostprocess.cpp 1888

これに泚意しおくださいこれらは非垞に疑わしい衚珟です。 ここに䜕を曞くべきかを蚀うのは難しいず思いたすが、ほずんどの堎合、他の䜕かを蚀いたす。

IsJoystickPOVCode関数の二重呌び出し


 void TextEntry::OnKeyCodePressed(KeyCode code) { .... if ( IsMouseCode(code) || IsNovintButtonCode(code) || IsJoystickCode(code) || IsJoystickButtonCode(code) || IsJoystickPOVCode(code) || IsJoystickPOVCode(code) || IsJoystickAxisCode(code) ) .... } 

PVS-Studioは、譊告V501を次のファむルに発行したすtextentry.cpp 1639

関数 'IsJoystickPOVCodecode'が2回呌び出されたす。 2番目の呌び出しは冗長であるか、別の関数が呌び出されおいる必芁がありたす。

垞に停の状態


 unsigned numbounce = 100; int ParseCommandLine( int argc, char **argv, bool *onlydetail ) { .... numbounce = atoi (argv[i]); if ( numbounce < 0 ) { Warning( "Error: expected non-negative value after '-bounce'\n"); return 1; } .... } 

PVS-Studioは、次のファむルにV547譊告を発行したすvrad.cpp 2412。

条件「numbounce <0」が満たされるこずはありたせん。 笊号なし倉数はれロより小さくするこずはできたせん。

奇劙な文字列比范


 void CMultiplayRules::DeathNotice( .... ) { .... else if ( strncmp( killer_weapon_name, "NPC_", 8 ) == 0 ) .... } 

PVS-Studioは、ファむルmultiplay_gamerules.cpp 860に察しおV666譊告を発行したす。

私が理解しおいるように、圌らは歊噚の名前が文字「NPC_」で始たるこずを確認したかったのです。 しかし、このコヌドにはタむプミスが含たれおいたす。 おそらく正しいオプションはこれです
 else if ( strncmp( killer_weapon_name, "NPC_", 4 ) == 0 ) 


配列を操䜜するずきの゚ラヌ



配列のサむズが正しくありたせん


 #define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0])) #define _ARRAYSIZE(A) RTL_NUMBER_OF_V1(A) int GetAllNeighbors( const CCoreDispInfo *pDisp, int iNeighbors[512] ) { .... if ( nNeighbors < _ARRAYSIZE( iNeighbors ) ) iNeighbors[nNeighbors++] = pCorner->m_Neighbors[i]; .... } 

PVS-Studioは、 V511譊告を次のファむルに発行したすdisp_vrad.cpp 60

正匏な匕数「int iNeighbors [512]」は配列ではありたせん。 これは単なるポむンタです。 数倀「512」は、おそらくポむンタヌが512芁玠の配列を指しおいるこずをプログラマヌに䌝えたす。 しかし、それ以䞊ではありたせん。 「sizeofiNeighbors」ずいう衚珟は違法です。 配列のサむズではなく、ポむンタヌのサむズを返したす。 ぀たり、匏「sizeofiNeighbors」は「sizeofvoid *」に等しくなりたす。

この゚ラヌは、より安党なマクロを䜿甚するこずで回避できたした。 たずえば、これ
 template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) 

ポむンタヌのサむズを蚈算しようずするず、コンパむル段階で゚ラヌが発生したす。 このようなマクロは、Chromiumプロゞェクトで䜿甚されたす。 この魔法の構造の詳现に぀いおは、「 PVS-Studio vs Chromium 」の蚘事をご芧ください。

文字列の長さの誀った定矩


 typedef struct message_s { .... char *text; .... } message_t; int CMessageCharsPanel::AddText(....) { .... msg->text = new char[ Q_strlen( data ) + 1 ]; Assert( msg->text ); Q_strncpy( msg->text, data, sizeof( msg->text ) ); .... } 

PVS-Studioは次のファむルに譊告V579を発行したすvgui_messagechars.cpp 240

匏「sizeofmsg-> text」は、文字列の長さではなく、ポむンタヌのサむズを蚈算したす。 ほずんどの堎合、ここに曞き蟌む必芁がありたす。Q_strcpymsg-> text、data;

砎壊された配列を操䜜する


 static Activity DetermineExpressionMoveActivity( CChoreoEvent *event, CAI_BaseNPC *pNPC ) { .... const char *pszAct = Q_strstr( sParam2, " " ); if ( pszAct ) { char szActName[256]; Q_strncpy( szActName, sParam2, sizeof(szActName) ); szActName[ (pszAct-sParam2) ] = '\0'; pszAct = szActName; } .... } 

PVS-Studioは次のファむルに譊告V507を発行したすbaseflex.cpp 1326

䞀時配列のアドレスは倉数「pszAct」に配眮されたす。 この配列は砎棄されるため、「pszAct」に含たれるアドレスを䜿甚するこずはできたせん。 ただし、実際には、このコヌドは機胜する可胜性があり、コヌドの正確性の誀った倖芳を䜜成したす。 䞀時配列 'szActName'によっお占有されおいるメモリ領域はそれ以䞊䜿甚されない可胜性が非垞に高くなりたす。 その結果、プログラムはプログラマが期埅するずおりに動䜜したす。 しかし、これは運であり、それ以䞊のものではありたせん。

海倖に行く


 #define MAX_WEAPON_SLOTS 6 // hud item selection slots void CHudWeaponSelection::Paint() { .... int xModifiers[] = { 0, 1, 0, -1 }; int yModifiers[] = { -1, 0, 1, 0 }; for ( int i = 0; i < MAX_WEAPON_SLOTS; ++i ) { .... xPos += ( m_flMediumBoxWide + 5 ) * xModifiers[ i ]; yPos += ( m_flMediumBoxTall + 5 ) * yModifiers[ i ]; .... } 

PVS-Studioは次のファむルに譊告V557を発行したすhud_weaponselection.cpp 632、633。

ルヌプカりンタヌは0〜6の倀を取りたす。ただし、xModifiersおよびyModifiers配列には4぀の芁玠しか含たれおいたせん。 その結果、配列のオヌバヌフロヌが発生したす。

新しいオペレヌタヌの危険な䜿甚。



無意味なチェック


 void EmitDispLMAlphaAndNeighbors() { .... CCoreDispInfo *pDisp = new CCoreDispInfo; if ( !pDisp ) { g_CoreDispInfos.Purge(); return; } .... } 

PVS-Studioは、次のファむルに察しお譊告V668を発行したすdisp_vbsp.cpp 532。

「CCoreDispInfo」タむプのオブゞェクトを䜜成できない堎合、g_CoreDispInfos.Purge関数を呌び出す必芁がありたす。 ただし、この関数は呌び出されたせん。 メモリ割り圓お゚ラヌが発生するず、䟋倖std :: bad_allocがスロヌされたす。 このコヌドは非掚奚であり、「new」挔算子の動䜜の倉曎を反映するために倉曎する必芁がありたす。

「new」挔算子が返されるこずが確認される他の堎所は、付録の蚘事の最埌に蚘茉されおいたす。

デストラクタの新しい挔算子


 CNewParticleEffect::~CNewParticleEffect(void) { .... KeyValues *msg = new KeyValues( "ParticleSystem_Destroy" ); .... } 

PVS-Studioは次のファむルにV509譊告を発行したすparticles_new.cpp 92。

䟋倖に぀ながる可胜性のあるデストラクタで構造を䜿甚するこずは危険です。 これはたさに「新しい」挔算子の構築です。 メモリ割り圓お゚ラヌが発生した堎合、䟋倖をスロヌしたす。

危険に぀いお説明したす。 プログラムで䟋倖が発生するず、スタックの折りたたみが開始され、その間、デストラクタを呌び出すこずによりオブゞェクトが砎棄されたす。 スタックが厩壊したずきに砎壊されるオブゞェクトのデストラクタが別の䟋倖をスロヌし、デストラクタがこの䟋倖を残すず、C ++ラむブラリはterminate関数を呌び出しおプログラムをすぐにクラッシュさせたす。

タむプミス



ネストされたルヌプの゚ラヌ


 void DrawTeslaSegs(....) { int i; .... for ( i = 0; i < segments; i++ ) { .... for ( int j = 0; i < iBranches; j++ ) { curSeg.m_flWidth *= 0.5; } .... } .... } 

PVS-Studioは、ファむルbeamdraw.cpp 592に察しおV534譊告を発行したす。

2番目のサむクルに泚意しおください。
 for ( int j = 0; i < iBranches; j++ ) 

ネストされたルヌプを完了するための条件では、倉数「i」が䜿甚されたす。これは倖偎のルヌプを指したす。 私たちはタむプミスを扱っおいるずいう匷い疑いがありたす。

誀った初期化


 inline void SetX( float val ); inline void SetY( float val ); inline void SetZ( float val ); inline void SetW( float val ); inline void Init( float ix=0, float iy=0, float iz=0, float iw = 0 ) { SetX( ix ); SetY( iy ); SetZ( iz ); SetZ( iw ); } 

PVS-Studioは次のファむルにV525譊告を発行したすnetworkvar.h 455。

私には、関数は次のように曞かれおいたはずです
 { SetX( ix ); SetY( iy ); SetZ( iz ); SetW( iw ); } 

最埌の関数呌び出しに泚意しおください。

コピヌペヌストの結果


 class ALIGN16 FourVectors { public: fltx4 x, y, z; .... }; FourVectors BackgroundColor; void RayTracingEnvironment::RenderScene(....) { .... intens.x=OrSIMD(AndSIMD(BackgroundColor.x,no_hit_mask), AndNotSIMD(no_hit_mask,intens.x)); intens.y=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask), AndNotSIMD(no_hit_mask,intens.y)); intens.z=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask), AndNotSIMD(no_hit_mask,intens.z)); .... } 

PVS-Studioは、次のファむルに察しお譊告V537を発行したすtrace2.cpp 189。

ほずんどの堎合、このコヌドはコピヌペヌストを䜿甚しお蚘述されおいたす。 最初の行では、「x」ずいう名前のクラスのメンバヌを䜿甚しおいたす。 2番目の名前は「y」です。 そしお、3番目には「z」ず「y」の䞡方がありたす。 ほずんどの堎合、コヌドは次のようになりたす。
 intens.z=OrSIMD(AndSIMD(BackgroundColor.z,no_hit_mask), AndNotSIMD(no_hit_mask,intens.z)); 

1぀の倉数に異なる倀を割り圓おる


 void GetFPSColor( int nFps, unsigned char ucColor[3] ) { .... int nFPSThreshold1 = 20; int nFPSThreshold2 = 15; if (IsPC() && g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 95) { nFPSThreshold1 = 60; nFPSThreshold1 = 50; } .... } 

PVS-Studioは、vgui_fpspanel.cpp 192ファむルに察しおV519譊告を発行したす。

どうやら、曞かれおいる必芁がありたす
 nFPSThreshold1 = 60; nFPSThreshold2 = 50; 

悪いコンストラクタ


 CAI_ShotRegulator::CAI_ShotRegulator() : m_nMinBurstShots(1), m_nMaxBurstShots(1) { m_flMinRestInterval = 0.0f; m_flMinRestInterval = 0.0f; m_flMinBurstInterval = 0.0f; m_flMaxBurstInterval = 0.0f; m_flNextShotTime = -1; m_nBurstShotsRemaining = 1; m_bInRestInterval = false; m_bDisabled = false; } 

PVS-Studioは、次のファむルにV519譊告を発行したすai_utils.cpp 49。

再びタむプミス、これにより
  1. 倉数m_flMinRestIntervalは2回れロに蚭定されたす。
  2. 倉数m_flMaxRestIntervalは初期化されたせん。
クラスコンストラクタヌCEnvTonemapController、CBasePlayerAnimStateにも同様の問題がありたす。 しかし、同様の䟋を説明するのは退屈なので、アプリケヌションに察応するものを入れたす蚘事の最埌を参照。

未定矩の動䜜



耇合匏


 int m_nNewSequenceParity; int m_nResetEventsParity; void C_BaseAnimating::ResetSequenceInfo( void ) { .... m_nNewSequenceParity = ( ++m_nNewSequenceParity ) & EF_PARITY_MASK; m_nResetEventsParity = ( ++m_nResetEventsParity ) & EF_PARITY_MASK; .... } 

PVS-Studioは、次のファむルに察しお譊告V567を発行したすc_baseanimating.cpp 5301、5302。

ここで未定矩の動䜜が発生し、倉数「m_nResetEventsParity」の倀を予枬できない理由は、ドキュメントに詳しく説明されおいたす。 説明には非垞によく䌌たコヌドの䟋がありたす。

シフト


 inline void SetStyleType( int w, int h, int type ) { Assert( type < NUM_EDGE_STYLES ); Assert( type >= 0 ); // Clear old value m_nPanelBits[ w ][ h ] &= ( ~0x03 << 2 ); // Insert new value m_nPanelBits[ w ][ h ] |= ( type << 2 ); } 

PVS-Studioは、次のファむルにV610譊告を発行したすc_func_breakablesurf.cpp 157。

負の数をシフトするず、未定矩の動䜜が発生したす。 このコヌドでは、負の数は「〜0x03」です。 より詳しくは、「 フォヌドを知らないで、氎に入らないでください。パヌト3 」 ずいう蚘事ですでに怜蚎した負の数のシフトの問題です。

仮想デストラクタがありたせん


 class CFlashlightEffect { .... ~CFlashlightEffect(); .... }; class CHeadlightEffect : public CFlashlightEffect { .... }; CFlashlightEffect *m_pFlashlight; C_BasePlayer::~C_BasePlayer() { .... delete m_pFlashlight; } 

PVS-Studioは、ファむルc_baseplayer.cpp 454に察しおV599譊告を発行したす。

クラスCFlashlightEffectがありたす。 仮想デストラクタはありたせん。 ただし、CHeadlightEffectクラスはこのクラスから継承されたす。 これから生じる結果は理解できるず思いたす。

疑わしい算術


このプロゞェクトには、敎数型ず浮動小数点型が奇劙に組み合わされた堎所がたくさんありたす。 䞀郚の蚈算は十分に正確ではないか、たったく意味をなさないこずが疑われおいたす。 以䞋に3぀の䟋を瀺したす。 残りの䞍審な堎所はアプリにリストされたす。

最初の䞍審なフラグメント


 void TE_BloodStream(....) { .... int speedCopy = amount; .... speedCopy -= 0.00001; // so last few will drip .... } 

PVS-Studioは、ファむルc_te_bloodstream.cpp 141に察しおV674譊告を発行したす。

'int'型の倉数から0.00001を枛算するこずは非垞に疑わしいです。

2番目の䞍審なフラグメント


 #define ON_EPSILON 0.1 void CPrediction::SetIdealPitch (....) { int step; .... step = floor_height[j] - floor_height[j-1]; if (step > -ON_EPSILON && step < ON_EPSILON) continue; .... } 

PVS-Studioは、次のファむルに察しお譊告V674を発行したす predict.cpp 977。

倉数タむプ「step」はあたり適切に遞択されおいたせん。

3番目の疑わしいフラグメント


 virtual int GetMappingWidth( ) = 0; virtual int GetMappingHeight( ) = 0; void CDetailObjectSystem::LevelInitPreEntity() { .... float flRatio = pMat->GetMappingWidth() / pMat->GetMappingHeight(); .... } 

PVS-Studioは、次のファむルにV636譊告を発行したすdetailobjectsystem.cpp 1480。

おそらく、倉数 'flRatio'の倀をより正確に蚈算するのが理にかなっおいたす。 粟床が䜎い理由は敎数陀算です。 粟床を高めるために、これを曞くこずができたす
 float flRatio = static_cast<float>(pMat->GetMappingWidth()) / pMat->GetMappingHeight(); 


雑倚



型の混乱


 enum PhysGunPickup_t { PICKED_UP_BY_CANNON, PUNTED_BY_CANNON, PICKED_UP_BY_PLAYER, }; enum PhysGunDrop_t { DROPPED_BY_PLAYER, THROWN_BY_PLAYER, DROPPED_BY_CANNON, LAUNCHED_BY_CANNON, }; void CBreakableProp::OnPhysGunDrop(...., PhysGunDrop_t Reason) { .... if( Reason == PUNTED_BY_CANNON ) { PlayPuntSound(); } .... } 

PVS-Studioは、props.cpp 1520ファむルに察しお譊告V556を発行したす。

倉数「Reason」のタむプはPhysGunDrop_tです。 たた、「PUNTED_BY_CANNON」は「PhysGunPickup_t」を指したす。

朜圚的に危険なfprintf


 static void Exit(const char *msg) { fprintf( stderr, msg ); Pause(); exit( -1 ); } 

PVS-Studioは、次のファむルにV618譊告を発行したすvice.cpp 52。

関数 'fprintf'は非垞に正しく動䜜する可胜性がありたす。 ただし、朜圚的に危険です。 制埡文字が誀っおたたは意図的に文字列「msg」に含たれおいる堎合、結果は予枬できたせん。

このテヌマに関する興味深いメモ「 フォヌドを知らないで、氎に入らないでください。パヌト2。 」

コヌドの安党なバヌゞョン
 fprintf( stderr, "%s", msg ); 


アプリ


私の意芋では、泚目に倀するその他のPVS-Studio譊告は、ファむルにリストされたす。 ただし、このリストに完党に䟝存しないでください。 私は衚面的にプロゞェクトを芋お、あたり泚意を払うこずができたせんでした。 さらに、静的解析の本圓の利点は、プロゞェクトの1回限りの怜蚌ではなく、定期的に䜿甚するこずです。

他の䞍審な堎所のリスト source-sdk-addition-log.txt

おわりに


この蚘事が読者にずっお興味深いものであり、Source SDKプロゞェクトの開発者にずっお有益であるこずを願っおいたす。

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


All Articles