CERT.CONC.LOCK.NO_RELEASE_ON_EXCEPTION

アクティブに保持されているロックが例外条件で解放されるようにする

このチェッカーは、モダンエンジンにのみ適用されます。

CERT.CONC.LOCK.NO_RELEASE_ON_EXCEPTION は CERT C++ 規則 CON51-CPP を実装しています。これは、手動でロックされているものの、すべての実行パスでロック解除されることが保証されていないミューテックスを報告します。例外発生時や早期終了時にロックされたままのミューテックスは、デッドロックを引き起こす可能性があります。

軽減と防止

lock() と unlock() を手動で呼び出すのではなく、std::lock_guard、std::unique_lock、std::shared_lock などの RAII ロック所有権ラッパーを使用することが推奨されます。これらのラッパーは、オブジェクトがスコープ外になった時点でミューテックスを自動的に解放するため、例外や早期終了を含むすべての終了パスにおいてロックが確実に解放されます。

手動ロックが必要な場合は、あらゆる実行パスにおいて、関数が終了する前にミューテックスが解放されるようにしてください。特に、例外パスでは、対応する unlock() の呼び出しを迂回してはなりません。すべてのパスでミューテックスを解放できない場合、ミューテックスが永久にロックされたままとなり、デッドロックを引き起こす可能性があります。

脆弱コード例

コピー
#include <mutex>

void manipulate_shared_data(std::mutex &pm) {
  pm.lock();          // CERT.CONC.LOCK.NO_RELEASE_ON_EXCEPTION
  // ... work on shared data that may throw ...
}                     // pm is still locked on every exit

関数復帰前に unlock() が呼び出されない、手動でロックされたミューテックス。処理中に例外が発生した場合、ミューテックスは解放されません。

修正コード例 1 (推奨: RAII)

コピー
#include <mutex>

void manipulate_shared_data(std::mutex &pm) {
  std::lock_guard<std::mutex> guard(pm);
  // ... work on shared data ...
}                     // guard's destructor unlocks on every exit, incl. exceptions

修正コード例 2 (手動でのロック解除)

コピー
#include <mutex>

void manipulate_shared_data(std::mutex &pm) {
  pm.lock();
  try {
    // ... work on shared data ...
  } catch (...) {
    pm.unlock();
    throw;
  }
  pm.unlock();
}

注: 現在のフロー非依存型チェッカーは、関数内に unlock() が含まれているため、例 2 を許容しています。ただし、catch パスで実際にロックが解除されるかは検証されません。標準的な手動ロック解除のイディオムには十分ですが、潜在的な偽陰性の発生源となります。

制限事項

CERT.CONC.LOCK.NO_RELEASE_ON_EXCEPTION チェッカーは、例外処理や制御フローを完全にモデル化しない簡略化された解析を使用しています。単一の関数内での単純なミューテックスオブジェクトの追跡のみをサポートしており、一部のラムダ式では重複したレポートが生成される場合があります。また、現在は std ミューテックス API のみをサポートしています。

外部参考資料