Powder Toyは 、PVS-Studio 5.20を使用してテストされました。 このプロジェクトは、Pythonスクリプトを使用してmsysのWindows上でビルドされるため、検証に特別なユーティリティPVS-Studio Standaloneを使用しました。 簡単ですぐに使用できます 。
検証結果
V501 「||」の左と右に同一のサブ式があります 演算子:!s [1] ||!s [2] ||!s [1] graphics.cpp 829
void Graphics::textsize(const char* s, int& width, int& height) { .... else if (*s == '\x0F') { if(!s[1] || !s[2] || !s[1]) break; //<== s+=3; //<== } .... }
特定の条件下では、文字配列の3つの連続する要素がチェックされますが、タイプミスにより要素s [3]がチェックされなかったため、状況によってはプログラムが正しく動作しない可能性があります。
V523 「then」ステートメントは「else」ステートメントと同等です。 button.cpp 142
void Button::Draw(const Point& screenPos) { .... if(Enabled) if(isButtonDown || (isTogglable && toggle)) { g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 255, iconInvert); } else { g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 255, iconInvert); } else g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 180, iconInvert); .... }
疑わしく同一のコードを持つ関数の断片。 条件式には多数の論理演算が含まれているため、このコードフラグメントには無駄なチェックが含まれていないと想定できますが、draw_icon()関数の最後から2番目のパラメーターで入力ミスが許可されます。 つまり どこかに値「255」を書き込まないでください。
同様の場所:
- V523「then」ステートメントは「else」ステートメントと同等です。 luascriptinterface.cpp 2758
- V523「then」ステートメントは「else」ステートメントと同等です。 searchview.cpp 305
V530関数 'empty'の戻り値を使用する必要があります。 requestbroker.cpp 309
std::vector<Request*> Children; RequestBroker::Request::~Request() { std::vector<Request*>::iterator iter = Children.begin(); while(iter != Children.end()) { delete (*iter); iter++; } Children.empty(); //<== }
ベクトルをクリアする代わりに、関数 'empty()'が呼び出されましたが、これはベクトルを変更しません。 コードはデストラクタ内にあるため、おそらくエラーはプログラムの実行に影響しません。 しかし、それにもかかわらず、私はこの瞬間を表明することにしました。
V547式 'partsData [i]> = 256'は常にfalseです。 符号なしchar型の値の範囲:[0、255]。 gamesave.cpp 816:
#define PT_DMND 28 //#define PT_NUM 161 #define PT_NUM 256 unsigned char *partsData = NULL, void GameSave::readOPS(char * data, int dataLength) { .... if(partsData[i] >= PT_NUM) partsData[i] = PT_DMND; //Replace all invalid elements.... .... }
コードには、作成者にのみ明らかな疑わしい場所が含まれています。 以前は、 'partsData'配列のi番目の要素が161以上の場合、値28が要素に書き込まれましたが、定数161がコメント化され、256に置き換えられました。その結果、条件は満たされません。 'unsigned char'の最大値:255。
V547式は常に偽です。 おそらく '||' ここで演算子を使用する必要があります。 previewview.cpp 449:
void PreviewView::NotifySaveChanged(PreviewModel * sender) { .... if(savePreview && savePreview->Buffer && !(savePreview->Width == XRES/2 && //<== savePreview->Width == YRES/2)) //<== { pixel * oldData = savePreview->Buffer; float factorX = ((float)XRES/2)/((float)savePreview->Width); float factorY = ((float)YRES/2)/((float)savePreview->Height); float scaleFactor = factorY < factorX ? factorY : factorX; savePreview->Buffer = Graphics::resample_img(....); delete[] oldData; savePreview->Width *= scaleFactor; savePreview->Height *= scaleFactor; } .... }
運のおかげで、条件の一部は常に真実です。 ここにタイプミスの可能性が高い:おそらく '||'演算子を使用する必要がある 「&&」の代わりに、またはある場合には、例えば「savePreview-> Height」をチェックする必要があります。
V560条件式の一部は常に真です:0x00002。 frzw.cpp 34
unsigned int Properties; Element_FRZW::Element_FRZW() { .... Properties = TYPE_LIQUID||PROP_LIFE_DEC; .... }
変数「Properties」を持つコード全体で、ビットごとの演算が実行されますが、2つの場所で「||」が使用されます 「|」の代わりに。 値1がプロパティに書き込まれることがわかります。
2番目のそのような場所:
- V560条件式の一部は常に真です:0x04000。 frzw.cpp 34
V567未定義の動作。 'sandcolour_frame'変数は、シーケンスポイント間で2回使用されている間に変更されます。 Simulation.cpp 4744
void Simulation::update_particles() { .... sandcolour_frame = (sandcolour_frame++)%360; .... }
変数 'sandcolour_frame'は、シーケンスの同じポイントで2回使用されます。 その結果、そのような式の結果を予測することは不可能です。 詳しくは、 V567診断の説明をご覧ください 。
V570 「parts [i] .colour」変数はそれ自体に割り当てられます。 fwrk.cpp 82
int Element_FWRK::update(UPDATE_FUNC_ARGS) { .... parts[i].life=rand()%10+18; parts[i].ctype=0; parts[i].vx -= gx*multiplier; parts[i].vy -= gy*multiplier; parts[i].dcolour = parts[i].dcolour; //<== .... }
固有値による疑わしいフィールドの初期化。
V576形式が正しくありません 。 'printf'関数の3番目の実引数を確認することを検討してください。 ポインターの値を出力するには、「%p」を使用する必要があります。 powdertoysdl.cpp 3247
int SDLOpen() { .... SDL_SysWMinfo SysInfo; SDL_VERSION(&SysInfo.version); if(SDL_GetWMInfo(&SysInfo) <= 0) { printf("%s : %d\n", SDL_GetError(), SysInfo.window); exit(-1); } .... }
ポインターを印刷するには、%p指定子を使用します。
V595 nullptrに対して検証される前に、「gameSave」ポインターが使用されました。 行を確認してください:1063、1070。gamecontroller.cpp 1063
void GameController::OpenLocalSaveWindow(bool asCurrent) { Simulation * sim = gameModel->GetSimulation(); GameSave * gameSave = sim->Save(); //<== gameSave->paused = gameModel->GetPaused(); gameSave->gravityMode = sim->gravityMode; gameSave->airMode = sim->air->airMode; gameSave->legacyEnable = sim->legacy_enable; gameSave->waterEEnabled = sim->water_equal_test; gameSave->gravityEnable = sim->grav->ngrav_enable; gameSave->aheatEnable = sim->aheat_enable; if(!gameSave) //<== { new ErrorMessage("Error", "Unable to build save."); } .... }
最初に「gameSave」ポインタのゼロをチェックしてから、フィールドに入力する方が論理的です。
さらにいくつかのそのような場所:
- V595 nullptrに対して検証される前に、「newSave」ポインターが使用されました。 行を確認:972、973。powdertoysdl.cpp 972
- V595 nullptrに対して検証される前に、「gameSave」ポインターが使用されました。 行を確認します:1271、1278。gamecontroller.cpp 1271
- V595 nullptrに対して検証される前に、「gameSave」ポインターが使用されました。 行を確認してください:1323、1330。gamecontroller.cpp 1323
- V595 nullptrに対して検証される前に、「state_」ポインターが使用されました。 行をチェック:220、232。engine.cpp 220
V611メモリーは「new T []」演算子を使用して割り当てられましたが、「delete」演算子を使用して解放されました。 このコードを調べることを検討してください。 「delete [] userSession;」を使用することをお勧めします。 apirequest.cpp 106
RequestBroker::ProcessResponse APIRequest::Process(RequestBroker & rb) { .... if(Client::Ref().GetAuthUser().ID) { User user = Client::Ref().GetAuthUser(); char userName[12]; char *userSession = new char[user.SessionID.length() + 1]; .... delete userSession; //<== } .... }
演算子new、new []、delete、delete []は、対応するペアで使用する必要があります。 ここで正しいでしょう: "delete [] userSession;"。
そして、この場所だけではありません:
- V611メモリーは「new T []」演算子を使用して割り当てられましたが、「delete」演算子を使用して解放されました。 このコードを調べることを検討してください。 「delete [] userSession;」を使用することをお勧めします。 webrequest.cpp 106
- V611メモリーは「new T []」演算子を使用して割り当てられましたが、「delete」演算子を使用して解放されました。 このコードを調べることを検討してください。 「delete [] workingDirectory;」を使用することをお勧めします。 optionsview.cpp 228
V614未初期化ポインター 'ndata'が使用されました。 Simulation.cpp 1688
void *Simulation::transform_save(....) { void *ndata; .... //ndata = build_save(....); //TODO: IMPLEMENT .... return ndata; }
この場所が計画的に完了する前に、関数は初期化されていないポインターを返します。
同様の場所:
- V614潜在的に初期化されていないポインター 'tempThumb'が使用されました。 saverenderer.cpp 150
おわりに
Powder Toyは、ゲーム、トレーニング、実験に使用できる興味深いクロスプラットフォームプロジェクトです。 プロジェクトの規模は小さいにも関わらず、それを理解することは興味深いものでした。 著者がソースコードの分析に注意を払い、完全な検証ログを分析することを願っています。
静的分析を定期的に使用すると、より便利なタスクやTODOを解決するのに多くの時間を節約できます。
この記事は英語です。
英語を話す聴衆とこの記事を共有したい場合は、翻訳へのリンクを使用してください:Svyatoslav Razmyslov。 The Powder Toy Simulatorの分析 。
記事を読んで質問がありますか?
多くの場合、記事には同じ質問が寄せられます。 ここでそれらに対する回答を収集しました: PVS-StudioおよびCppCatバージョン2014に関する記事の読者からの質問への回答 。 リストをご覧ください。