Safeguard
Application Security•

CWE-1087 Virtual Method Without a Virtual Destructor in C++

CWE-1087 is a class with virtual methods but no virtual destructor. Deleting through a base pointer then skips the derived cleanup, with undefined behavior and leaks.

Safeguard Security Team
4 min read

CWE-1087, Class with Virtual Method without a Virtual Destructor, describes a class that declares a virtual method but has no associated virtual destructor. It is tagged for object oriented languages, and C++ is where it bites.

It is a close cousin of CWE-1079, which covers a parent class with children but no virtual destructor. The signal here is different: a virtual method is a strong hint that the class is designed to be used polymorphically, which means someone will eventually delete a derived object through a base pointer. MITRE allows this entry for mapping real world vulnerabilities, after careful reading of its description.

How it happens

The rule in C++ is simple. If a class has virtual functions, objects will be handled through base class pointers or references, and deletion through that pointer needs a virtual destructor to reach the derived destructor.

// Vulnerable pattern
class Codec {
public:
    virtual void decode(const char* data) = 0;
    ~Codec() {}               // not virtual
};

class Mp3Codec : public Codec {
    unsigned char* frame = new unsigned char[8192];
public:
    void decode(const char* data) override { /* ... */ }
    ~Mp3Codec() { delete[] frame; }
};

void run() {
    Codec* c = new Mp3Codec();
    c->decode("...");
    delete c;                 // undefined behavior, Mp3Codec::~Mp3Codec skipped
}

The compiler accepts this without complaint on default settings. The program may even appear to work, because undefined behavior includes behaving normally on your machine.

// Fix
class Codec {
public:
    virtual void decode(const char* data) = 0;
    virtual ~Codec() = default;
};

What an attacker gains

MITRE lists reduced reliability, for example undefined behavior, and notes that if an attacker can reach the code, the reliability problem might become a vulnerability. Think of a network service that creates and destroys a polymorphic object per request. A leak in the derived destructor, hit on every request, becomes a memory exhaustion path. Undefined behavior can also corrupt memory in ways that are hard to predict.

How to find it

MITRE names automated static analysis as a detection method. Beyond that:

  • Enable the compiler warning for deleting an object of a class with virtual functions through a base pointer with a non virtual destructor. GCC and Clang both offer one.
  • Run a static analyzer in CI and treat the finding as an error.
  • Run tests under a memory checker such as AddressSanitizer, which reports leaks and some invalid deletes.
  • Grep new classes for the keyword virtual and check that a destructor is declared.

How to fix it

The MITRE record lists no formal mitigations, so these are standard C++ practice.

  1. Declare a virtual destructor in every class that has virtual methods and is meant as a base.
  2. Use = default when the destructor has nothing to do. It costs nothing and documents intent.
  3. If the class should not be deleted through a base pointer at all, make the base destructor protected and non virtual instead.
  4. Mark classes that are not meant to be inherited from as final.
  5. Hold polymorphic objects in a smart pointer so raw delete calls are rare. This reduces exposure, but it does not replace a correct class design.

What a good review looks like

When you read a pull request that adds a class hierarchy, ask three questions. Is there a virtual method? Is there a destructor on the base? Is it virtual or protected? Those three checks take seconds and prevent a class of bug that is otherwise silent until production load shows up the leak.

Quick check

Why does a virtual method imply a virtual destructor?

A virtual method means the class is meant for polymorphic use. Objects will be deleted through base pointers, and only a virtual destructor makes the derived destructor run.

What is the alternative to a virtual destructor?

A protected non virtual destructor, which stops callers from deleting through the base pointer at all. Pick one approach deliberately.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.