VNX-1087 – Class with Virtual Method without a Virtual Destructor
Overview
VNX-1087 maps to CWE-1087: Class with Virtual Method without a Virtual Destructor.
A class that declares virtual methods is meant to be used polymorphically — held and deleted through a pointer to a base class. If its destructor is not virtual, delete basePtr runs only the base destructor. The derived class’s destructor never runs, so whatever it owned is leaked, and the standard defines the behaviour as undefined.
Severity: Medium | CWE: CWE-1087 | Languages: C++
What Gets Flagged
A class with at least one virtual member function and no virtual destructor:
// FLAGGED: virtual method, non-virtual destructor
class Base {
public:
virtual void handle();
~Base();
};
// FLAGGED: virtual method, no destructor declared, so it inherits a non-virtual one
class NoDtor {
public:
virtual int compute() const;
};
What Does Not Get Flagged
Three shapes are correct and are deliberately not reported:
// Destructor is virtual — the usual fix.
class Good {
public:
virtual void handle();
virtual ~Good();
};
// `final`: cannot be derived from, so it can never be deleted through a base
// pointer of its own type.
class Sealed final {
public:
virtual void handle();
~Sealed();
};
// Protected non-virtual destructor: the other sanctioned resolution. Callers
// cannot delete through a base pointer at all, so there is nothing to slice.
class Guarded {
public:
virtual void handle();
protected:
~Guarded();
};
A destructor marked override or final also counts as virtual, since only a virtual function can override one.
Remediation
- If the class is meant to be a polymorphic base, declare the destructor
virtual:virtual ~Base(); - If it is not meant to be deleted through a base pointer, make the destructor
protectedand non-virtual. This is cheaper — no vtable entry — and the compiler enforces the intent. - If the class is not meant to be derived from at all, mark it
final. - For a confirmed false positive, add
// vulnetix-ignore: VNX-1087on the line.
Scope
The rule analyses class bodies up to 400 lines; a longer class is skipped rather than guessed at. Forward declarations (class X;) have no body and are ignored.
Note on earlier versions
Before 2026-08, this rule shipped as a Python “function has no docstring” check — unrelated to its declared CWE, byte-identical to VNX-1117, and the largest single source of findings on any Python repository. VNX-1117 remains the general documentation lint; VNX-1087 now checks what its identifier says it checks.