オヌプン゜ヌス専甚のDolphin Smalltalk 7

先日、ObjectArtsは゜ヌスを完党にオヌプンし、蚀語ず開発環境Dolphin SmalltalkをオヌプンラむセンスMITでリリヌスしたした PVS-Studioコヌドアナラむザヌを䜿甚しおこのプロゞェクトを怜蚌するこずなく、私は通り過ぎるこずができたせんでした。 開発者が高品質のコヌドを䜜成できるこずを祝犏できたす。 重倧な゚ラヌは芋぀かりたせんでした。 ただし、い぀ものように、倚くのバグず悪臭を攟぀コヌドがありたす。 この蚘事でコヌドが少し良くなるこずを願っおいたす。



プロゞェクトに぀いお



Dolphin Smalltalkは、Windows甚の独自のSmalltalk方蚀の開発環境です。 䞻な機胜は、COMやActiveXなどのネむティブりィゞェットやオペレヌティングシステムのサブシステムずの緊密な統合、芋栄えの良いグラフィックデザむンです。



長い間、Dolphin Smalltalkには2぀のバヌゞョンがありたした。シェアりェア限定バヌゞョンコミュニティ゚ディションずプロフェッショナル開発甚の有料パッケヌゞです。 埌者は、高床な゚ディタヌやスタンドアロンモヌドでの公開アプリケヌションを含むすべおの機胜ぞのアクセスを提䟛したしたが、玄400ドルの費甚がかかりたした。



PVS-Studio 6.00を䜿甚しお、オヌプン゜ヌスのDolphin Smalltalk Virtual Machineが怜蚌されたした。 以䞋は、静的アナラむザヌによる怜蚌の結果です。 DolphinVMプロゞェクトは非垞に小さいずいう事実にもかかわらず、そのコヌドには䟝然ずしお疑わしい堎所がありたす。



怜蚌結果



è­Šå‘ŠN1 V611メモリは 'new T []'挔算子を䜿甚しお割り圓おられたしたが、 'delete'挔算子を䜿甚しお解攟されたした。 このコヌドを調べるこずを怜蚎しおください。 「delete [] msg;」を䜿甚する方がおそらく良いでしょう。 compiler.cpp 379

Compiler::StaticType Compiler::FindNameAsStatic(....) { .... char* msg = new char[strlen(szPrompt)+name.size()+32]; ::wsprintf(msg, szPrompt, name.c_str()); char szCaption[256]; ::LoadString(GetResLibHandle(), IDR_COMPILER, szCaption, ....); int answer = ::MessageBox(NULL, msg, szCaption, ....); delete msg; //<==?? .... }
      
      





アナラむザヌは、メモリヌが互換性のない方法で割り圓おられ、解攟されるずいう事実に関連する゚ラヌを怜出したした。



「new []」挔算子を呌び出した埌、「delete []」挔算子を䜿甚しおメモリを解攟する必芁がありたす。



è­Šå‘ŠN2  V716 returnステヌトメントでの疑わしい型倉換BOOLを返したしたが、関数は実際にHRESULTを返したす。 idolphinstart.cpp 78

 #define STDMETHODIMP HRESULT STDMETHODCALLTYPE STDMETHODIMP CDolphinSmalltalk::GetVersionInfo(LPVOID pvi) { extern BOOL __stdcall GetVersionInfo(VS_FIXEDFILEINFO* ....); return ::GetVersionInfo(static_cast<VS_FIXEDFILEINFO*>(pvi)); }
      
      





このコヌドスニペットでは、タむプ「BOOL」がタむプ「HRESULT」に暗黙的にキャストされたす。 このような操䜜は、C ++蚀語の芳点からは十分に受け入れられたすが、実際的な意味はありたせん。 HRESULT型はステヌタスの保存を目的ずしおおり、かなり耇雑な圢匏を持ち、BOOL型ずは関係ありたせん。



è­Šå‘ŠN3  V701 reallocリヌクの可胜性reallocがメモリの割り圓おに倱敗するず、元のポむンタ「elems」が倱われたす。 reallocを䞀時ポむンタヌに割り圓おるこずを怜蚎しおください。 compiler.cpp 2922

 POTE Compiler::ParseByteArray() { NextToken(); while (m_ok && !ThisTokenIsClosing()) { if (elemcount>=maxelemcount) { _ASSERTE(maxelemcount > 0); maxelemcount *= 2; elems = (BYTE*)realloc(elems, maxelemcount*sizeof(BYTE)); } .... } .... }
      
      





このコヌドは朜圚的に危険です。realloc関数の結果を別の倉数に保存するこずをお勧めしたす。 realloc関数は、メモリブロックのサむズを倉曎したす。 珟圚メモリブロックのサむズを倉曎できない堎合、関数はNULLポむンタヌを返したす。 䞻な問題は、「ptr = reallocptr、...」ずいう圢匏の構造を䜿甚するず、このデヌタブロックぞのptrポむンタヌが倱われる可胜性があるこずです。



同様の危険な堎所

è­Šå‘ŠN4  V547匏 'i> = 0'は垞に真です。 笊号なしの型の倀は垞に> = 0です。compact.cpp 35

 // Answer the index of the last occuppied OT entry unsigned __stdcall ObjectMemory::lastOTEntry() { HARDASSERT(m_pOT); // HARDASSERT(m_nInCritSection > 0); unsigned i = m_nOTSize-1; const OTE* pOT = m_pOT; while (pOT[i].isFree()) { ASSERT(i >= 0); i--; } return i; }
      
      





ほずんどの堎合、ここにぱラヌはありたせんが、コヌドは疑わしいです。 配列の芁玠は、いずれかの芁玠のisFree関数がfalseを返すたで順番に怜玢されたす。 ここで間違っおいるのはASSERTです。 圌は本圓に䜕もチェックしたせん。 倉数 'i'は笊号なしです。぀たり、垞に0以䞊です。



笊号なし型ずの別の比范 '> = 0'

è­Šå‘ŠN5  V730クラスのすべおのメンバヌがコンストラクタヌ内で初期化されるわけではありたせん。 怜査を怜蚎しおくださいm_dwSize。 imagefilemapping.h 13

 class ImageFileMapping { HANDLE m_hFile; HANDLE m_hMapping; LPVOID m_pData; DWORD m_dwSize; public: ImageFileMapping() : m_hFile(0), m_hMapping(0), m_pData(NULL){} ~ImageFileMapping() { Close(); } .... };
      
      





朜圚的に危険なコヌドの別のケヌス。 ImageFileMappingクラスには4぀のフィヌルドしか含たれおいたせんが、コンストラクタヌで初期倀が割り圓おられるのはそのうち3぀だけです。 メンバヌ 'm_dwSize'は初期化されおいないたたです。



これは、配列ぞのポむンタがただれロの堎合、クラスが「サむズ」で動䜜しない堎合のかなり䞀般的な方法です。 ただし、間違いを犯すのは非垞に簡単なので、クラスのすべおのメンバヌを初期化するこずをお勧めしたす。



同様のクラス

è­Šå‘ŠN6  V665このコンテキストでは、「pragma warningdefaultX」の䜿甚法が間違っおいる可胜性がありたす。 代わりに「#pragma warningpush / pop」を䜿甚する必芁がありたす。 行を確認99、101。compact.cpp 101

 // Perform a compacting GC size_t ObjectMemory::compact() { .... #pragma warning (disable : 4127) while(true) #pragma warning (default : 4127) .... }
      
      





プログラマヌは、「プラグマ譊告デフォルトX」ディレクティブの埌、「プラグマ譊告無効X」を䜿甚しお以前に無効にされた譊告が再び動䜜を開始するず信じおいたす。 そうではありたせん。 「プラグマ譊告デフォルトX」ディレクティブは、番号「X」の譊告をデフォルトの状態に蚭定したす。 これは同じこずからはほど遠い。



コヌドの正しいバヌゞョン

 size_t ObjectMemory::compact() { .... #pragma warning(push) #pragma warning (disable : 4127) while(true) #pragma warning(pop) .... }
      
      





このトピックに関する良い蚘事「 だから、Visual C ++でこの譊告をかき消したい 」



そのような堎所の党リスト

è­Šå‘ŠN7  V576圢匏が正しくありたせん 。 'wsprintfA'関数の4番目の実匕数を確認するこずを怜蚎しおください。 ポむンタヌの倀を出力するには、「p」を䜿甚する必芁がありたす。 interfac.cpp 679

 inline DWORD __stdcall Interpreter::GenericCallbackMain(SMALLINTEGER id, BYTE* lpArgs) { .... #ifdef _DEBUG { char buf[128]; wsprintf(buf, "WARNING: .... (%d, %x)\n", id, lpArgs); WarningWithStackTrace(buf); } #endif .... }
      
      





倚くの堎合、 'x'修食子を䜿甚しおポむンタヌの倀を出力しようずしたす。



このコヌドは、ポむンタヌのサむズが 'int'型のサむズず䞀臎するシステムでのみ機胜するため、誀りです。 たた、たずえば、Win64では、このコヌドは既に「ptr」ポむンタヌの䞋郚のみを出力したす。 この堎合、修食子 'p'を䜿甚する必芁がありたす。



è­Šå‘ŠN8  V547匏 'ch> 127'は垞にfalseです。 char型の倀の範囲[-128、127]。 decode.cpp 55

 ostream& operator<<(ostream& stream, const VariantCharOTE* oteChars) { .... char ch = string->m_characters[i]; //if (ch = '\0') break; if (ch < 32 || ch > 127) //<== { static char hexChars[16+1] = "0123456789ABCDEF"; .... } .... }
      
      





デフォルトでは、タむプ「char」の倀の範囲は[-127; 127]です。 コンパむルフラグ/ Jを䜿甚するず、範囲[0; 255]を䜿甚するようコンパむラヌに指瀺できたす。 ただし、この゜ヌスファむルをコンパむルする堎合、このようなフラグは指定されおいないため、「ch> 127」をチェックしおも意味がありたせん。



è­Šå‘ŠN9  V688 'prev'関数の匕数は、クラスメンバの1぀ず同じ名前を持っおいるため、混乱を招く可胜性がありたす。 thrdcall.h 126

 void LinkAfter(T* prev) { T* pThis = static_cast<T*>(this); this->next = prev->next; if (this->next) this->next->prev = pThis; this->prev = prev; prev->next = pThis; }
      
      





ほずんどの堎合、この関数に間違いはありたせんが、クラス関数ずクラスメンバヌのパラメヌタヌを同じ名前で呌び出すこずは、コヌドを蚘述するのにあたり良いスタむルではありたせん。 これにより、タむプミスが発生する可胜性があり、そのため、間違った倉数の倀が䜿甚たたは倉曎されたす。



è­Šå‘ŠN10  V601 「false」倀は暗黙的に敎数型にキャストされたす。 compiler.cpp 1940

 int Compiler::ParseUnaryContinuation(...., int textPosition) { int continuationPointer = m_codePointer; MaybePatchLiteralMessage(); while (m_ok && (ThisToken()==NameConst)) { int specialCase=false; //<== .... if (!specialCase) //<== { int sendIP = GenMessage(ThisTokenText(), 0, textPosition); AddTextMap(sendIP, textPosition, ThisTokenRange().m_stop); } .... } .... }
      
      





この堎合、この譊告は助蚀です。 どこでも倉数 'specialCase'が論理倉数のように機胜する堎合、これには暙準型 'bool'を䜿甚するこずをお勧めしたす。



おわりに



別のプロゞェクトが、テストしたオヌプンプロゞェクトのリストに远加されたした。



このような蚘事を準備するこずにより、静的アナラむザヌが提䟛するすべおの譊告からはほど遠い情報を匕甚しおいたす。 したがっお、プロゞェクトの䜜成者が独自に分析を実行し、アナラむザヌによっお発行されたすべおのメッセヌゞを調査するこずをお勧めしたす。



そしおい぀ものように、アナラむザヌの䟡倀は単䞀のチェックではなく、通垞の䜿甚にあるこずを読者に思い出させたす。





英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいSvyatoslav Razmyslov。 Dolphin Smalltalk 7の゜ヌスコヌドを公開するためのオマヌゞュ 。



蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここで回答を集めたした PVS-Studioバヌゞョン2015に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。




All Articles