悪は比范機胜に生きる

比范関数の悪






読者は、「The Last Lineの効果」ずいうタむトルの私の蚘事を芚えおいるかもしれたせん。 これは、私が気付いたパタヌンを指したす。ほずんどの堎合、同じタむプのテキストブロックの最埌の行に間違いがありたす。 ここで、新しい興味深い芳察に぀いおお話したいず思いたす。 プログラマヌは、2぀のオブゞェクトを比范する機胜に間違いを犯しがちです。 そのような声明は信じがたいように思えたすが、読者に衝撃を䞎える゚ラヌの膚倧な数の䟋を瀺したす。 新しい研究を読んでください、それは面癜くお怖いでしょう。



発行



プログラマは、2぀のオブゞェクトを比范するように蚭蚈された非垞に単玔な関数でミスを犯すこずが非垞に倚いず䞻匵したす。 このステヌトメントは、C、C ++、およびCで開かれた倚数のプロゞェクトをチェックするチヌムの経隓に基づいおいたす。



問題の関数は、 IsEqual 、 Equals 、 Compare 、 AreEqualなどず呌ばれたす。 たたは== ,! =などのオヌバヌロヌド挔算子。



蚘事を曞いおいるずきに、比范関数に関連する゚ラヌに遭遇するこずがよくありたす。 この問題をより詳现に調査するこずにし、発芋した゚ラヌのデヌタベヌスを調査したした。 これを行うために、デヌタベヌスでCmp 、 Equal 、 Compareなどの単語を含む名前の関数が怜玢されたした。 結果は非垞に印象的で衝撃的でした。



䞀般に、このストヌリヌは「The Last Lineの効果」ずいう蚘事を曞くこずに䌌おいたす。 同様に、私は異垞に気づき、それをより詳现に研究するこずにしたした。 残念ながら、蚀及された蚘事ずは異なり、私はどのような数字や統蚈を持っおくるべきかわかりたせん。 おそらく、どのように、䜕を蚈算できるか-埌で考えたす。 今、私は盎芳に導かれ、私の気持ちを共有するこずができるだけです。 圌らは、比范関数に倚くの゚ラヌがあるず蚀いたす。そしお、私が倚数の印象的な䟋を瀺すずき、あなたは同じ感芚を持っおいるず確信しおいたす。



心理孊



最埌のラむン゚フェクトに戻りたしょう。 ちなみに、もしあなたがそれを読んでいないのなら、䞀時停止しお圌女ず知り合うこずをお勧めしたす。 このトピックのより詳现な分析がありたす「 最埌の行の効果の説明 」。



䞀般に、最埌の行の゚ラヌの原因は、開発者が珟圚のフラグメントの完了に集䞭するのではなく、次の行/タスクにすでに心を動かしおいるずいう事実によるず結論付けるこずができたす。 その結果、同じタむプのテキストブロックを最埌に曞いた最埌に、圌は以前のものよりもタむプミスをする可胜性が高くなりたす。



比范関数を曞くずき、開発者はそれが些现であるず考えお、それに焊点を合わせないこずが倚いず思いたす。 蚀い換えれば、圌はそれを考えずに自動的にコヌドを曞きたす。 それ以倖の堎合、同様の゚ラヌを䜜成する方法は明確ではありたせん。



bool IsLuidsEqual(LUID luid1, LUID luid2) { return (luid1.LowPart == luid2.LowPart) && (luid2.HighPart == luid2.HighPart); }
      
      





PVS-Studioアナラむザヌは、 RunAsAdmin Explorer Shim C ++プロゞェクトコヌドでこの゚ラヌを怜出したした V501 '=='挔算子の巊右に同じ副次匏がありたすluid2.HighPart == luid2.HighPart RAACommon raacommonfuncs。 cpp 1511



タむプミス。 2行目は、 luid1.HighPart == luid2.HighPartになっおいるはずです。



同意しお、コヌドは簡単です。 どうやら、コヌドの単玔さはすべおを台無しにしたす。 プログラマヌは、このような関数暙準を䜜成しお、面癜くないタスクをすぐに認識したす。 圌は即座に頭の䞭で関数の曞き方を決定し、コヌドを実装するだけです。 これはルヌチンですが、残念ながら、より重芁で耇雑で興味深いコヌドの䜜成に進むために必芁なプロセスです。 開発者の考えはすでにそこにあり、その結果、圌は間違いを犯したす。



さらに、プログラマヌがこのような機胜の単䜓テストを䜜成するこずはほずんどありたせん。 繰り返したすが、これらの関数の芋かけ䞊の単玔さは倱敗したす。 これらの関数は非垞に単玔で同じタむプであるため、それらをテストするのは冗長なようです。 人は自分の生掻の䞭で䜕癟ものそのような機胜を曞いたが、圌は本圓に次の機胜で間違いを犯すこずができるのか たぶんそうです。



同時に、この蚘事はプログラミングを孊ぶ孊生のコヌドに関するものではないずいう事実に泚意を喚起したいず思いたす。 GCC、Qt、GDB、LibreOffice、Unreal Engine 4、CryEngine V、Chromium、MongoDB、Oracle VM Virtual Box、FreeBSD、WinMerge、CoreCLR、MySQL、Mono、CoreFX、Roslyn、MSBuildなどのプロゞェクトのコヌドの゚ラヌに぀いお話しおいるなど すべおが非垞に深刻です。



私はあなたが寝るのが怖いほど倚くの倚様な䟋を瀺したす。



比范関数の誀ったパタヌン



比范関数のすべおの゚ラヌは、いく぀かのパタヌンに分割されたす。 この蚘事では、C、C ++、C蚀語のプロゞェクトの゚ラヌを扱いたすが、倚くのパタヌンは異なる蚀語で同じであるため、これらの蚀語を䜕らかの圢で分ける意味はありたせん。



パタヌンA <B、B> A



倚くの堎合、比范関数では次のチェックを実行する必芁がありたす。





プログラマヌは、同じ<operatorを䜿甚しお倉数名を亀換する方がより矎しいず感じる堎合がありたす。





ただし、䞍泚意のため、次のチェックが取埗されたす。





実際、同じ比范がここで2回実行されたす。 おそらくあなたは議論されおいるこずを理解しおいなかったかもしれたせんが、実際の䟋に移り、すべおが明らかになりたす。



 string _server; .... bool operator<( const ServerAndQuery& other ) const { if ( ! _orderObject.isEmpty() ) return _orderObject.woCompare( other._orderObject ) < 0; if ( _server < other._server ) return true; if ( other._server > _server ) return false; return _extra.woCompare( other._extra ) < 0; }
      
      





PVS-Studioアナラむザヌは、 MongoDB C ++プロゞェクトコヌドでこの゚ラヌを怜出したした V581互いに䞊んでいる「if」挔算子の条件匏は同䞀です。 チェック行44、46。parallel.h 46



この状態は次のずおりです。



 if ( other._server > _server )
      
      





䞊蚘の2行ずたったく同じチェックが実行されたため、垞にfalseになりたす。 コヌドの正しいバヌゞョン



 if ( _server < other._server ) return true; if ( other._server < _server ) return false;
      
      





この゚ラヌはChromiumプロゞェクトコヌドC ++にもありたす。



 enum ContentSettingsType; struct EntryMapKey { ContentSettingsType content_type; ... }; bool OriginIdentifierValueMap::EntryMapKey::operator<( const OriginIdentifierValueMap::EntryMapKey& other) const { if (content_type < other.content_type) return true; else if (other.content_type > content_type) return false; return (resource_identifier < other.resource_identifier); }
      
      





PVS-Studio è­Šå‘Š  V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 61、63行目を確認したす。browser content_settings_origin_identifier_value_map.cc 61



C ++で䟋を瀺したしたが、今床はCの番です IronPythonおよびIronRuby Cコヌドで次の゚ラヌが芋぀かりたした。



 public static int Compare(SourceLocation left, SourceLocation right) { if (left < right) return -1; if (right > left) return 1; return 0; }
      
      





PVS-Studio譊告C V3021同䞀の条件匏を持぀2぀の「if」ステヌトメントがありたす。 最初の「if」ステヌトメントにはメ゜ッドの戻り倀が含たれたす。 これは、2番目の「if」ステヌトメントが無意味であるこずを意味したす。 SourceLocation.cs 156



ここでの説明は䞍芁だず思いたす。



ご泚意 Cの゚ラヌの䟋は1぀しかありたせんが、C ++の゚ラヌは2぀ありたした。 ただし、Cの方がずっず安党であるずいう結論に急ぐこずはお勧めしたせん。 実際、PVS-Studioアナラむザヌは比范的最近Cコヌドを分析するこずを孊び、CおよびC ++よりもCで蚘述されたプロゞェクトをはるかに少なくチェックしただけです。



パタヌンクラスメンバはそれ自身ず比范したす



比范関数は、倚くの堎合、フィヌルド構造/クラスの順次比范で構成されたす。 このようなコヌドは、クラスのメンバヌが自分自身ず比范し始めるずきに゚ラヌになりやすいです。 この堎合、゚ラヌの2぀のサブタむプを区別できたす。



最初のケヌスでは、オブゞェクトの名前を瀺すのを忘れお、次のように曞きたす。



 return m_x == foo.m_x && m_y == m_y && // <= m_z == foo.m_z;
      
      





2番目の堎合、同じオブゞェクト名を曞き蟌みたす。

 return zzz.m_x == foo.m_x && zzz.m_y == zzz.m_y && // <= zzz.m_z == foo.m_z;
      
      





実際にこのパタヌンの゚ラヌに慣れおみたしょう。 ちなみに、誀った比范は、同じテキストブロックの最埌によく芋られるこずに泚意しおください。「最埌の行の効果」です。



Unreal Engine 4 C ++プロゞェクトのコヌドで゚ラヌが芋぀かりたした



 bool Compare(const FPooledRenderTargetDesc& rhs, bool bExact) const { .... return Extent == rhs.Extent && Depth == rhs.Depth && bIsArray == rhs.bIsArray && ArraySize == rhs.ArraySize && NumMips == rhs.NumMips && NumSamples == rhs.NumSamples && Format == rhs.Format && LhsFlags == RhsFlags && TargetableFlags == rhs.TargetableFlags && bForceSeparateTargetAndShaderResource == rhs.bForceSeparateTargetAndShaderResource && ClearValue == rhs.ClearValue && AutoWritable == AutoWritable; // <= }
      
      





è­Šå‘ŠPVS-Studio V501 「==」挔算子の巊右に同じサブ匏がありたすAutoWritable == AutoWritable rendererinterface.h 180



SambaプロゞェクトコヌドC



 static int compare_procids(const void *p1, const void *p2) { const struct server_id *i1 = (struct server_id *)p1; const struct server_id *i2 = (struct server_id *)p2; if (i1->pid < i2->pid) return -1; if (i2->pid > i2->pid) return 1; return 0; }
      
      





è­Šå‘ŠPVS-Studio V501 「>」挔算子の巊右に同じ副次匏がありたすi2-> pid> i2-> pid brlock.c 1901



MongoDBプロゞェクトコヌドC ++



 bool operator==(const MemberCfg& r) const { .... return _id==r._id && votes == r.votes && h == rh && priority == r.priority && arbiterOnly == r.arbiterOnly && slaveDelay == r.slaveDelay && hidden == r.hidden && buildIndexes == buildIndexes; // <= }
      
      





è­Šå‘ŠPVS-Studio V501 '=='挔算子の巊右に同じ副次匏がありたすbuildIndexes == buildIndexes rs_config.h 101



Geant4゜フトりェアプロゞェクトコヌド C ++



 inline G4bool G4FermiIntegerPartition:: operator==(const G4FermiIntegerPartition& right) { return (total == right.total && enableNull == enableNull && // <= partition == right.partition); }
      
      





PVS-Studioの譊告  V501 「==」挔算子の巊右に同じ副次匏がありたす。enableNull == enableNull G4hadronic_deex_fermi_breakup g4fermiintegerpartition.icc 58



LibreOfficeプロゞェクトコヌドC ++



 class SvgGradientEntry { .... bool operator==(const SvgGradientEntry& rCompare) const { return (getOffset() == rCompare.getOffset() && getColor() == getColor() // <= && getOpacity() == getOpacity()); // <= } .... }
      
      





PVS-Studio譊告 V501 「==」挔算子の巊右に同じ副次匏がありたすgetColor== getColorsvggradientprimitive2d.hxx 61



ChromiumプロゞェクトコヌドC ++



 bool FileIOTest::MatchesResult(const TestStep& a, const TestStep& b) { .... return (a.data_size == a.data_size && // <= std::equal(a.data, a.data + a.data_size, b.data)); }
      
      





PVS-Studio譊告 V501 「==」挔算子の巊右に同䞀のサブ匏がありたすa.data_size == a.data_size cdm_file_io_test.cc 367



FreeCADプロゞェクトコヌドC ++



 bool FaceTypedBSpline::isEqual(const TopoDS_Face &faceOne, const TopoDS_Face &faceTwo) const { .... if (surfaceOne->IsURational() != surfaceTwo->IsURational()) return false; if (surfaceTwo->IsVRational() != // <= surfaceTwo->IsVRational()) // <= return false; if (surfaceOne->IsUPeriodic() != surfaceTwo->IsUPeriodic()) return false; if (surfaceOne->IsVPeriodic() != surfaceTwo->IsVPeriodic()) return false; if (surfaceOne->IsUClosed() != surfaceTwo->IsUClosed()) return false; if (surfaceOne->IsVClosed() != surfaceTwo->IsVClosed()) return false; if (surfaceOne->UDegree() != surfaceTwo->UDegree()) return false; if (surfaceOne->VDegree() != surfaceTwo->VDegree()) return false; .... }
      
      





PVS-Studioの譊告  V501 「=」挔算子の巊偎ず右偎には、同䞀の郚分匏「surfaceTwo-> IsVRational」がありたす。 modelrefine.cpp 780



深刻な゚ンゞンプロゞェクトコヌドC ++



 class CTexParams { public: inline BOOL IsEqual( CTexParams tp) { return tp_iFilter == tp.tp_iFilter && tp_iAnisotropy == tp_iAnisotropy && // <= tp_eWrapU == tp.tp_eWrapU && tp_eWrapV == tp.tp_eWrapV; }; .... };
      
      





è­Šå‘ŠPVS-Studio V501 '=='挔算子の巊右に同じ副次匏がありたすtp_iAnisotropy == tp_iAnisotropy gfx_wrapper.h 180



QtプロゞェクトコヌドC ++



 inline bool qCompare(QImage const &t1, QImage const &t2, ....) { .... if (t1.width() != t2.width() || t2.height() != t2.height()) { .... }
      
      





PVS-Studio譊告 V501 '='挔算子の巊右に同じ副次匏がありたすt2.height= T2.heightqtest_gui.h 101



FreeBSDプロゞェクトコヌドC



 static int compare_sh(const void *_a, const void *_b) { const struct ipfw_sopt_handler *a, *b; a = (const struct ipfw_sopt_handler *)_a; b = (const struct ipfw_sopt_handler *)_b; .... if ((uintptr_t)a->handler < (uintptr_t)b->handler) return (-1); else if ((uintptr_t)b->handler > (uintptr_t)b->handler) // <= return (1); return (0); }
      
      





PVS-Studioの譊告  V501 「>」挔算子の巊右には、同䞀の副次匏「uintptr_tb->ハンドラヌ」がありたす。 ip_fw_sockopt.c 2893



モノプロゞェクトコヌドC



 static bool AreEqual (VisualStyleElement value1, VisualStyleElement value2) { return value1.ClassName == value1.ClassName && // <= value1.Part == value2.Part && value1.State == value2.State; }
      
      





PVS-Studio譊告 V3001 「==」挔算子の巊偎ず右偎に同䞀のサブ匏「value1.ClassName」がありたす。 ThemeVisualStyles.cs 2141



モノプロゞェクトコヌドC



 public int ExactInference (TypeSpec u, TypeSpec v) { .... var ac_u = (ArrayContainer) u; var ac_v = (ArrayContainer) v; .... var ga_u = u.TypeArguments; var ga_v = v.TypeArguments; .... if (u.TypeArguments.Length != u.TypeArguments.Length) // <= return 0; .... }
      
      





PVS-Studio譊告 V3001 「=」挔算子の巊偎ず右偎には、同䞀のサブ匏「u.TypeArguments.Length」がありたす。 generic.cs 3135



MonoDevelopプロゞェクトコヌドC



 Accessibility DeclaredAccessibility { get; } bool IsStatic { get; } private bool MembersMatch(ISymbol member1, ISymbol member2) { if (member1.Kind != member2.Kind) { return false; } if (member1.DeclaredAccessibility != // <=1 member1.DeclaredAccessibility // <=1 || member1.IsStatic != member1.IsStatic) // <=2 { return false; } if (member1.ExplicitInterfaceImplementations().Any() || member2.ExplicitInterfaceImplementations().Any()) { return false; } return SignatureComparer .HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors( member1, member2, this.IsCaseSensitive); }
      
      





PVS-Studio譊告 V3001 「=」挔算子の巊偎ず右偎には、同䞀の郚分匏「member1.IsStatic」がありたす。 CSharpBinding AbstractImplementInterfaceService.CodeAction.cs 545



HaikuプロゞェクトコヌドC ++



 int __CORTEX_NAMESPACE__ compareTypeAndID(....) { int retValue = 0; .... if (lJack && rJack) { if (lJack->m_jackType < lJack->m_jackType) // <= { return -1; } if (lJack->m_jackType == lJack->m_jackType) // <= { if (lJack->m_index < rJack->m_index) { return -1; } else { return 1; } } else if (lJack->m_jackType > rJack->m_jackType) { retValue = 1; } } return retValue; }
      
      





PVS-Studio譊告 V501 「<」挔算子の巊右に同じ副次匏がありたすlJack-> m_jackType <lJack-> m_jackType MediaJack.cpp 783



すぐ䞋に、たったく同じ゚ラヌがもう1぀ありたす。 私が理解するように、どちらの堎合も、圌らはlJackをrJackに眮き換えるのを忘れおいたした 。



CryEngine VプロゞェクトコヌドC ++



 bool CompareRotation(const Quat& q1, const Quat& q2, float epsilon) { return (fabs_tpl(q1.vx - q2.vx) <= epsilon) && (fabs_tpl(q1.vy - q2.vy) <= epsilon) && (fabs_tpl(q2.vz - q2.vz) <= epsilon) // <= && (fabs_tpl(q1.w - q2.w) <= epsilon); }
      
      





è­Šå‘ŠPVS-Studio V501 「-」挔算子の巊右に同じ副次匏がありたすq2.vz-q2.vz entitynode.cpp 93



パタヌン構造/クラスサむズの代わりにポむンタヌサむズを蚈算する



このタむプの゚ラヌは、CおよびC ++プログラムで発生し、 sizeof挔算子の誀った䜿甚に関連しおいたす。 ゚ラヌはオブゞェクトのサむズではなく、ポむンタヌのサむズを蚈算しおいたす。 䟋



 T *a = foo1(); T *b = foo2(); x = memcmp(a, b, sizeof(a));
      
      





構造䜓Tのサむズの代わりに、ポむンタヌのサむズが蚈算されたす。 ポむンタヌのサむズは䜿甚されるデヌタモデルによっお異なりたすが、通垞は4たたは8バむトです。 その結果、構造が占有するよりも倚いたたは少ないバむトのメモリが比范されたす。



正しいコヌドは次のずおりです。



 x = memcmp(a, b, sizeof(T));
      
      





たたは



 x = memcmp(a, b, sizeof(*a));
      
      





さあ、緎習したしょう。 CryEngine V C ++プロゞェクトのコヌドでこのような゚ラヌがどのように芋えるかを次に瀺したす。



 bool operator==(const SComputePipelineStateDescription& other) const { return 0 == memcmp(this, &other, sizeof(this)); }
      
      





PVS-Studio譊告 V579 memcmp関数は、ポむンタヌずそのサむズを匕数ずしお受け取りたす。 間違いかもしれたせん。 3番目の匕数を調べたす。 graphicspipelinestateset.h 58



アンリアル゚ンゞン4 C ++プロゞェクトコヌド



 bool FRecastQueryFilter::IsEqual( const INavigationQueryFilterInterface* Other) const { // @NOTE: not type safe, should be changed when // another filter type is introduced return FMemory::Memcmp(this, Other, sizeof(this)) == 0; }
      
      





PVS-Studio譊告 V579 Memcmp関数は、ポむンタヌずそのサむズを匕数ずしお受け取りたす。 間違いかもしれたせん。 3番目の匕数を調べたす。 pimplrecastnavmesh.cpp 172



パタヌンCmpA、Aの圢匏の繰り返し匕数



倚くの堎合、比范関数は他の比范関数を呌び出したす。 この堎合、考えられる゚ラヌの1぀は、同じオブゞェクトぞのリンク/ポむンタヌが2回送信されるこずです。 䟋



 x = memcmp(A, A, sizeof(T));
      
      





ここでは、オブゞェクトAがそれ自䜓ず比范されたすが、これはもちろん意味がありたせん。



GDB Cデバッガヌコヌドで芋぀かった゚ラヌから始めたす。



 static int psymbol_compare (const void *addr1, const void *addr2, int length) { struct partial_symbol *sym1 = (struct partial_symbol *) addr1; struct partial_symbol *sym2 = (struct partial_symbol *) addr2; return (memcmp (&sym1->ginfo.value, &sym1->ginfo.value, // <= sizeof (sym1->ginfo.value)) == 0 && sym1->ginfo.language == sym2->ginfo.language && PSYMBOL_DOMAIN (sym1) == PSYMBOL_DOMAIN (sym2) && PSYMBOL_CLASS (sym1) == PSYMBOL_CLASS (sym2) && sym1->ginfo.name == sym2->ginfo.name); }
      
      





è­Šå‘ŠPVS-Studio V549 'memcmp'関数の最初の匕数は2番目の匕数ず同じです。 psymtab.c 1580



CryEngineSDKプロゞェクトコヌドC ++



 inline bool operator != (const SEfResTexture &m) const { if (stricmp(m_Name.c_str(), m_Name.c_str()) != 0 || // <= m_TexFlags != m.m_TexFlags || m_bUTile != m.m_bUTile || m_bVTile != m.m_bVTile || m_Filter != m.m_Filter || m_Ext != m.m_Ext || m_Sampler != m.m_Sampler) return true; return false; }
      
      





è­Šå‘ŠPVS-Studio V549 'stricmp'関数の最初の匕数は2番目の匕数ず同じです。 ishader.h 2089



PascalABC.NETプロゞェクトコヌドC



 private List<string> enum_consts = new List<string>(); public override bool IsEqual(SymScope ts) { EnumScope es = ts as EnumScope; if (es == null) return false; if (enum_consts.Count != es.enum_consts.Count) return false; for (int i = 0; i < es.enum_consts.Count; i++) if (string.Compare(enum_consts[i], this.enum_consts[i], true) != 0) return false; return true; }
      
      





PVS-Studio譊告 V3038 'enum_consts [i]'匕数が 'Compare'メ゜ッドに数回枡されたした。 代わりに他の匕数を枡す必芁がありたす。 CodeCompletion SymTable.cs 2206



ここで少し説明したす。 Compare関数の実際の匕数の゚ラヌ



 string.Compare(enum_consts[i], this.enum_consts[i], true)
      
      





実際には、 enum_consts [i]ずthis.enum_consts [i]はたったく同じです。 私が理解しおいるように、正しい呌び出しは次のようになりたす。



 string.Compare(es.enum_consts[i], this.enum_consts[i], true)
      
      





たたは



 string.Compare(enum_consts[i], es.enum_consts[i], true)
      
      





パタヌンチェックの繰り返しA == B && A == B



䞀般的なプログラミング゚ラヌは、同じチェックが2回実行される堎合です。 䟋



 return A == B && C == D && // <= C == D && // <= E == F;
      
      





このような堎合、2぀のオプションが可胜です。 1぀目は無害です。1぀の比范は䞍芁であり、単玔に削陀できたす。 2番目はさらに悪いこずです。圌らは他の倉数をチェックしたかったのですが、封印されおいたした。



いずれにせよ、そのようなコヌドには现心の泚意が必芁です。 もっずひどく怖がらせお、そのような゚ラヌはGCC Cコンパむラコヌドでも芋぀けられるこずを瀺したしょう。



 static bool dw_val_equal_p (dw_val_node *a, dw_val_node *b) { .... case dw_val_class_vms_delta: return (!strcmp (a->v.val_vms_delta.lbl1, b->v.val_vms_delta.lbl1) && !strcmp (a->v.val_vms_delta.lbl1, b->v.val_vms_delta.lbl1)); .... }
      
      





PVS-Studioの譊告  V501 「&&」挔算子の巊右に同䞀の副次匏「Strcmpa-> v.val_vms_delta.lbl1、b-> v.val_vms_delta.lbl1」がありたす。 dwarf2out.c 1428



strcmp関数は、同じ匕数のセットで2回呌び出されたす。



アンリアル゚ンゞン4 C ++プロゞェクトコヌド



 FORCEINLINE bool operator==(const FShapedGlyphEntryKey& Other) const { return FontFace == Other.FontFace && GlyphIndex == Other.GlyphIndex // <= && FontSize == Other.FontSize && FontScale == Other.FontScale && GlyphIndex == Other.GlyphIndex; // <= }
      
      





PVS-Studio譊告 V501 「&&」挔算子の巊偎ず右偎には、同䞀の副次匏「GlyphIndex == Other.GlyphIndex」がありたす。 fontcache.h 139



深刻な゚ンゞンプロゞェクトコヌドC ++



 inline BOOL CValuesForPrimitive::operator==(....) { return ( (....) && (vfp_ptPrimitiveType == vfpToCompare.vfp_ptPrimitiveType) && .... (vfp_ptPrimitiveType == vfpToCompare.vfp_ptPrimitiveType) && .... );
      
      





PVS-Studioの譊告  V501 「&&」挔算子の巊偎ず右偎に同䞀の副次匏「vfp_ptPrimitiveType == vfpToCompare.vfp_ptPrimitiveType」がありたす。 worldeditor.h 580



Oracle VM Virtual Box Project CodeC ++



 typedef struct SCMDIFFSTATE { .... bool fIgnoreTrailingWhite; bool fIgnoreLeadingWhite; .... } SCMDIFFSTATE; /* Pointer to a diff state. */ typedef SCMDIFFSTATE *PSCMDIFFSTATE; /* Compare two lines */ DECLINLINE(bool) scmDiffCompare(PSCMDIFFSTATE pState, ....) { .... if (pState->fIgnoreTrailingWhite // <= || pState->fIgnoreTrailingWhite) // <= return scmDiffCompareSlow(....); .... }
      
      





PVS-Studio譊告 V501 「||」の巊右に同䞀のサブ匏「pState-> fIgnoreTrailingWhite」がありたす。 挔算子。 scmdiff.cpp 238



パタヌンmemcmp関数によっお返される倀の誀甚



memcmp関数は、 int型の次の倀を返したす。





泚意しおください。 「0より倧きい」ずは、任意の数倀を意味し、1ではありたせん。これらの数倀は、2、3、100、256、1024、5555、65536などです。 これは、この結果をchar型たたはshort型の倉数に配眮できないこずを意味したす。 有意なビットがドロップされる可胜性があり、プログラム実行ロゞックに違反したす。



このこずから、結果を定数1たたは-1ず比范できないこずにもなりたす。 蚀い換えれば、そう間違っおいる



 if (memcmp(a, b, sizeof(T)) == 1) if (memcmp(x, y, sizeof(T)) == -1)
      
      





正しい比范



 if (memcmp(a, b, sizeof(T)) > 0) if (memcmp(a, b, sizeof(T)) < 0)
      
      





蚘茉されおいる゚ラヌの陰湿さは、コヌドが長時間正垞に動䜜できるこずです。 新しいプラットフォヌムに切り替えたり、コンパむラのバヌゞョンを倉曎したりするず、゚ラヌが発生し始める可胜性がありたす。



ReactOSプロゞェクトコヌドC ++



 HRESULT WINAPI CRecycleBin::CompareIDs(....) { .... return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb)); }
      
      





PVS-Studio譊告 V642 「memcmp」関数の結果を「unsigned short」型倉数内に保存するこずは䞍適切です。 プログラムのロゞックを壊しお、重芁なビットが倱われる可胜性がありたす。 recyclebin.cpp 542



FirebirdプロゞェクトコヌドC ++



 SSHORT TextType::compare(ULONG len1, const UCHAR* str1, ULONG len2, const UCHAR* str2) { .... SSHORT cmp = memcmp(str1, str2, MIN(len1, len2)); if (cmp == 0) cmp = (len1 < len2 ? -1 : (len1 > len2 ? 1 : 0)); return cmp; }
      
      





PVS-Studio譊告 V642 「memcmp」関数の結果を「short」型倉数内に保存するこずは䞍適切です。 プログラムのロゞックを壊しお、重芁なビットが倱われる可胜性がありたす。 texttype.cpp 338



CoreCLRプロゞェクトコヌド C ++



 bool operator( )(const GUID& _Key1, const GUID& _Key2) const { return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1; }
      
      





PVS-Studioの譊告  V698匏 'memcmp....== -1'は正しくありたせん。 この関数は、倀「-1」だけでなく、負の倀も返すこずができたす。 代わりに 'memcmp....<0'の䜿甚を怜蚎しおください。 sos util.cpp 142



OpenToonzプロゞェクトコヌド C ++



 bool TFilePath::operator<(const TFilePath &fp) const { .... char differ; differ = _wcsicmp(iName.c_str(), jName.c_str()); if (differ != 0) return differ < 0 ? true : false; .... }
      
      





PVS-Studio譊告 V642 「_wcsicmp」関数の結果を「char」型倉数内に保存するこずは䞍適切です。 重芁なビットが倱われ、プログラムのロゞックが砎損する可胜性がありたす。 tfilepath.cpp 328



パタヌン無効なnull参照チェック



この゚ラヌパタヌンは、Cプログラムの兞型的なものです。 比范関数では、 as挔算子を䜿甚しお型キャストが実行される堎合がありたす。 ゚ラヌは、 nullぞの䞍泚意が新しいリンクではなく、元のリンクに察しおチェックされるずいう事実にありたす。 合成䟋を考えおみたしょう



 ChildT foo = obj as ChildT; if (obj == null) return false; if (foo.zzz()) {}
      
      





obj倉数にnull参照が含たれおいる堎合、 obj == nullが状況を保護するかどうかを確認したす。 ただし、 as挔算子がnull参照を返すこずが刀明した堎合、ケヌスに察する保護はありたせん。 正しいコヌドは次のようになりたす。



 ChildT foo = obj as ChildT; if (foo == null) return false; if (foo.zzz()) {}
      
      





原則ずしお、プログラマヌの䞍泚意により゚ラヌが発生したす。 CおよびC ++プログラムでも同様の゚ラヌが発生する可胜性がありたすが、芋぀かった゚ラヌのデヌタベヌスにはそのようなケヌスは芋぀かりたせんでした。



MonoDevelopプロゞェクトコヌドC



 public override bool Equals (object o) { SolutionItemReference sr = o as SolutionItemReference; if (o == null) return false; return (path == sr.path) && (id == sr.id); }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「o」、「sr」を確認したす。 MonoDevelop.Core SolutionItemReference.cs 81



CoreFXプロゞェクトコヌド C



 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, .... .... }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「comparand」、「comparedCredentialKey」を確認しおください。 CredentialCache.cs 4007



ロズリンプロゞェクトコヌドC



 public override bool Equals(object obj) { var d = obj as DiagnosticDescription; if (obj == null) return false; if (!_code.Equals(d._code)) return false; .... }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「obj」、「d」を確認したす。 DiagnosticDescription.cs 201



ロズリンプロゞェクトコヌドC



 protected override bool AreEqual(object other) { var otherResourceString = other as LocalizableResourceString; return other != null && _nameOfLocalizableResource == otherResourceString._nameOfLocalizableResource && _resourceManager == otherResourceString._resourceManager && _resourceSource == otherResourceString._resourceSource && .... }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「other」、「otherResourceString」を確認しおください。 LocalizableResourceString.cs 121



MSBuildプロゞェクトコヌドC



 public override bool Equals(object obj) { AssemblyNameExtension name = obj as AssemblyNameExtension; if (obj == null) // <= { return false; } .... }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「obj」、「name」を確認しおください。 AssemblyRemapping.cs 64



モノプロゞェクトコヌドC



 public override bool Equals (object o) { UrlMembershipCondition umc = (o as UrlMembershipCondition); if (o == null) // <= return false; .... return (String.Compare (u, 0, umc.Url, ....) == 0); // <= }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「o」、「umc」を確認しおください。 UrlMembershipCondition.cs 111



Media Portal 2プロゞェクトコヌドC



 public override bool Equals(object obj) { EpisodeInfo other = obj as EpisodeInfo; if (obj == null) return false; if (TvdbId > 0 && other.TvdbId > 0) return TvdbId == other.TvdbId; .... }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「obj」、「other」を確認しおください。 EpisodeInfo.cs 560



NASA World WindプロゞェクトコヌドC



 public int CompareTo(object obj) { RenderableObject robj = obj as RenderableObject; if(obj == null) // <= return 1; return this.m_renderPriority.CompareTo(robj.RenderPriority); }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数 'obj'、 'robj'を確認しおください。 RenderableObject.cs 199



パタヌンタむプ間違ったサむクル



䞀郚の関数は、アむテムのコレクションを比范したす。 圓然、それらを比范するために、さたざたなサむクルオプションが䜿甚されたす。 比范関数で起こるように、コヌドを䞍泚意に曞くず、ルヌプず䜕かを混同しやすくなりたす。 そのような状況をいく぀か考えおみたしょう。



トランスプロテオヌムパむプラむン C ++プロゞェクトコヌド



 bool Peptide::operator==(Peptide& p) { .... for (i = 0, j = 0; i < this->stripped.length(), j < p.stripped.length(); i++, j++) { .... }
      
      





PVS-Studio譊告 V521 「、」挔算子を䜿甚したこのような匏は危険です。 匏が正しいこずを確認しおください。 tpplibペプチド.cpp 191



条件はコンマ挔算子を䜿甚するこずに泚意しおください。 カンマの巊偎の条件は考慮されないため、コヌドは明らかに正しくありたせん。 ぀たり、巊偎の条件が蚈算されたすが、その結果はたったく䜿甚されたせん。



QtプロゞェクトコヌドC ++



 bool equals( class1* val1, class2* val2 ) const { ... size_t size = val1->size(); ... while ( --size >= 0 ){ if ( !comp(*itr1,*itr2) ) return false; itr1++; itr2++; } ... }
      
      





PVS-Studio 譊告V547匏 ' -size > = 0'は垞にtrueです。笊号なしの型の倀は垞に> = 0です。QtCLucene array.h 154 CLucene



プロゞェクトコヌドC ++



 class Arrays { .... bool equals( class1* val1, class2* val2 ) const{ static _comparator comp; if ( val1 == val2 ) return true; size_t size = val1->size(); if ( size != val2->size() ) return false; _itr1 itr1 = val1->begin(); _itr2 itr2 = val2->begin(); while ( --size >= 0 ){ if ( !comp(*itr1,*itr2) ) return false; itr1++; itr2++; } return true; } .... }
      
      





PVS-Studio 譊告V547匏 ' -size > = 0'は垞にtrueです。笊号なしの型の倀は垞に> = 0です。arrays.h154 Mono



プロゞェクトコヌドC



 public override bool Equals (object obj) { .... for (int i=0; i < list.Count; i++) { bool found = false; for (int j=0; i < ps.list.Count; j++) { // <= if (list [i].Equals (ps.list [j])) { found = true; break; } } if (!found) return false; } return true; }
      
      





PVS-Studio譊告V3015「for」挔算子内で間違った倉数が比范されおいる可胜性がありたす。「I」corlib-net_4_x芋盎しを怜蚎 PermissionSet.cs 607



倉数を䜿甚するネストされたルヌプの状態では、ほずんどのタむプミスをしお、実際にJを代わりに、I



 for (int j=0; j < ps.list.Count; j++)
      
      





パタヌンA = getA、B = GetA



倚くの堎合、比范関数では、このタむプのコヌドを䜜成する必芁がありたす。



 if (GetA().x == GetB().x && GetA().y == GetB().y)
      
      





条件のサむズを小さくするため、たたは最適化するために、䞭間倉数が䜿甚されたす。



 Type A = GetA(); Type B = GetB(); if (Ax == Bx && Ay == By)
      
      





同時に、圌らは䞍泚意で間違いを犯し、同じ倀で䞀時倉数を初期化したす。



 Type A = GetA(); Type B = GetA();
      
      





実際のアプリケヌションのコヌドでこのような゚ラヌがどのように芋えるかを芋おみたしょう。LibreOffice



プロゞェクトコヌドC ++



 bool CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2) { .... bool bNumOffsetEqual = false; ::boost::optional<sal_uInt16> oNumOffset1 = static_cast<const SwFmtPageDesc&>(rItem1).GetNumOffset(); ::boost::optional<sal_uInt16> oNumOffset2 = static_cast<const SwFmtPageDesc&>(rItem1).GetNumOffset(); if (!oNumOffset1 && !oNumOffset2) { bNumOffsetEqual = true; } else if (oNumOffset1 && oNumOffset2) { bNumOffsetEqual = oNumOffset1.get() == oNumOffset2.get(); } else { bNumOffsetEqual = false; } .... }
      
      





PVS-Studioの譊告V656倉数 'oNumOffset1'、 'oNumOffset2'は、同じ関数の呌び出しによっお初期化されたす。おそらく゚ラヌたたは最適化されおいないコヌドです。68、69行を確認したす。findattr.cxx69 Qt



プロゞェクトコヌドC ++



 AtomicComparator::ComparisonResult IntegerComparator::compare(const Item &o1, const AtomicComparator::Operator, const Item &o2) const { const Numeric *const num1 = o1.as<Numeric>(); const Numeric *const num2 = o1.as<Numeric>(); if(num1->isSigned() || num2->isSigned()) .... }
      
      





PVS-Studio譊告V656倉数 'num1'、 'num2'は、同じ関数の呌び出しによっお初期化されたす。おそらく゚ラヌたたは最適化されおいないコヌドです。「o1.as <Numeric>」匏の怜査を怜蚎しおください。行を確認しおください220、221。qatomiccomparators.cpp 221



パタヌン倱敗したコヌドのコピヌ



前述の゚ラヌの倚くは、コピヌペヌストの倱敗の結果ず呌ぶこずができたす。それらはある皮の誀ったパタヌンに該圓し、それらを察応するセクションで説明するこずが論理的であるず刀断したした。ただし、コヌドのコピヌが倱敗したために明らかに発生したいく぀かの゚ラヌがありたすが、分類方法はわかりたせん。そこで、これらの゚ラヌをここでコンパむルしたした。CoreCLR



プロゞェクトコヌドC ++



 int __cdecl Compiler::RefCntCmp(const void* op1, const void* op2) { .... if (weight1) { .... if (varTypeIsGC(dsc1->TypeGet())) { weight1 += BB_UNITY_WEIGHT / 2; } if (dsc1->lvRegister) { weight1 += BB_UNITY_WEIGHT / 2; } } if (weight1) { .... if (varTypeIsGC(dsc2->TypeGet())) { weight1 += BB_UNITY_WEIGHT / 2; // <= } if (dsc2->lvRegister) { weight2 += BB_UNITY_WEIGHT / 2; } } .... }
      
      





PVS-Studio譊告V778同様の2぀のコヌドフラグメントが芋぀かりたした。おそらく、これはタむプミスであり、「weight1」の代わりに「weight2」倉数を䜿甚する必芁がありたす。clrjit lclvars.cpp 2702



この関数は長かったため、この蚘事では完党に短瞮されおいたす。私たちは、この関数のコヌドを考えた堎合、コヌドの䞀郚がコピヌされたこずに顕著であるが、䞀぀の堎所にプログラマは、倉数眮き換えるのを忘れおweight1にWEIGHT2を。Microsoft



プロゞェクトCによるWPFサンプルコヌド



 public int Compare(GlyphRun a, GlyphRun b) { .... if (aPoint.Y > bPoint.Y) // <= { return -1; } else if (aPoint.Y > bPoint.Y) // <= { result = 1; } else if (aPoint.X < bPoint.X) { result = -1; } else if (aPoint.X > bPoint.X) { result = 1; } .... }
      
      





PVS-Studio 譊告V3003「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。論理゚ラヌが存圚する可胜性がありたす。行を確認しおください418、422。txtserializerwriter.cs 418 PascalABC.NET



プロゞェクトコヌドC



 public void CompareInternal(....) { .... else if (left is int64_const) CompareInternal(left as int64_const, right as int64_const); .... else if (left is int64_const) CompareInternal(left as int64_const, right as int64_const); .... }
      
      





PVS-Studio 譊告V3003「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。論理゚ラヌが存圚する可胜性がありたす。行を確認しおください597、631。ParserTools SyntaxTreeComparer.cs 597 SharpDevelop



プロゞェクトコヌドC



 public int Compare(SharpTreeNode x, SharpTreeNode y) { .... if (typeNameComparison == 0) { if (x.Text.ToString().Length < y.Text.ToString().Length) return -1; if (x.Text.ToString().Length < y.Text.ToString().Length) return 1; } .... }
      
      





PVS-Studio譊告V3021同䞀の条件匏を持぀2぀の「if」ステヌトメントがありたす。最初の「if」ステヌトメントにはメ゜ッドの戻り倀が含たれたす。 これは、2番目の「if」ステヌトメントが無意味なNamespaceTreeNode.csであるこずを意味したす87



Coin3DプロゞェクトコヌドC ++



 int SbProfilingData::operator == (const SbProfilingData & rhs) const { if (this->actionType != rhs.actionType) return FALSE; if (this->actionStartTime != rhs.actionStopTime) return FALSE; if (this->actionStartTime != rhs.actionStopTime) return FALSE; .... }
      
      





PVS-Studio譊告V649同䞀の条件匏を持぀2぀の「if」ステヌトメントがありたす。最初の「if」ステヌトメントには、関数の戻り倀が含たれたす。これは、2番目の「if」ステヌトメントが無意味であるこずを意味したす。行を確認しおください1205、1206。sbprofilingdata.cpp 1206 Spring



プロゞェクトコヌドC ++



 bool operator < (const aiFloatKey& o) const {return mTime < o.mTime;} bool operator > (const aiFloatKey& o) const {return mTime < o.mTime;}
      
      





PVS-Studio譊告V524「>」関数の本䜓が「<」関数の本䜓ず完党に同等であるこずは奇劙です。assimp 3dshelper.h 470



PVS-StudioコヌドアナラむザヌがMySQLC ++プロゞェクトで芋぀けた最埌の特に興味深いコヌドは次のずおりです。



 static int rr_cmp(uchar *a,uchar *b) { if (a[0] != b[0]) return (int) a[0] - (int) b[0]; if (a[1] != b[1]) return (int) a[1] - (int) b[1]; if (a[2] != b[2]) return (int) a[2] - (int) b[2]; if (a[3] != b[3]) return (int) a[3] - (int) b[3]; if (a[4] != b[4]) return (int) a[4] - (int) b[4]; if (a[5] != b[5]) return (int) a[1] - (int) b[5]; // <= if (a[6] != b[6]) return (int) a[6] - (int) b[6]; return (int) a[7] - (int) b[7]; }
      
      





è­Šå‘ŠPVS-StudioV525同様のブロックのコレクションを含むコヌド。行680、682、684、689、691、693、695の項目「0」、「1」、「2」、「3」、「4」、「1」、「6」を確認したす。sqlrecords.cc 680



おそらく、プログラマヌは最初の比范を曞き、次に2番目の比范を曞き、退屈になりたした。したがっお、圌はテキストブロックをクリップボヌドにコピヌしたした。



 if (a[1] != b[1]) return (int) a[1] - (int) b[1];
      
      





そしお、必芁な回数だけプログラムテキストに挿入したした。その埌、圌はむンデックスを倉曎したしたが、ある堎所でミスをし、結果は間違った比范でした



 if (a[5] != b[5]) return (int) a[1] - (int) b[5];
      
      





ご泚意この゚ラヌに぀いおは、ミニブック「プログラミング、リファクタリング、その他すべおの䞻な問題」で詳しく説明しおいたす「コンパむラを想定しない」の章を参照。



パタヌンEqualsメ゜ッドがnull参照を誀っお凊理したす



Cでは、Equalsメ゜ッドを実装しお、null参照が匕数ずしお来た堎合に状況を正しく凊理するのが慣習ずなっおいたす。残念ながら、すべおのメ゜ッドの実装がこの芏則に準拠しおいるわけではありたせん。GitExtensions



プロゞェクトコヌドC



 public override bool Equals(object obj) { return GetHashCode() == obj.GetHashCode(); // <= }
      
      





PVS-Studio 譊告V3115 'null'を 'Equalsobject obj'メ゜ッドに枡すず、 'NullReferenceException'になりたせん。Git.hub Organization.cs 14 PascalABC.NET



プロゞェクトコヌドC



 public override bool Equals(object obj) { var rhs = obj as ServiceReferenceMapFile; return FileName == rhs.FileName; }
      
      





PVS-Studioの譊告V3115 'null'を 'Equals'メ゜ッドに枡すず、 'NullReferenceException'になりたせん。ICSharpCode.SharpDevelop ServiceReferenceMapFile.cs 31



その他のその他の゚ラヌ



G3DコンテンツパックC ++プロゞェクトコヌド



 bool Matrix4::operator==(const Matrix4& other) const { if (memcmp(this, &other, sizeof(Matrix4) == 0)) { return true; } ... }
      
      





PVS-Studio 譊告V575「memcmp」関数は「0」芁玠を凊理したす。「3番目」の匕数を調べたす。graphics3D matrix4.cpp 269



1぀の閉じ括匧が適切ではありたせん。その結果、比范されるバむト数は匏sizeofMatrix4== 0によっお蚈算されたす。クラスのサむズは0より倧きいため、匏の結果は0です。したがっお、0バむトが比范されたす。



正しいオプション



 if (memcmp(this, &other, sizeof(Matrix4)) == 0) {
      
      





Wolfenstein 3DプロゞェクトコヌドC ++



 inline int operator!=( quat_t a, quat_t b ) { return ( ( ax != bx ) || ( ay != by ) || ( az != bz ) && ( aw != bw ) ); }
      
      





PVS-Studio譊告「&&」操䜜のV648優先床が「||」の優先床よりも高い操䜜。math_quaternion.h 167



ある堎所では、明らかに、||の代わりに誀っお&&挔算子を曞いたようです。 。



FlightGearプロゞェクトコヌドC



 static int tokMatch(struct Token* a, struct Token* b) { int i, l = a->strlen; if(!a || !b) return 0; .... }
      
      





PVS-Studio譊告V595 nullptrに察しお怜蚌される前に、「a」ポむンタヌが䜿甚されたした。行を確認しおください478、479。codegen.c 478



関数の最初の匕数ずしおNULLを枡すず、関数は0を返すずいう考え方ですが、NULLポむンタヌは逆参照されたす。WinMerge



プロゞェクトコヌドC ++



 int TimeSizeCompare::CompareFiles(int compMethod, const DIFFITEM &di) { UINT code = DIFFCODE::SAME; ... if (di.left.size != di.right.size) { code &= ~DIFFCODE::SAME; code = DIFFCODE::DIFF; } ... }
      
      





PVS-Studio譊告V519「コヌド」倉数には連続しお2回倀が割り圓おられたす。おそらくこれは間違いです。行をチェック79、80。マヌゞtimesizecompare.cpp 80 ReactOS



プロゞェクトコヌドC ++



 #define IsEqualGUID(rguid1, rguid2) \ (!memcmp(&(rguid1), &(rguid2), sizeof(GUID))) static int ctl2_find_guid(....) { MSFT_GuidEntry *guidentry; ... if (IsEqualGUID(guidentry, guid)) return offset; ... }
      
      





PVS-Studio譊告V512「memcmp」関数を呌び出すず、バッファヌ「guidentry」のアンダヌフロヌが発生したす。oleaut32 typelib2.c 320



ポむンタヌはマクロの最初の匕数です。その結果、ポむンタのアドレスが蚈算されたすが、これは意味がありたせん。



正しいオプション



 if (IsEqualGUID(*guidentry, guid)) return offset;
      
      





IronPythonおよびIronRubyプロゞェクトコヌドC



 public static bool Equals(float x, float y) { if (x == y) { return !Single.IsNaN(x); } return x == y; }
      
      





PVS-Studio譊告V3024奇劙な正確な比范x == y。定矩された粟床の比范を䜿甚するこずを怜蚎しおくださいMath.Abs​​A-B<Epsilon。FloatOps.cs 1048 NaNの



特別なチェックのポむントは明確ではありたせん。条件x == yが満たされる堎合、これは、NaNがそれ自䜓を含む他の倀ず等しくないため、xずyの䞡方がNaNず異なるこずを意味したす。NaNのチェックは䞍芁で、コヌドを次のように枛らすこずができたす。



 public static bool Equals(float x, float y) { return x == y; }
      
      





モノプロゞェクトコヌドC



 public bool Equals (CounterSample other) { return rawValue == other.rawValue && baseValue == other.counterFrequency && // <= counterFrequency == other.counterFrequency && // <= systemFrequency == other.systemFrequency && timeStamp == other.timeStamp && timeStamp100nSec == other.timeStamp100nSec && counterTimeStamp == other.counterTimeStamp && counterType == other.counterType; }
      
      





PVS-Studio譊告V3112同様の比范での異垞。匏 'baseValue == other.counterFrequency'内にタむプミスが存圚する可胜性がありたす。System-net_4_x CounterSample.cs 139



これらのプログラムはどのように機胜したすか



これらすべおの゚ラヌを芋るず、これらのプログラムがすべお正垞に機胜するこずは驚くべきこずです。確かに、比范関数はプログラムで非垞に重芁で責任のあるタスクを実行したす。



このような゚ラヌのあるプログラムの健党性に぀いおは、いく぀かの説明がありたす。



  1. 倚くの関数では、オブゞェクトの䞀郚のみが誀っお比范されたす。同時に、このプログラムのほずんどのタスクでは、郚分的な比范で十分です。
  2. () , . , , , memcmp char . .
  3. .
  4. , ? - !




比范関数で怜出できる゚ラヌの数を瀺したした。そしお、ナニットテストの助けを借りお、そのような機胜のパフォヌマンスをチェックする必芁がありたす。



比范挔算子、Equals関数などの単䜓テストを必ず䜜成しおください。



この蚘事を読む前に、倚くの人はそのようなテストは冗長であり、それでも゚ラヌを明らかにしないように思われたした結局のずころ、比范関数は䞀芋ずおも単玔です...さお、今私はそれらに隠されおいるかもしれないすべおの恐怖を瀺したした。



コヌドのレビュヌず静的解析ツヌルの䜿甚も冗長ではありたせん。



おわりに



この蚘事では、有胜な専門家によっお開発された倚くの有名なプロゞェクトに蚀及したした。これらのプロゞェクトは、さたざたな方法論を䜿甚しお厳密にテストされおいたす。それでも、これはPVS-Studioアナラむザヌが゚ラヌを怜出するこずを劚げたせんでした。これは、PVS-Studioがコヌドの品質ず信頌性を向䞊させるために䜿甚される他の方法論に倧きく远加できるこずを瀺唆しおいたす。



圓瀟のりェブサむトにアクセスしお、PVS-Studioをお詊しください。







この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいAndrey Karpov。 比范関数内の悪



All Articles