CERT.DCL.SAME_SCOPE_ALLOC_DEALLOC

同一のスコープでペアとして割り当て関数と割り当て解除関数をオーバーロードしてください。

同一のスコープでペアとして割り当て関数と割り当て解除関数をオーバーロードしてください。

脆弱性とリスク

対応する動的なストレージ関数のオーバーロードに失敗すると、MEM51-CPP などの規則に違反する可能性があります。動的に割り当てられたリソースは、適切に割り当てを解除してください。たとえば、オーバーロードされた割り当て関数がプライベートヒープを使用してその割り当てを実行する場合、返されたポインターをデフォルトの割り当て解除関数に渡すと、定義されていない動作が発生する可能性があります。割り当て関数が最終的にデフォルトのアロケータを使用してメモリへのポインターを取得する状況でも、対応する割り当て解除関数のオーバーロードが失敗すると、カスタムアロケータの内部データが更新されなくなり、プログラムが予期しない状態になる可能性があります。

軽減と防止

割り当て関数を特定のスコープでオーバーロードする場合、対応する割り当て解除関数も同じスコープでオーバーロードする必要があります (この逆も同様)。

コピー
  #include <Windows.h>
  #include <new>
  
  extern "C++" void update_bookkeeping(void *allocated_ptr, std::size_t size, bool alloc);
  
  void *operator new(std::size_t size) noexcept(false) {     // uncompliant code, it only overloads 'new' in std namespace, not 'delete'.
    static HANDLE h = ::HeapCreate(0, 0, 0); // Private, expandable heap.
    if (h) {
      return ::HeapAlloc(h, 0, size);
   }
   throw std::bad_alloc();
 }
 
 struct S_positive {
   void *operator new(std::size_t size) noexcept(false) {
     void *ptr = ::operator new(size);
     update_bookkeeping(ptr, size, true);                        // uncompliant code
     return ptr;
   }
 };
 
 struct S_negative {                                                        // compliant code
   void *operator new(std::size_t size) noexcept(false) {
     void *ptr = ::operator new(size);
     update_bookkeeping(ptr, size, true);
     return ptr;
   }
 
   void operator delete(void *ptr, std::size_t size) noexcept {
     ::operator delete(ptr);
     update_bookkeeping(ptr, size, false);
   }
 };

6 行目と 15 行目の割り当てメソッドと同じスコープ内には、オーバーロードされた対応する割り当て解除メソッドが存在しません。38 行目のオーバーロードされた割り当てメソッドは適合していると言えます。対応する割り当て解除メソッドが 29 行目でオーバーロードされているためです。