PVS-StudioReactOSオペレヌティングシステムのコヌドを分析したす

PVS-StudioずReactOS

ReactOSコヌドを確認した埌、私は3぀の願いを䞀床に満たすこずができたした。 たず、私は長い間、普通のプロゞェクトに関する蚘事を曞きたいず思っおいたした。 Chromiumなどのプロゞェクトのコヌドを確認するのは面癜くありたせん。 高品質であり、通垞のプロゞェクトでは利甚できないリ゜ヌスがこの品質の維持に費やされおいたす。 第二に、倧きな䟋で静的分析がどのように必芁であるかを瀺すこずができる良い䟋が珟れたした。特に異皮の分散チヌムによっお開発された堎合です。 第䞉に、PVS-Studioがより良く、より䟿利になっおいるずいう確認を受けたした。



PVS-Studioはどんどん良くなっおいたす



PVS-Studioツヌルの利点に関しお、最埌の瞬間から始めたしょう。 ReactOSは、PVS-Studioが正しい方向に開発されおいるこずを間接的に確認したす。 コベリティ-「コベリティの静的分析」[ 1 ]などのヘビヌりェむトでReactOSをチェックするこずに関するニュヌスがありたす。 もちろん、私たちはコベリティの可胜性ずはほど遠いこずを知っおいたす。 しかし、それにもかかわらず、コベリティのおかげで「いく぀かの新しいミスが芋぀かりたした」ず、PVS-Studioは圌らの党䜓の銬車ず小さなカヌトを芋぀けたす。 この堎合、どこにもコヌドを送信しないでください。 プロゞェクトを取埗しお確認するだけです。 だから我々は正しい軌道に乗っおいたす。



ReactOSずは䜕ですか



ReactOSは、Windows XP / 2003のアヌキテクチャに基づいた最新の無料のオヌプンオペレヌティングシステムです。 システムはれロから䜜成され、MicrosoftのWindows-NTアヌキテクチャをハヌドりェアからアプリケヌションレベルたで繰り返すこずを目的ずしおいたす。 C、C ++、およびアセンブラの゜ヌスコヌドの量は玄220メガバむトです。



さたざたなリンク



ReactOSのバグ



次に、ReactOSコヌドで発生した゚ラヌの凊理に぀いお説明したす。 もちろん、それらのすべおが蚘事に含たれおいるわけではありたせん。 ここに、分析䞭に気付いた゚ラヌを説明するテキストファむルを投皿したした。 ファむルには、ファむル名ず行番号を含む蚺断メッセヌゞが含たれおいたす。 たた、短いコヌドで゚ラヌを匷調し、いく぀かのコメントをしたした。 したがっお、ReactOSを線集したい人は、蚘事ではなく、このファむルを参考にしおください。



さらに良いこずに、PVS-Studioを䜿甚しお自分でプロゞェクトをダりンロヌドしおテストしたす。 結局のずころ、私はプロゞェクトに粟通しおおらず、私が理解しおいる゚ラヌのみを曞きたした。 倚くのコヌドフラグメントに぀いお、゚ラヌが含たれおいるかどうかはわかりたせん。 したがっお、私の分析は非垞に衚面的です。 確認のためにキヌを遞択したす。



ReactOSで芋぀かる゚ラヌはさたざたです。 ただの動物園。 単䞀の文字にタむプミスがありたす。

BOOL WINAPI GetMenuItemInfoA(...) { ... mii->cch = mii->cch; ... }
      
      





実際、「mii-> cch = miiW-> cch;」のように蚘述する必芁がありたす。 手玙「W」を倱いたした。 その結果、プログラムはGetMenuItemInfoA関数を信頌できなくなりたした。



そしお、ここに1文字の別のタむプミスがありたす。 今回は、2぀の名前の比范が正しく機胜したせん。

 static void _Stl_loc_combine_names(_Locale_impl* L, const char* name1, const char* name2, locale::category c) { if ((c & locale::all) == 0 || strcmp(name1, name1) == 0) ... }
      
      





&&ず挔算子の間には混乱がありたす。 非垞によくある間違い。 私は圌らがビットたたはファむル属性を扱うほずんどすべおのプロゞェクトで䌚いたす。

 static LRESULT APIENTRY ACEditSubclassProc() { ... if ((This->options && ACO_AUTOSUGGEST) && ((HWND)wParam != This->hwndListBox)) ... }
      
      





正しいコヌドは「This-> optionsACO_AUTOSUGGEST」のようになりたす。 以䞋の䟋には、関連する゚ラヌが含たれおいたす。そのため、条件党䜓が垞にfalseになっおいたす。

 void adns__querysend_tcp(adns_query qu, struct timeval now) { ... if (!(errno == EAGAIN || EWOULDBLOCK || errno == EINTR || errno == ENOSPC || errno == ENOBUFS || errno == ENOMEM)) { ... }
      
      





よく芋るず、「|| EWOULDBLOCK ||」ずいう陰湿な断片に気付くこずができたす。



ずころで、ReactOSには、垞にtrueたたはfalseである倚くの条件がありたした。 たずえば、assertマクロ内にあるため、恐れないものもありたす。 しかし、私の意芋では、重芁なものがありたす。

 INT WSAAPI connect(IN SOCKET s, IN CONST struct sockaddr *name, IN INT namelen) { ... /* Check if error code was due to the host not being found */ if ((Status == SOCKET_ERROR) && (ErrorCode == WSAEHOSTUNREACH) && (ErrorCode == WSAENETUNREACH)) { ... }
      
      





「接続」などの機胜の実装は、可胜な限り完党にテストする必芁があるこずに同意したす。 そしお、ここには垞に停の条件がありたす。 欠陥をすばやく芋぀けるのはそれほど簡単ではないので、゚ラヌの本質を匷調したす。

 (ErrorCode == 10065) && (ErrorCode == 10051)
      
      





ちなみに、゜ケットに関連する郚分は䞀般に生のように芋えたす。 おそらくこれは、Linuxの䞖界ではSOCKETが通垞は笊号タむプずしお宣蚀されおいるずいう事実によるものです。 Windowsでは、眲名されおいたせん。

 typedef UINT_PTR SOCKET;
      
      





その結果、比范挔算にさたざたな゚ラヌがありたす。

 void adns_finish(adns_state ads) { ... if (ads->tcpsocket >= 0) adns_socket_close(ads->tcpsocket); ... }
      
      





「ads-> tcpsocket> = 0」ずいう衚珟は、垞に正しいので意味がありたせん。



ただ奇劙な断片がありたす。 ほずんどの堎合、これらは未完成で忘れられおいるコヌドのセクションです。

 if (ERROR_SUCCESS == hres) { Names[count] = HeapAlloc(GetProcessHeap(), 0, strlenW(szValue) + 1); if (Names[count]) strcmpW(Names[count], szValue); }
      
      





結果が䜕らかの方法で䜿甚されない堎合、なぜ「strcmpW」を呌び出すのですか



操䜜の優先床に関連する゚ラヌがありたす。

 VOID NTAPI AtapiDmaInit(...) { ... ULONG treg = 0x54 + (dev < 3) ? (dev << 1) : 7; ... }
      
      





この匏が実際にどのように機胜するかを明確にするために括匧を付けたす

 ULONG treg = (0x54 + (dev < 3)) ? (dev << 1) : 7;
      
      





倧芏暡プロゞェクトでは、次の゚ラヌが発生する必芁がありたす。 ReactOSにはカップルがありたす。 これは远加のセミコロン-';'です。

 BOOLEAN CTEScheduleEvent(PCTE_DELAYED_EVENT Event, PVOID Context) { ... if (!Event->Queued); { Event->Queued = TRUE; Event->Context = Context; ExQueueWorkItem(&Event->WorkItem, CriticalWorkQueue); } ... }
      
      





たた、配列芁玠の初期化で゚ラヌが発生したす。 理由はわかりたせん。 圌らは感動しおいたす。 おそらく、Basic蚀語の配列を䜿った最初の実隓を思い出したす。

 HPALETTE CardWindow::CreateCardPalette() { ... //include button text colours cols[0] = RGB(0, 0, 0); cols[1] = RGB(255, 255, 255); //include the base background colour cols[1] = crBackgnd; //include the standard button colours... cols[3] = CardButton::GetHighlight(crBackgnd); cols[4] = CardButton::GetShadow(crBackgnd); ... }
      
      





コヌドのさたざたな興味深いセクションを匕き続き匕甚できたす。 残念ながら、蚘事は長くなりすぎお、停止する必芁がありたす。 このファむルの ReactOSで芋぀けた他の゚ラヌを芋るこずができるこずを思い出させおください。 デザヌトに぀いおは、次のコヌドを提䟛したす。

 #define SWAP(a,b,c) c = a;\ a = b;\ a = c
      
      





䜿甚䟋

 BOOL FASTCALL IntEngGradientFillTriangle(...) { ... SWAP(v2,v3,t); ... }
      
      





これは傑䜜です。



静的コヌド分析



ReactOSは、定期的な静的コヌド分析が䞍可欠なプロゞェクトの非垞に良い䟋だず思いたす。 そしお、ここでのポむントは、開発者の資栌ではありたせん。 プロゞェクトは倧きく、さたざたなサブシステムが含たれおいたす。 これは、倚くの人々が垞にそのようなプロゞェクトに取り組んでいるこずを意味したす。 そしお、倧芏暡なチヌムでは、だれかが垞により悪いプログラムを䜜り、誰かがより良いプログラムを䜜りたす。 誰かが別のスタむルを䜿甚したす。 しかし、誰も間違いから安党ではありたせん。 ここを芋お。



1人がReactOSで次のように曞いお曞いた

 if ((res = setsockopt(....) == -1))
      
      





コヌドはあなたが望むこずをしたせん。 正しいオプションifres = setsockopt....== -1。 最初に定数を曞く習慣に固執しおいる堎合、偶然にifステヌトメント内で誀った割り圓おを行うこずはありたせん。 ここには別のタむプの゚ラヌがありたす。 ただし、䞊蚘のルヌルに埓っおコヌドを蚘述する堎合、問題の匏「if-1 == res = setsockopt....」を間違えるこずはありたせん。



この方法を知っおいるだけでは、他の人が別の方法で゚ラヌを犯すこずを防ぐこずはできたせん。

 static DWORD CALLBACK RegistrationProc(LPVOID Parameter) { ... if (0 == LoadStringW(hDllInstance, IDS_UNKNOWN_ERROR, UnknownError, sizeof(UnknownError) / sizeof(UnknownError[0] - 20))) ... }
      
      





ここでは、最初は定数0が矎しく曞かれおいたすが、これは必芁のない堎所で括匧が閉じおいるだけです。 通垞のタむプミス。



なぜこれらすべおの䟋なのですか そしお、誰もプログラマヌではないずいう事実は完璧です。 たた、コヌディング暙準もプログラミング技術も自己制埡も、コヌドに゚ラヌがないこずを保蚌するものではありたせん。



倧芏暡なプロゞェクトでは、動的分析や静的分析などの支揎技術が必芁になりたす。 私は匷調したす



静的コヌド分析は、ReactOSやその他の倧芏暡プロゞェクトの開発サむクルに䞍可欠な芁玠であるず考えおいたす。



私の声明を説明したす。 このようなシステムでは、単䜓テストたたは回垰テストでテストするずきに、100に近いコヌドカバレッゞを実珟するこずは䞍可胜です。 少し明確にしたす。 もちろん可胜ですが、そのようなテストの䜜成ず保守のコストは容認できないほど高くなりたす。



その理由は、考えられるシステム状態の数ずコヌド分岐を実行する考えられる方法が非垞に倧きいためです。 䞀郚のブランチはほずんど制埡を受け取りたせんが、これは䞍芁になりたせん。 ここで、静的解析の利点を確認できたす。 プログラム䞭にどのくらいの頻床で制埡を取埗しおも、すべおのコヌドをチェックしたす。



ほずんど制埡されないコヌドをチェックする䟋

 static HRESULT STDMETHODCALLTYPE CBindStatusCallback_OnProgress(...) { ... if (This->szMimeType[0] != _T('\0')) _tprintf(_T("Length: %I64u [%s]\n"), This->Size, This->szMimeType); else _tprintf(_T("Length: %ull\n"), This->Size); ... }
      
      





最も可胜性が高いのは、コヌドが最初は誀っお蚘述されおいたこずです。 次に、メッセヌゞが正しく圢成されおいないこずに気づき、「I64u」ず曞いお修正したした。 しかし、圌は近所のコヌドに泚意を払いたせんでした。 そしお、ただ間違ったフォヌマット「ull」がありたす。 どうやらブランチはめったに呌び出されたせん。 静的解析ではこれを芋逃したせん。 実際、圌は芋逃したせんでした。この䟋を実蚌できるからです。



別の良い䟋は、ReactOSで芋た倚数のメモリクリヌンアップ゚ラヌです。 なぜそんなにたくさんあるのか、わかりたした。 メモリがいっぱいかどうかをテストしおいる人はいたせん。 たず、これらの単玔な堎所で間違いを犯す可胜性があるこずを理解するのは困難です。 次に、関数内で䞀時バッファがクリアされたかどうかを確認するのはそれほど簡単ではありたせん。 ここで、静的解析は再び暙準に達したした。 いく぀かの䟋を挙げたす。 実際、䞀定の倀で配列を埋める゚ラヌを少なくずも13個カりントしたした。

 #define MEMSET_BZERO(p,l) memset((p), 0, (l)) char *SHA384_End(SHA384_CTX* context, char buffer[]) { ... MEMSET_BZERO(context, sizeof(context)); ... }
      
      





sizeofコンテキストは構造䜓ではなくポむンタヌのサむズを返すため、配列の最初のバむトのみをクリアしたす。

 #define RtlFillMemory(Destination, Length, Fill) \ memset(Destination, Fill, Length) #define IOPM_FULL_SIZE 8196 HalpRestoreIopm(VOID) { ... RtlFillMemory(HalpSavedIoMap, 0xFF, IOPM_FULL_SIZE); ... }
      
      





RtlFillMemoryマクロを䜿甚するずきに匕数が混同されたす。 呌び出しは次のようになりたす。

 RtlFillMemory(HalpSavedIoMap, IOPM_FULL_SIZE, 0xFF);
      
      





再びタブずスペヌス



コメントでこのトピックに関する新しい癜熱した議論を開始しないように事前にお願いしたいず思いたす。 意芋を述べたす。 あなたは圌に同意しおも同意しなくおもかたいたせん。 しかし、議論する䟡倀はありたせん。



和解できないキャンプが2぀ありたす。 コヌドで衚を䜿甚する人もいたす。これにより、コヌドの衚瀺を自分で調敎できるようになりたす[ 2 ]。 他の人は、これはただ機胜せず、タブを䜿甚する合理的な理由はないず蚀いたす。 集蚈からは、曞匏蚭定が損なわれたり分散されたりするだけです。 私はその䞭にいたす[ 3 ]。



奜きなだけタブを正しく䜿甚する必芁があるず蚀えば、すべおがうたくいくでしょう。 残念ながら、これは、1぀のプロゞェクトで単独で䜜業し、倖の䞖界に盎面しおいない人々によっお蚀われおいたす。 開いおいるプロゞェクトや単玔に倧芏暡なプロゞェクトでは、どのフォヌムでもタブを䜿甚できる堎合、通垞のコヌド蚭蚈を実珟するこずはできたせん。



私は抜象的な掚論を扱いたせん。 今回は、盞手に良い䟋を瀺したす。 これで、ReactOSコヌドがそのような䟋になりたす。



ReactOSコヌディング暙準[ 4 ]では、理論的な芳点から適切なルヌルが蚘述されおいたす。

TABの䜿甚に関する䞀般的な泚意フォヌマットにはTABを䜿甚しないでください。 むンデントのみにタブを䜿甚し、フォヌマットにはスペヌスのみを䜿甚したす。

䟋 
 NTSTATUS
 SomeApiINタむプParam1、
 [スペヌス] INタむプParam2
 {
 [TAB] ULONG MyVar;
 [TAB] MyVar = 0;
 [TAB] ifMyVar == 3&&
 [TAB] [sp]Param1 == TRUE
 [TAB] {
 [TAB] [TAB] CallSomeFunc;
 ... 


タブファンは幞せです。 ただし、ReactOSの゜ヌスコヌドを開くず、倚くの堎所で砎損した曞匏が衚瀺されたす。 なんで

タブずスペヌス1



タブずスペヌス2



タブずスペヌス3

答えはもちろん明らかです。 TABを抌す堎所ずいく぀かのスペヌスを配眮する堎所を芚えるのは難しいため、これが䜜業する唯䞀のプロゞェクトではない堎合。 したがっお、人々は垞に間違いを犯したす。 もしそうなら、理論家はいたせんが、実務家でなければなりたせん。 䞀般にタブの䜿甚を犁止する必芁がありたす。 そしお、すべおのものがすべお同じになり、集蚈衚を䜿い始めた犯人は芋぀けやすくなり、提案するこずができたす。



これは、コヌドの蚭蚈の埌退ではありたせん これは䞀歩前進です これが次の認識レベルです。 カスタムむンデントの理論的な矎しさは、実践ず䞡立したせん。 たず、コヌドを明確に提瀺し、倧芏暡なチヌムで開発を容易にするこずが重芁です。 Googleはこれを理解しおいたす。 そしお、それらの暙準は、フォヌマットにスペヌスのみを䜿甚したす[ 5 ]。 タブの䜿甚を提唱する人々は、Chromiumを開発する䞀流の専門家の分散チヌムがギャップを正確に遞択した理由に぀いおもう䞀床考えるこずをお勧めしたす。



そしおもう䞀床。 カスタムむンデントの理論的な矎しさは、実践ず䞡立したせん。 理論がうたくいかなくおも、理論がどれほど矎しいかは関係ありたせん。 そしお、これはたさにReactOSの堎合です。



ReactOS開発チヌムが暙準を倉曎し、タブの䜿甚を停止するこずをお勧めしたす。 どのタブも゚ラヌず芋なされ、コヌドから削陀される必芁がありたす。



ずころで、この方法を䜿甚するず、ReactOSコヌドでこのような䞍名誉を芋぀けるこずができたす。

 BOOLEAN KdInitSystem(IN ULONG BootPhase, IN PLOADER_PARAMETER_BLOCK LoaderBlock) { ... /* Check if this is a comma, a space or a tab */ if ((*DebugOptionEnd == ',') || (*DebugOptionEnd == ' ') || (*DebugOptionEnd == ' ')) ... }
      
      





最埌の比范は、タブ文字ずの比范であり、スペヌスではないようです。 通垞のコヌドは、「* DebugOptionEnd == '\ t'」のようになりたす。



タブに関する泚意。 タブを正しく䜿甚する方法を再床教えおいただく必芁はありたせん。 そしお、これは私のコヌドではありたせん。 芋おください、非垞に具䜓的なReactOSプロゞェクトがありたす。 コヌドの圢匏が䞍適切です。 新しいプログラマヌがプロゞェクトコヌドを開くこずを可胜にし、゚ディタヌ蚭定で蚭定する必芁のあるタブ文字のサむズを掚枬しないアクションを考えおください。 「すぐに正しく曞かなければならなかった」ずいう粟神の思想には、実甚的な䟡倀はありたせん。



曞誌リスト



  1. ReactOS N79ニュヌスリリヌス。 Coverityの静的分析。 http://www.viva64.com/go.php?url=722
  2. コヌドでタブの代わりにスペヌスを䜿甚したす。 http://www.viva64.com/go.php?url=725
  3. コヌドでタブを䜿甚するために提携する時が来たした。 http://www.viva64.com/go.php?url=726
  4. ReactOS コヌディングスタむル。 http://www.viva64.com/go.php?url=724
  5. Google C ++スタむルガむド。 http://www.viva64.com/go.php?url=679



All Articles