Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Wiki Markup
The C+\+ Standard [ISO/IEC 14882-2003|AA. C++ References#ISO/IEC 14882-2003] "One definition rule" (Section 3.2) says: "No translation unit shall contain more than one definition of any variable, function, class type, enumeration type or template."  Moreover, paragraph 3 says: "Every program shall contain exactly one definition of every non-inline function or object that is used in that program; no diagnostic required."  Although it is possible to check that the ODR is complied with (see \[[Quinlan 06|AA. C++ References#Quinlan 06]\]), as of October 2006 we are not aware of any compilers that enforce the rule or even issue a diagnostic.  As the paper by Quinlan et al. shows, failing to enforce the ODR enables a virtual function pointer attack, known as the VPTR [exploit|BB. Definitions#exploit].  This is where an object's virtual function table is corrupted so that calling a virtual function on the object results in malicious code being executed.  See the paper by Quinlan et al. for more details.

Non-Compliant Code Example

This example is taken from the paper by Quinlan et al. referenced in the introduction to this rule.

Base abstract class (Base.h)

Code Block
bgColor#FFcccc
class Base {
public:
    virtual ~Base () {}
    virtual void run () = 0;

Innocuous module (Module.cpp)

Code Block
bgColor#FFcccc
# include "Base.h"

class Derived: public Base {
public:
    Derived () {buf_[0] = 'a';}
    void run () {buf_[0] = 'z';}
    char buf_[1];
};

void runModule () {
    Derived a, b;
    Base *pa = &a, *pb = &b;
    pb->run ();  // Expect b.buf_[0] == 'z'
    pa->run ();  // Expect a.buf_[0] == 'z'
}

Malicious module (Attacker.cpp)

Code Block
bgColor#FFcccc
# include "Base.h"

class Attacker: public Base {
public: void run () {
        // vtable is overwritten
        // do malicious things here
        // ...
    }
}

class Derived: public Base {  // Class violating ODR
public:
    void run () {
        buf_[0] = 'z';  // Looks normal, but ...
        Attacker x;  // Instantiate to get a vtable to inject
        *((unsigned *)(buf_ + 12)) = *((const unsigned *)(&x));
    }
    char buf_[16];  // Buffer used to overwrite vtable
};

Derived d;  // Instantiate to get malicious Derived

If the attacker module can be introduced into the system so that the linker chooses it in preference to the "proper" class defined in Module.cpp (which can usually be achieved by putting the attacker module before the innocuous module in the list of modules to be linked, or in the shared library path), then it is possible to corrupt the virtual function table. The attacker derived class contains a buffer that overlays the vtable and its run method injects the malicious code into the appropriate place in the vtable. (This is dependent on the architecture of the system running the code.)

Compliant Solution

The solution is to not allow more than one definition of a non-inline function or object to be admitted into a system.

Risk Assessment

Failing to obey the ODR allows the VPTR exploit, which could lead to an attacker being able to execute arbitrary code. However, note that the attacker must have access to the system running the code to introduce the malicious class.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MSC33-CPP

3 (high)

1 (unlikely)

1 (high)

P3

L3

References

Wiki Markup
\[[ISO/IEC 14882-2003|AA. C++ References#ISO/IEC 14882-2003]\] Section 3.2, "One definition rule"

Wiki Markup
\[[Quinlan 06|AA. C++ References#Quinlan 06]\]


MSC04-A. Use comments consistently and in a readable fashion      49. Miscellaneous (MSC)      12. Vectors (VEC)