Skip to content

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.

Updated 02 Nov 2026Checked against cli@de9b52a
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

  1. Declare virtual ~Name() = default; in the base class.
  2. If the class must never be deleted through a base pointer, make the destructor protected and 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.

Edit this pageSources: cli/internal/sast/rules/vnx-1079.rego, cli/internal/sast/docs/vnx-1079.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.