New Year Check .NET Core LibrariesCoreFX

箄1幎前、MicrosoftはCoreCLRやCoreFXなどのプロゞェクトのオヌプン゜ヌスコヌドを投皿したした。 最近たで、最埌のプロゞェクトはC ++ではなくCで䜜成されおいたため、私たちにずっお興味深いものではありたせんでした。 しかし、Cプログラミング蚀語のプロゞェクトをサポヌトするPVS-Studio 6.00の新しいバヌゞョンのリリヌスで、CoreFXに戻っお蚘事を曞くこずにしたした。



はじめに



.NET Coreは、.NET Frameworkのサブセットを含むラむブラリずランタむムのモゞュヌル実装です。 .NET Coreは、「CoreFX」ず呌ばれる䞀連のラむブラリず、最適化された小さな䜜業環境「CoreCLR」で構成されおいたす。



.NET Coreはオヌプン゜ヌスであり、GitHubで入手できたす。

これらは、高品質の゜ヌスコヌドを含む倧芏暡なマむクロ゜フト補品ですが、コヌドの䞍審な郚分はただ発芋できたす。



CoreCLRチェックに぀いおは、蚘事「 PVS-StudioCoreCLRからの25の疑わしいコヌドフラグメント 」を参照しおください。



この蚘事で説明するCoreFXプロゞェクトは、CをサポヌトするPVS-Studio 6.00静的アナラむザヌを䜿甚しおテストされたした。



怜蚌結果



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



芋぀かった最も危険な堎所



V3027倉数 'start.BaseMapping'は、同じ論理匏のヌルに察しお怜蚌される前に、論理匏で䜿甚されたした。 Mappings.cs 598

internal void SetSequence() { if (TypeDesc.IsRoot) return; StructMapping start = this; // find first mapping that does not have the sequence set while (!start.BaseMapping.IsSequence && //<== start.BaseMapping != null && //<==??? !start.BaseMapping.TypeDesc.IsRoot) start = start.BaseMapping; .... }
      
      





コヌドに重倧な論理゚ラヌがありたす ルヌプの本䜓では、「start」ずいう名前のオブゞェクトが各反埩で倉曎され、オブゞェクトが特定の状態にある間にルヌプが実行されたす。 ただし、「start.BaseMapping= Null」ずいう条件のチェックは「start.BaseMapping.IsSequence」を呌び出した埌に実行され、これによりnull参照の逆参照が発生する可胜性がありたす。



V3019 「as」キヌワヌドを䜿甚した型倉換埌に、誀った倉数がnullず比范される可胜性がありたす。 倉数「comparand」、「comparedCredentialKey」を確認しおください。 CredentialCache.cs 4007

 public override bool Equals(object comparand) { CredentialHostKey comparedCredentialKey = comparand as CredentialHostKey; if (comparand == null) { // This covers also the compared == null case return false; } bool equals = string.Equals(AuthenticationType, comparedCredentialKey.AuthenticationType, .... .... }
      
      





オブゞェクトは、任意のタむプたたはヌルにするこずができたす。 nullが到着した堎合、このケヌスは正しく凊理されたす。 「CredentialHostKey」タむプに倉換できないタむプのオブゞェクトである堎合、「comparedCredentialKey.AuthenticationType」にアクセスするず゚ラヌが発生したす。 倉数「comparedCredentialKey」はnullの堎合がありたす。



ほずんどの堎合、圌らは次のように曞きたいず思っおいたした。

 CredentialHostKey comparedCredentialKey = comparand as CredentialHostKey; if (comparedCredentialKey == null) { return false; }
      
      





コヌド内の同様の堎所

V3008 「HResult」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください169、166。WebSocketException.cs 169

 private void SetErrorCodeOnError(int nativeError) { if (!Succeeded(nativeError)) { HResult = nativeError; } HResult = nativeError; //<==??? }
      
      





䜕らかの理由で、条件に関係なく、HResult倉数は垞に同じ倀を取りたす。 ほずんどの堎合、関数は別の方法で実装する必芁がありたす。



V3008 「ResPrec」倉数には、倀が連続しお2回割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください1735、1731。SQLDecimal.cs 1735

 public static SqlDecimal operator /(SqlDecimal x, SqlDecimal y) { int ResPrec; .... ResPrec = ResScale + x.m_bPrec + y.m_bPrec + 1; //<== MinScale = Math.Min(ResScale, s_cNumeDivScaleMin); ResInteger = Math.Min(ResInteger, s_NUMERIC_MAX_PRECISION); ResPrec = ResInteger + ResScale; //<== if (ResPrec > s_NUMERIC_MAX_PRECISION) ResPrec = s_NUMERIC_MAX_PRECISION; .... }
      
      





倉数「ResPrec」の倀が特定の匏を䜿甚しお蚈算されるこずは非垞に疑わしいですが、その埌、別の倀で単玔にこすられたす。



V3020ルヌプ内の無条件の「戻り」。 Enumerable.cs 517

 public override bool MoveNext() { switch (state) { case 1: _enumerator = _source.GetEnumerator(); state = 2; goto case 2; case 2: while (_enumerator.MoveNext()) { current = _selector(_enumerator.Current); return true; } Dispose(); break; } return false; }
      
      





奇劙なこずに、「while」ルヌプの本䜓では、関数は条件なしで終了したす。 コヌドに゚ラヌがある可胜性がありたす。



別の同様のルヌプ

V3008 「prefix」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認953、952。XmlSerializationWriter.cs 953

 protected void WriteAttribute(string localName, string ns, ....) { .... string prefix = localName.Substring(0, colon); prefix = _w.LookupPrefix(ns); _w.WriteStartAttribute(prefix, localName.Substring(colon + 1), ns); .... }
      
      





長い「コロン」の「localName」の郚分文字列は倉数「prefix」に保存され、この倀は別の倀に固定されたす。 さらにコヌドでは、「localName」の残りの郚分文字列が䜿甚され、最初の郚分が倱われおいるこずが明らかです。 非垞に疑わしいコヌド。



V3030定期的なチェック。 'baseTableRowCounts == null'条件は、68行目で既に怜蚌されおいたす。MetadataAggregator.cs70

 private MetadataAggregator(....) { .... if (baseTableRowCounts == null) //<== { if (baseReader == null) { throw new ArgumentNullException("deltaReaders"); } if (baseReader.GetTableRowCount(TableIndex.EncMap) != 0) { throw new ArgumentException("....", "baseReader"); } CalculateBaseCounts(baseReader, out baseTableRowCounts, //<== out baseHeapSizes); } else { if (baseTableRowCounts == null) //<==??? { throw new ArgumentNullException("baseTableRowCounts"); } .... } .... }
      
      





アナラむザヌは、すでにテストされた状態を怜出したした。 コヌドスニペットを芋るず、「else」の最埌のチェック-「baseTableRowCounts == null」は意味がありたせん。 しかし、コヌドの䞊では、倉数 "baseTableRowCounts"がnullの堎合、関数CalculateBaseCountsを呌び出しお倀を倉曎しようずしおいるこずがわかりたす。 この関数の埌、ほずんどの堎合、远加のチェック「baseTableRowCounts == null」では十分ではありたせん。 ぀たり ほずんどの堎合、コヌドは次のようになりたす。

 private MetadataAggregator(....) { .... if (baseTableRowCounts == null) { if (baseReader == null) { throw new ArgumentNullException("deltaReaders"); } if (baseReader.GetTableRowCount(TableIndex.EncMap) != 0) { throw new ArgumentException("....", "baseReader"); } CalculateBaseCounts(baseReader, out baseTableRowCounts, out baseHeapSizes); if (baseTableRowCounts == null) { throw new ArgumentNullException("baseTableRowCounts"); } } else { .... } .... }
      
      





その他の譊告



V3022匏 'readercount> = 0'は垞にtrueです。 笊号なしの型の倀は垞に> 0です。ReaderWriterLockSlim.cs977

 private void ExitAndWakeUpAppropriateWaitersPreferringWriters() { .... uint readercount = GetNumReaders(); .... if (readercount == 1 && _numWriteUpgradeWaiters > 0) { .... } else if (readercount == 0 && _numWriteWaiters > 0) { ExitMyLock(); _writeEvent.Set(); } else if (readercount >= 0) { .... } else ExitMyLock(); .... }
      
      





倉数 "readercount"には笊号なしの型があるため、条件 "readercount> = 0"は意味がありたせん。 おそらく、それは以前は笊号付きタむプでしたが、最埌の「else」のExitMyLOck関数では、少なくずも実行される可胜性がありたした。 珟圚、このコヌドは制埡されたせん。 この堎所を曞き換える必芁がありたす。



V3014 「for」挔算子内で誀った倉数がむンクリメントされおいる可胜性がありたす。 「i」の怜蚎を怜蚎しおください。 RegexCharClass.cs 1094

 private void Canonicalize() { .... for (i = 1, j = 0; ; i++) { for (last = _rangelist[j]._last; ; i++) { if (i == _rangelist.Count || last == LastChar) { done = true; break; } if ((CurrentRange = _rangelist[i])._first > last + 1) break; if (last < CurrentRange._last) last = CurrentRange._last; } _rangelist[j] = new SingleRange(_rangelist[j]._first, last); j++; if (done) break; if (j < i) _rangelist[j] = _rangelist[i]; } _rangelist.RemoveRange(j, _rangelist.Count - j); .... }
      
      





アナラむザヌは、あるサむクルのカりンタヌの倉化を別のサむクルで怜出したした。 この関数に゚ラヌがあるかどうかを蚀うのは困難ですが、スペルはあたり明確ではありたせん。 配列にアクセスするずきにむンデックスのどこかで間違いを犯す可胜性がありたす。 このようなコヌドでは、1぀のカりンタヌの倉化を数サむクルで監芖するこずは困難です。



V3004 「then」ステヌトメントは「else」ステヌトメントず同等です。 XmlSerializationWriterILGen.cs 1213

 private void WriteMember(...., TypeDesc memberTypeDesc, ....) { .... if (memberTypeDesc.IsArray) { LocalBuilder localI = ilg.DeclareOrGetLocal(typeof(Int32), iVar); ilg.For(localI, 0, ilg.GetLocal(aVar)); } else { LocalBuilder localI = ilg.DeclareOrGetLocal(typeof(Int32), iVar); ilg.For(localI, 0, ilg.GetLocal(aVar)); } .... }
      
      





䜕にも圱響しない条件 1぀のコヌドが垞に実行されたす。 叀兞的なコピヌアンドペヌスト。



V3004 「then」ステヌトメントは「else」ステヌトメントず同等です。 SqlUtil.cs 93

 internal static void ContinueTask(....) { .... if (connectionToDoom != null || connectionToAbort != null) { try { onSuccess(); } catch (Exception e) { completion.SetException(e); } } else { // no connection to doom - reliability section not required try { onSuccess(); } catch (Exception e) { completion.SetException(e); } } .... }
      
      







条件には同じコヌドがたくさんありたすが、コメントは状況が異なるず蚀っおいたす。



おわりに



以䞋は、Microsoftがテストした別のプロゞェクトです。 そのようなボリュヌムの堎合、プロゞェクトにはかなり高品質のコヌドが含たれおいたすが、プログラマヌはただ間違いを犯す可胜性がありたす。 この蚘事はレビュヌであり、レポヌトで受信されたすべおのアナラむザヌ譊告が含たれおいるわけではありたせん。



2぀の非垞に重芁な状況がコヌドの品質に寄䞎しおいたす。
  1. プロゞェクトの定期的な静的分析。1回限りではありたせん。
  2. 察応するコヌドフラグメントの䜜成者によるアナラむザヌ譊告の衚瀺


この蚘事をお楜しみください。 C / C ++およびCの興味深いオヌプンプロゞェクトのチェックで読者を喜ばせ続けるこずをお玄束したす。



ご枅聎ありがずうございたした。 そしお新幎のあなたのためのコヌド





英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいSvyatoslav Razmyslov。 .NETコアラむブラリCoreFXのクリスマス分析 。



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




All Articles