Skip to content

Instantly share code, notes, and snippets.

@Blaisorblade
Last active December 22, 2023 12:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Blaisorblade/cf27cdb540f6d14963f8e875cd3237f5 to your computer and use it in GitHub Desktop.
Save Blaisorblade/cf27cdb540f6d14963f8e875cd3237f5 to your computer and use it in GitHub Desktop.
C++ compilation does not preserve abstraction
struct A {
int x; //
void print() const;
};
void break_encapsulation(A& a) {
a.print();
a.x = 1;
a.print();
}
int main() {
A a;
break_encapsulation(a);
}
CXX := g++
CXXFLAGS := -std=c++11
prog: evil_client.o module.o
$(CXX) $(CXXFLAGS) -o $@ $^
# Redundant, but included for completeness
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $^
#include <cstdio>
using namespace std;
class A {
int x{0}; //
public:
int getX() const { return x; }
void print() const;
};
void break_encapsulation(A& a);
void A::print() const {
printf("this.x: %d\n", getX());
}
@Blaisorblade
Copy link
Author

This example shows (one reason that) private: in C++ does not protect when linking against arbitrary external code — so any privacy guarantees given by private are not preserved by compilation.

Of course, this code is undefined behavior (because it has conflicting definitions of A, which violates the one-definition rule), but typical implementations will indeed break encapsulation, and the UB can be avoided by moving break_encapsulation to C or assembly, or replacing a.x = 1; with *reinterpret_cast<int&>(&a) = 1; and sharing the definition of A.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment