一年を通して、私たちはオープンソースの広大な平原を乗り越え、犯罪を調査し、脆弱性を取り出し、トロフィーを収集してきました。 面白いストーリーが欲しい? 一年間、我々はCとC++のオープンソースプロジェクトから様々なバグと戦ってきた。我々はそれぞれを捕らえ、それらを尋問し、その過ちをファイルに記録した。 今日は、私たちがワイルド・ウエストのさまざまな角で出会った10の最も興味深いバグについてのストーリーを紹介します。これらのフラグメントのそれぞれのために、私たちは別々のファイル - 完全な記事を持っています。 ポリモルフ分配器の冷静な使用があなたの生活を浸透させることができる方法 C++プログラマーの未定義行動ガイド 第2部:CとC++、Qt、Clang、Unreal Engineの標準化 std::array in C++ is faster than array in C. たまに Safe Array Handling? Never heard of it 聞いたことがない あなたは私たちのブログの記事の完全なリストを見つけることができます私たちのウェブサイトのリンクを介して。 リンク 座って、旅人よ、物語は長いものになる。 N10 かつて、我々は行方不明の馬の事例を持っていたが、は常に安定していたが、我々が町に乗りたがるたびに、それは薄い空気の中に消えるように見えた。 PVSスタジオの警告: 'float' 型の文字列 '0.5f' は、 'SetRenderColor' 関数を呼び出すときに、暗示的に 'unsigned char' 型に投げ込まれています。 168 ワイ674 grenade_bugbait.cpp typedef unsigned char byte; inline void CBaseEntity::SetRenderColor( byte r, byte g, byte b, byte a ) { m_clrRender.Init( r, g, b, a ); } void CGrenadeBugBait::BugBaitTouch( CBaseEntity *pOther ) { .... if ( pSporeExplosion ) { .... pSporeExplosion->SetRenderColor( 0.0f, 0.5f, 0.25f, 0.15f ); // <= .... } .... } The Function Set the 色値は、それぞれのパラメータが 可能な値範囲を持つタイプ 議論を進めようとすると、The 割り切った部分が割り切られるので、 で、 で、 で、 function parameters will have values equal to function parameters will have values equal to function parameters . SetRenderColor RGBA unsigned char [0 .. 255] float r g b a 0 残念ながら、リポジトリには責任情報が欠けているので、このエラーがコードにどのように現れたかについて2つのシナリオがあります。 The function once processed colors in a floating-point representation. The processing was later changed to integers, but not all call sites were updated. 開発者は、SetRenderColorが浮動点番号を処理し、それに応じて設定したと誤って信じていました。 以下に類似の警告があります。 V674 「float」型の文字通り「0.5f」は、「SetRenderColor」関数を呼び出すときに暗示的に「unsigned char」型に投げ込まれています。 V674 文字通り「25.6」の「ダブル」型は、「SetScrollRate」関数を呼び出すときに暗示的に「int」型に投げ込まれている。 We detected this bug in the Source SDK project. You can find the full article at the . link リンク N9 かつては100本の同じ道を渡らなければならなかったので、すでに乗っていた道に戻り、まったく違う場所へと向かいました。 PVSスタジオの警告: 「もし(A) {...} その他(A) {...}」のパターンの使用が検出された場合、論理的なエラーの存在の可能性があります。チェックライン: 2903, 3053. V517 erl_bif_info.c 2903 BIF_RETTYPE system_info_1(BIF_ALIST_1) { .... if (is_tuple(BIF_ARG_1)) { // L2778 .... } else if (BIF_ARG_1 == am_scheduler_id) { // L2782 .... } .... else if (BIF_ARG_1 == am_garbage_collection) { // L2903 .... } else if (BIF_ARG_1 == am_fullsweep_after) { // L2921 .... } else if (BIF_ARG_1 == am_garbage_collection) { // L3053 .... } else if (BIF_ARG_1 == am_instruction_counts) { // L3056 .... } .... else if (ERTS_IS_ATOM_STR("halt_flush_timeout", BIF_ARG_1)) { // L3552 .... } } アナリストは、膨大な数を含む関数で同一のチェックを持ついくつかの支店を検出しました。 声明 - しかし、それぞれ異なる論理を持っています: そして 枝の数と複製物の間の150行間の間隔を考えると、これが起こりうることは驚きではありません。 if-else if 約800ライン 最初のチェック 第2次チェック Erlang プロジェクトでこのバグを検出しました; あなたはリンクで全文を見つけることができます。 link N8 わたしは、すべての犯罪に対して一つの判決しか持っていない警官を知っていた: 「無罪」 彼は奇妙な人だった。 PVSスタジオの警告: 「それから」という文は、次のコードフラグに等しい。 V523 cmComputeLinkInformation.cxx 1748 bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry) { BT<std::string> const& item = entry.Item; // We only switch to a pathless item if the link type may be // enforced. Fortunately only platforms that support link types // seem to have magic per-architecture implicit link directories. if (!this->LinkTypeEnabled) { return false; } // Check if this item is in an implicit link directory. std::string dir = cmSystemTools::GetFilenamePath(item.Value); if (!cm::contains(this->ImplicitLinkDirs, dir)) { // Only libraries in implicit link directories are converted to // pathless items. return false; } // Only apply the policy below if the library file is one that can // be found by the linker. std::string file = cmSystemTools::GetFilenameName(item.Value); if (!this->ExtractAnyLibraryName.find(file)) { return false; } return false; } アナリストは、間違いなく何かが間違っていることを示唆しています。 機能: CheckImplicitDirItem 最後の文が下のコードを複製する場合(false を返します)。 関数実行のすべての分野は False Return で終了します。 when called from , it'll never trigger an early return; AddFullItem the full function body can be replaced with , as this won't change the program behavior. return false; 関数は「 」を用いて書かれていることに注意してください。 コードの巣立つことを減らすのに役立つパターン: 最もポジティブな結果は関数の終わりに置かれ、関数の目的と異なる場合、他のコードはできるだけ早く関数を終了する必要があります。 早期復帰 私たちの例では、この関数の最も「ポジティブな」結果は、その関数のオブジェクトが タイプは、必要なすべての検査を経過したA return value. LinkEntry true Here's an option how to fix the code: .... std::string file = cmSystemTools::GetFilenameName(item.Value); if (!this->ExtractAnyLibraryName.find(file)) { return false; } return true; We detected this bug in the CMake project; you can find the full article at the . リンク リンク N7 ある村で、私は絶望したプレイヤーを見たが、彼のすべてのゴールドをデッキにさえなかったカードに賭けた。 The PVS-Studio warning: 無効なイタレータ 'shades.end()' をリファレンスする場合があります。 V783 ColorHelper.cpp 194 winrt::Windows::UI::Color ColorHelper::GetAccentColor( const winrt::Windows::UI::Color& color ) { .... auto shades = std::map<float, HSL>(); .... // 3f is quite nice if the whole non-client area is painted constexpr auto readability = 1.75f; for (auto shade : shades) { if (shade.first >= readability) { return HslToRgb(shade.second); } } return HslToRgb(shades.end()->second); // <= } 色合いのいずれも可読性の基準を満たさない可能性がありますか? 確実に言うことはできませんが、かなり可能性があります。 オ ―それを証明するために、何らかの精神的な書類をフラッシュする必要はありません。 causes exactly that, as this iterator points just past the last element in a . テキストケース undefined behavior std::map::end() std::map Windows Terminal プロジェクトでこのバグを検出しました; あなたはリンクで全文を見つけることができます。 リンク N6 私はかつて、私たちが見つけた宝物を分け与えるために旅行者の仲間と契約を結んだ。彼はノックして、振り向いて、ミラージュのように消え去った。 PVSスタジオの警告: 「グラフ」参照は、関数によって返されたスマートポインタが破壊されたときに無効になります。 ワイ758 トップページ > CPP 391 template<typename T> struct Ptr : public std::shared_ptr<T>; // .... Ptr<FlannNeighborhoodGraph> FlannNeighborhoodGraph::create( const Mat &points, int points_size, int k_nearest_neighbors_, bool get_distances, int flann_search_params_, int num_kd_trees) { return makePtr<FlannNeighborhoodGraphImpl>(points, points_size, k_nearest_neighbors_, get_distances, flann_search_params_, num_kd_trees); } void Utils::densitySort (const Mat &points, int knn, Mat &sorted_points, std::vector<int> &sorted_mask) { // .... // get neighbors FlannNeighborhoodGraph &graph = // <= *FlannNeighborhoodGraph::create(points, points_size, knn, true /*get distances */, 6, 1); std::vector<double> sum_knn_distances (points_size, 0); for (int p = 0; p < points_size; p++) { const std::vector<double> &dists = graph.getNeighborsDistances(p); for (int k = 0; k < knn; k++) sum_knn_distances[p] += dists[k]; } // .... } A bit more context on Ptr template<typename T> struct Ptr : public std::shared_ptr<T> { inline Ptr(const std::shared_ptr<T>& o) CV_NOEXCEPT : std::shared_ptr<T>(o) {} inline Ptr(std::shared_ptr<T>&& o) CV_NOEXCEPT : std::shared_ptr<T>(std::move(o)) {} typename std::add_lvalue_reference<T>::type operator*() const CV_NOEXCEPT { return *std::shared_ptr<T>::get(); } // .... } template<typename _Tp, typename ... A1> static inline Ptr<_Tp> makePtr(const A1&... a1) { static_assert( !has_custom_delete<_Tp>::value, "Can't use this makePtr with custom DefaultDeleter"); return (Ptr<_Tp>)std::make_shared<_Tp>(a1...); } スマートポインタの使用は、ここでリファレンスやメモリアクセスの問題を解決しません。 作成関数は、FlannNeighborhoodGraphImpl 型のスマートポインタを作成して返し、そのオブジェクト参照数は 1 です。 グラフ参照は、このスマートポインタの値のために作成され、オブジェクト参照数は変更されません。 ポインタは一時的なオブジェクトであるため、初期化が完了した後、参照カウンターはゼロに達し、管理されたオブジェクトをリリースします。 ループへの参照は無効な参照です。 その結果、正しいように見えたコードは未定義の行動につながります。さらに、PVS-Studioはこの問題を検出する唯一のツールではありません。 . proof これを修正するには、スマートポインタを保存する必要がありますので、 オブジェクトはブロックの終わりまで残ります. たとえば、私たちは : FlannNeighborhoodGraph こんな感じ std::vector<double> sum_knn_distances (points_size, 0); { // get neighbors auto graph = FlannNeighborhoodGraph::create(points, points_size, knn, true /*get distances */, 6, 1); for (int p = 0; p < points_size; p++) { const std::vector<double> &dists = graph->getNeighborsDistances(p); for (int k = 0; k < knn; k++) sum_knn_distances[p] += dists[k]; } } また、我々は制限する scope to free the resource after the loop execution. graph We detected this bug in the OpenCV project; you can find the full article at the . リンク N5 ある時、地元の専門家が川を渡る地図を描いたが、彼は石炭から抜け出したので、石炭の最後の部分で描かれた最も危険な部分は、最初の雨で洗われた。 The PVS-Studio warnings: 「1 << (brake->type + 1)」の表現を確認することを検討してください. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. V629 phpdbg_bp.c 1209 ビットマスクのサイズは、最初のオペランドのサイズより小さいので、より高いビットの損失を引き起こします。 ワイ784 phpdbg_bp.c 1209 uint64_t flags .... PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num) { .... if ((brake = phpdbg_find_breakbase_ex(num, &table, &numkey, &strkey))) { int type = brake->type; char *name = NULL; size_t name_len = 0L; switch (type) { .... default: { if (zend_hash_num_elements(table) == 1) { PHPDBG_G(flags) &= ~(1<<(brake->type+1)); // <= } } } .... } } Math guys, no time to relax. リラックスする時間がない。 variable is of the タイプですが、 is of the type. The code is designed to remove a specific bit from . Now, let's take a closer look at what's really going on: flags unsigned long int brake->type int flags int 型の 1 個の常数は、一定のビット数によって変換されます. ほとんどの場合、int 型は 32 ビットです. We hope that the shift is not by 32 or more bits, otherwise we get undefined behavior. 転換の結果は bitwise に逆転します. The result of the inversion still has the int type. 逆転の結果は int 型です。 逆転の結果は、左のオペランドによって64ビットの未署名型に拡張されます。オリジナル型が署名されているため、シグナル拡張が発生します。これは、ポジティブな数字の場合、最も重要な32ビットがゼロビットを含み、ネガティブな数字の場合、それらが含まれることを意味します。 The bitwise "AND" applies the conversion result to . The loss of significant bits in will occur when the right operand is positive. It'll only be so when there is a 31-bit shift to the left—when the 31st bit in has to be cleared. Catch a . flags flags flags proof このような無害な表現のために私たちがどれだけ心に留めなければならないかご注意ください? 問題は、いくつかのサブ表現の異なるオペランドサイズと記号にあります。 それを修正するために、開発者は単にそのタイプを変更する必要があります。 constant from 2位 , and the code will execute as intended: コードは意図したとおりに実行されます。 1 int unsigned long long PHPDBG_G(flags) &= ~( 1uLL <<(brake->type+1)); We discovered this bug in the PHP project; you can find the full article via the . リンク N4 私はかつて、ある若いカウボーイが違法者を追いかけていたのを見たが、彼は地元のバーの1つで死んだ場所に彼を巻き込んだが、彼は犯罪者ではなく、灰色の鏡で自らの反射に撃った。 PVSスタジオの警告: 割り当てオペレーターは「this ==&other」の場合から保護されるべきである。 ワイ794 fs_path.cpp 36 FsPath& FsPath::operator=(FsPath&& other) { m_path = std::move(other.m_path); other.m_path.clear(); return *this; } In this snippet, we have the move assignment operator for the 別のオブジェクトから現在のインスタンスにデータを転送するクラスですが、自己割り当てのチェックはありません。 意図しない結果につながる可能性があります。 FsPath (this == &other) オブジェクトを自分自身に割り当てようとすると、 操作はコンテンツを動かす 入 その後の呼びかけは、 データを削除した結果、 予期せぬ状態で終わり、開発者にハッピーなデバッグを願うしかありません :) m_path = std::move(other.m_path); other.m_path m_path other.m_path.clear(); m_path リスクを排除するには、オペレーターの開始時に以下のチェックを追加することをお勧めします。 if (this == std::addressof(other)) { return *this; } 利用 代わりに、The オペレーターは、オペレーターが正しいアドレス比較を確保する場合でも、 is overloaded in the class. std::addressof & & We detected this bug in the Nau Engine project; you can find the full article at the . link リンク N3 I once saw a shaman trying to summon a spirit without reaching the sacred grounds. A spirit came, but it was a completely different one—a coyote from the nearest ravine. PVSスタジオの警告: uninitialized derived class の 'window_id' 関数を歌うと、modal_dialog ベースクラスを初期化すると、未定義の行動が起こります。 29 V1099 install_dependencies.hpp より class install_dependencies : public modal_dialog { public: explicit install_dependencies(const addons_list& addons) : modal_dialog(window_id()), addons_(addons) // <= {} .... private: virtual const std::string& window_id() const override; .... } Thanks to this code snippet, I can tell you more about undefined behavior. 上から見ると、その クラスの由来は、 class. In the constructor, the base class is initialized with the value returned by (wait for it...) the non-static 機能は、これで起こります: install_dependencies modal_dialog install_dependencies window_id Execution of the initialization list: a call to the method; install_dependencies::window_id a constructor call to the class; modal_dialog an initialization of the data member; addons_ install_dependencies クラスの構造体を実行します。 This results in a function call of a class object which hasn't been initialized yet! This violates the following : rule of the standard Member functions (including virtual member functions, [class.virtual]) can be called for an object under construction 同様に、構築中のオブジェクトは、typeid オペレーター([expr.typeid])または dynamic_cast ([expr.dynamic.cast])のオペレーダである可能性があります。 However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the program has undefined behavior. But wait, there's more! As you may have noticed, the member function is virtual and overridden in the 問題は、プログラマーが由来のクラスを書くときに後で発生する場合があります。 過剰です。 window_id install_dependencies window_id この由来のクラスのオブジェクトが作成され、 constructor が実行され、新しい override の存在についての情報はまだありません。 この機能は、初期化リストで常に呼び出されますが、開発者の最初の意図とは異なります。 . installed_dependencies installed_dependencies::window_id ここ We detected this bug in the Wesnoth project; you can find the full article at the link. このバグをWesnothプロジェクトで検出しました。 リンク N2 私は壁の影を撃ったカウボーイを知っていたが、それを隠れている敵と勘違いした。ショットは爆発し、ギターが崩壊し、壁に穴だけを残した。 The PVS-Studio warning: null ポインタは「fseek」関数に移行します。 ワイ575 void ati_eeprom_load_mach8(ati_eeprom_t *eeprom, char *fn, int mca) { FILE *fp; .... fp = nvr_fopen(eeprom->fn, "rb"); size = 128; if (!fp) { if (mca) { (void) fseek(fp, 2L, SEEK_SET); // <= memset(eeprom->data + 2, 0xff, size - 2); fp = nvr_fopen(eeprom->fn, "wb"); fwrite(eeprom->data, 1, size, fp); .... } 私たちはビデオアダプタのNVRAMに保存されたデータをロードする必要があります. バイナリファイルに保存します. ファイルが存在しない場合は、「デフォルト」データで作成する必要があります. ファイルが欠けているケースを見てみましょう. ファイルポインタを変更します。 null pointer dereference. fp もう少し近づいてみましょう♪ ♪ The standard doesn't define requirements for the function first parameter and doesn't guarantee a check for . This means it's up to the standard library developers to handle it properly. Now let's welcome: fseek C11 NULL GNU glibc; BSD libc from FreeBSD 14.3; Microsoft Universal CRT from Windows SDK 10.0.26100 musl v1.2.5. C 標準ライブラリの最後の 2 つの実装はゲストとしてここにあります: 86Box はそれらと動作するように設計されていません。 したがって、期待される標準ライブラリから始め、null ファイルポインタを使用して同じアクションを繰り返すよう求めましょう。 構築指示 Flicking the power switch IBM PS/2 モデル 55SX を想像の棚から取り出し、ATI 製の IBM 8514/A 2D 加速器を「接続」します。 最初のテスト対象は MinGW を使用して構築された Windows インスタンスです. 私たちは、開始前に NVRAM ファイルが欠落していることを確認します。 ディレクター for the ファイルが存在する場合は削除します。 %userprofile%\86Box VMs\<virtual machine name>\nvr ati8514_mca.nvr ♪ Turning on the power supply, and... ♪ Nothing exploded! Everything is fine: the NVRAM file is written, the computer is running, and the smoke test on glibc is complete. No defect detected. ♪ Moving on to FreeBSD. The libc library implements the standard C library in this OS. This is generally true for all BSD-family operating systems. We use the same configuration. We check for the absence of the NVRAM file at the path. Three, two, one, power is on... ati8514_mca.nvr ~/.local/share/86Box/Virtual Machines/<virtual machine name>/nvr ♪ Well, only an event from 最近の過去は、この状況をよりよく説明することができるでしょう:) Ben Grubbs' ♪ 爆発の後、目を閉じたまま、私たちはコンソールを見つめ、異常な出口を確認しました! void VMManagerSystem::launchMainProcess() Full Command: "/root/86Box/build_freebsd/src/86Box" ("--vmpath", "/root/.local/share/86Box/Virtual Machines/somevm", "--vmname", "somevm") Connection received on 86Box.socket.5876c5 Connection disconnected Abnormal program termination while launching main process: exit code 11, exit status QProcess::CrashExit エミュレータを実行可能なエミュレータの隣にコア・ダンプ・ファイルが表示されました. Let's welcome LLDB: root@freebsd:~/86Box/build_freebsd/src # lldb 86Box -c 86Box.core (lldb) target create "86Box" --core "86Box.core" Core file '/root/86Box/build_freebsd/src/86Box.core' (x86_64) was loaded. (lldb) bt * thread #1, name = '86Box', stop reason = signal SIGSEGV * frame #0: 0x0000000832f880bf libc.so.7`_flockfile(fp=0x0000000000000000) at _flock_stub.c:65:20 frame #1: 0x0000000832f8b675 libc.so.7`fseek(fp=0x0000000000000000, offset=2, whence=0) at fseek.c:62:2 frame #2: 0x00000000018cd964 86Box`ati_eeprom_load_mach8(eeprom=...., fn=<unavailable>, mca=1) at vid_ati_eeprom.c:61:20 THE null pointer makes a spectacular fire show—there was no way to lock the file as its descriptor is invalid. Unfortunately, LLDB didn't really want to work in real time, crashing either with a quiet or with a loud boom and special effects. Therefore, I can't show you how the code is executed like in Windows. fp lost connection We detected this bug in the 86Box project; you can find the full article at the . リンク リンク N1 私が知っている近隣の町のシェリフは、かつて証人が自らの証言を確認したと聞き取り手紙で書いたが、裁判所はそれが誤りだったか、あるいは賢い防衛戦術だったかを決して知らなかった。 PVSスタジオの警告: There are identical sub-expressions to the left and to the right of the '==' operator: PeekArg.getValNo() == PeekArg.getValNo() V501 PPCISelLowering.cpp 7865 SDValue PPCTargetLowering::LowerCall_AIX(....) const { .... for (unsigned I = 0, E = ArgLocs.size(); I != E;) { .... CCValAssign &GPR1 = VA; .... if (I != E) { // If only 1 GPR was available, there will only be one custom GPR and // the argument will also pass in memory. CCValAssign &PeekArg = ArgLocs[I]; if (PeekArg.isRegLoc() && PeekArg.getValNo() == PeekArg.getValNo()) // <= { assert(PeekArg.needsCustom() && "A second custom GPR is expected."); CCValAssign &GPR2 = ArgLocs[I++]; RegsToPass.push_back(std::make_pair(GPR2.getLocReg(), DAG.getZExtOrTrunc(ArgAsInt, dl, MVT::i32))); } } .... } 私たちは今、それはもう一つのコピーパスタの犠牲者であると仮定しています。 副作用がある場合: getValNo class CCValAssign{ .... unsigned ValNo; unsigned getValNo() const { return ValNo; } } なんか変なことないけど、最後まで見てね。 : commit CCValAssign &GPR1 = VA; .... assert(I != E && "A second custom GPR is expected!"); CCValAssign &GPR2 = ArgLocs[I++]; assert(GPR2.isRegLoc() && GPR2.getValNo() == GPR1.getValNo() && GPR2.needsCustom() && "A second custom GPR is expected!"); RegsToPass.push_back( std::make_pair(GPR2.getLocReg(), DAG.getZExtOrTrunc(ArgAsInt, dl, MVT::i32))); アイデアは明らかです:以前は主張によって守られていた例外的なケースは、通常の支部に再設計されました。 This patch implements the caller side of placing function call arguments stack memory. This removes the current limitation where LLVM on AIX 論点がレジストリに含まれない場合に致命的なエラーを報告します。 Note that, in addition to the error found, there's another strange assignment: CCValAssign &PeekArg = ArgLocs[I]; .... CCValAssign &GPR2 = ArgLocs[I++]; // here PeekArg == GPR2 開発者は、こういうことを書くつもりなのかもしれない。 if (I != E) { CCValAssign &GPR2 = ArgLocs[I]; if (GPR2.isRegLoc() && PeekArg.getValNo() == GPR1.getValNo()) { assert(PeekArg.needsCustom() && "A second custom GPR is expected."); I++; RegsToPass.push_back(std::make_pair( GPR2.getLocReg(), DAG.getZExtOrTrunc(ArgAsInt, dl, MVT::i32))); } } しかし、明確さのために、開発者は破綻した。 から 以前の無条件コードとは異なり、議論はまず「peeked」されなければならないことを示すためです。 たまたま状態から落ちてしまった。 PeekArg GPR2 GPR1 訂正した likely should be: if if (PeekArg.isRegLoc() && PeekArg.getValNo() == GPR1.getValNo()) Interestingly, before migrating to GitHub, LLVM had a code review platform, and the commit included a そこで、私たちは、手動のレビューが常に状況を救うことができないことを見ることができます: リンク ♪ このバグはLLVMプロジェクトで発見しましたが、全文はリンクでご覧いただけます。 リンク Conclusion 沈黙がサロンの上に落ち、入口のドアの裂け声と暖炉のロゴの裂け声によってのみ破られた。2025年の最も大胆な10のバグは、今や世代から世代に渡された物語に過ぎません。 このワイルドウエストでは、信頼できるパートナーなしには遠くまで行けませんので、私の信頼できる助手は、あらゆるケースで analyzer. It's ready to help you find dangerous code snippets: PVS-Studio オープンソースプロジェクトについて; 教育目的のため、 30日間の試用版を使用して、素晴らしいスタートを切ります。 そして、あなたの道があなたを他の領域へと導くなら、JavaとC#で書かれたプロジェクトからバグに関するストーリーを聞くことをお勧めします。 ; Top 10 noteworthy Java errors in 2025 2025年にC#プロジェクトで発見された10のエラー