VNX-1079: Parent Class without Virtual Destructor Method
A C++ class or struct with virtual member functions and no base clause whose destructor is public and not virtual, where VNX-1087 does not already report it.
| Field | Value |
|---|---|
| Rule ID | VNX-1079 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | C++ |
| CWE | CWE-1079 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | parent-class-without-virtual-destructor cwe-1079 |
| Source | internal/sast/rules/vnx-1079.rego |
Overview
VNX-1079 maps to CWE-1079: Parent Class without Virtual Destructor Method. It reports a C++ class or struct with no base clause that declares at least one virtual member function, so it is meant to be used through a base pointer, but whose destructor is public and not virtual: none declared, or ~Name() declared without virtual in a public section. The finding is on the class header.
It does not report a virtual destructor, a protected or private destructor (the idiom for bases that must not be deleted polymorphically), classes with no virtual members, final classes, derived classes, forward declarations, test sources, or comments. A virtual member of a nested class does not count for the enclosing class. virtual after an attribute ([[nodiscard]] virtual) counts, and virtual alone on the line above ~Name() is a virtual destructor.
Why This Matters
delete base; or a std::unique_ptr<Base> holding a derived object is undefined behaviour when the base destructor is not virtual. In practice the derived destructor is skipped, leaking or corrupting whatever the derived class owns.
What Gets Flagged
class Codec { // FLAGGED
int level_ = 0;
public:
~Codec() { flush(); } // public, not virtual
virtual int encode(const char *in, char *out);
private:
void flush();
};
A class with virtual members and no virtual destructor is also what VNX-1087 reports, at the same class head. When VNX-1087 reports a class (class Shape { public: virtual double area() const = 0; };, struct Plugin { ~Plugin(); virtual void run() = 0; };), this rule leaves it to VNX-1087 so the class is reported once. This rule keeps the classes VNX-1087 does not judge: a public non-virtual destructor in a class that also has a protected or private section (VNX-1087 treats any such section as a protected destructor), and headers in .ipp, .tpp, .inl, .h++ and .c++ files.
Not flagged: virtual ~Shape() = default;, a protected: ~Observer() = default;.
Remediation
- Declare
virtual ~Name() = default;in the base class. - If the class must never be deleted through a base pointer, make the destructor
protectedand non-virtual.
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-1079
// nosec vnx-1079
Or record an ignore rule: vulnetix ignore add --rule vnx-1079 --reason "...". See Suppressing findings.