アプリケーション実行ロジックのエラーを検出したいです。たとえば、
free()
返送先住所に電話するのを忘れました。malloc()
- 返されたファイルハンドルが閉じられませんでした。
open()
- 無効なフラグが渡されました。
open()
- 渡されたファイルハンドルが正しくありません。
poll()
write()
書き込み用に開いていないfd呼び出しopen()
たとえば、open("/etc/fstab", 4) に無効なフラグを渡します。close()
無効なfd呼び出し中- ...
何百ものものがあると思います。
おそらく、ツールはftrace
またはように実行できますが、strace
エラー呼び出しを含むカーネルログで十分です。
答え1
free()
返送先住所に電話するのを忘れました。malloc()
まあ、malloc
これはfree
カーネル呼び出しではありません!なんですかmalloc()
(これはlibcライブラリ関数、通常のユーザープロセスコードです!)
- メモリプールに要求されたサイズのブロックがあることを確認する
- その場合、使用済みとしてマークされ、プログラムに返されます。そうでない場合、呼び出し
sbrk
(または同等に匿名メモリ割り当てが一般的です)は、カーネルに特定の数の新しい仮想メモリページを要求してプールに追加し、プログラムの要件を満たします。
free
以前に返されたsメモリブロックをインポートするmalloc
と、メモリプールで未使用としてマークされます。 (そうでなければ未定義の動作が発生しますが、ほとんどのlibcはこの時点で中断されます。)ほとんどの実装はメモリをオペレーティングシステムfree
に返そうとしません!
メモリのクリーンアップが必要な場合は、これらのエントリと呼び出しを監視し、メモリブロックのアドレスがまだプログラムのどこかに「保存」されているかどうかを追跡するためのgcc -fsanitize
ツール(valgrindなど)があります。関数が終了すると、そのアドレスを保持するポインタが存在しなくなり、メモリが割り当てられたことを誰も覚えていません。それは本当のバグです。メモリをすぐに解放しないか、プログラムが終了するまで解放を延期することはまったく問題になりません。ポイントは、あなたが得るメモリの寿命が潜在的に無限であるということです! (ヒント:これらの問題が心配され、そうすることが正しい場合は、Cを書かないでください。オブジェクトの寿命を適切に追跡できる言語で記述してください。これはRustやC ++のような言語です。free
malloc
malloc
malloc
C ++をCの拡張と見なさない限り。私のC ++コードではまったく使用されていないか、よりnew
悪いいくつかの大きなプログラムがあります。malloc
スマートポインタは肩から多くのトラップを削除します。これはC ++でも非常に重要です。許可する手動メモリ制御を実行できますが、最新のバリエーションではコストのかかるオブジェクトの寿命トレースを提供するので、そうしないことをお勧めします。 )
返されたファイルハンドルが閉じられませんでした。
open()
それは問題ではありません!メモリと比較してプログラムが終了するまでファイルを開いたままにすることは、完全に許容可能であっても合理的です。たとえば、ファイルロックをすぐに放棄すると機能しません。そして、制御インタフェースはプログラムが閉じるまで開かなければなりません。
繰り返しますが、プログラムの制御フロー内で何千ものファイルが開いていて閉じることを忘れる可能性がある場合は、Cで書かずに、ファイルハンドルに寿命があり、ファイルハンドルを閉じることができる言語で書いてください。閉鎖される。これ以上不要になった場合は、デフォルトのファイル記述子です。
ただ: 「開いたファイルがあるがまだ閉じていないファイルがあります」はまったく問題にはなりません。特にPOSIXシステムでは、同時ファイルアクセスは正常であり、多くの点で明確に定義されています。
無効なフラグが渡されました。
open()
エラーコードを返す以外にこれをどのように知ることができますか?
私は、ライブラリが「書き込み+追加」モードでファイルを開くことができるかどうかを確認するのが正常ですが、開くことができない場合は問題になりません。
システムコールが行われるたびに監視し、その引数や人気プログラムで使用されるように、ユーザースペースに「返される」内容を取得するには、ptrace
システムコールが役立ちます。strace
他のオプションには、eBPFプローブまたはアップローブの作成が含まれます。これは非常に効率的で、そのイベントのロギングを「インテリジェントにフィルタリング」するために使用できます。
渡されたファイルハンドルが正しくありません。
poll()
前の質問と同様に、これはファイルハンドルをポーリングできるかどうかを確認するプログラムです。すべての(擬似)ファイルシステムに該当するわけではありません。
またpoll
、必要に応じて、実際にはglibcが提供するラッパー関数(シンボル)の名前であり、「void」はシステムコールの「void」と異なる場合がありますpoll
。
答え2
まず、割り当てられたメモリブロック解除の失敗は必ずしもカーネルに関連しているわけでmalloc()
はありません。メモリ割り当てはCライブラリによって処理されます。ウォールグラインドメモリチェックこれらは検出できます。
カーネルから返されたエラーを追跡するには、次のコマンドを使用してプログラムを実行できますstrace -Z
(strace
5.2以降で利用可能)。このコマンドは、エラーを返すシステム呼び出しのみを追跡します。たとえばEBADFD
、結果を後処理する必要があります。EINVAL
呼び出しに失敗しfree()
たり、close()
必ずしもエラーになるわけではありません。これらのリソースはとにかくプログラムの終了時に解放されるため、場合によってはリソースを明示的に放棄しないことが完全に許可されます。
答え3
これらの一般的なコーディングエラーを見つけるために使用できるさまざまな静的コード分析ツールがあります。すべてのシナリオをカバーするかどうかは言えませんが、SonarQubeはCをサポートするツールの1つです。https://www.sonarqube.org/features/multi-ランゲージ/c
C言語には何百ものSonarQubeルールがあります。https://rules.sonarsource.com/c
静的コード分析が不十分な場合は、実行中のプログラムを調べるために動的分析が必要になる場合があります。https://en.wikipedia.org/wiki/Dynamic_program_analytic