Multiple Inheritance from Concrete Classes
Description
Multiple Inheritance from Concrete Classes occurs when a class inherits from more than one concrete (non-abstract) class. While some languages like C++ and Python support multiple inheritance, inheriting from multiple concrete classes creates complexity through potential diamond inheritance problems, method resolution ambiguities, and tight coupling to multiple implementations. This pattern makes code harder to understand, test, and maintain, and can lead to unexpected behavior when parent classes are modified.
Risk
While primarily a code quality issue, multiple inheritance from concrete classes has indirect security implications. The complexity introduced makes security audits more difficult as reviewers must understand multiple inheritance hierarchies. Method resolution order (MRO) ambiguities may cause security-critical methods to be called from unexpected parent classes. Changes to any parent class may unexpectedly affect security behavior. Testing becomes more complex, potentially leaving security issues undiscovered. The tight coupling to multiple implementations makes it difficult to isolate and secure individual components.
Solution
Favor composition over inheritance. Use interfaces (or pure abstract classes) for defining contracts and compose concrete implementations. If multiple inheritance is truly needed, inherit from at most one concrete class and use interfaces/abstract classes for additional capabilities. Apply the Interface Segregation Principle to keep interfaces focused. Use mixins carefully and ensure they don't carry mutable state. Consider using traits or protocols where language support exists. Document the inheritance hierarchy clearly when multiple inheritance is used.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Reduce Maintainability - Multiple inheritance from concrete classes creates complexity that makes the codebase harder to understand and maintain securely. |
| Other | Scope: Other Quality Degradation - Method resolution ambiguities and tight coupling may introduce subtle bugs including security vulnerabilities. |
Example Code
Vulnerable Code
// Vulnerable: Multiple inheritance from concrete classes in C++
// Concrete class 1
class FileLogger {
protected:
std::ofstream logFile;
std::string logPath;
public:
FileLogger(const std::string& path) : logPath(path) {
logFile.open(path, std::ios::app);
}
virtual void log(const std::string& message) {
logFile << "[LOG] " << message << std::endl;
}
virtual ~FileLogger() {
logFile.close();
}
};
// Concrete class 2
class NetworkClient {
protected:
int socket_fd;
std::string serverAddress;
public:
NetworkClient(const std::string& server) : serverAddress(server) {
connect();
}
virtual void send(const std::string& data) {
// Send data over network
}
virtual ~NetworkClient() {
close(socket_fd);
}
};
// Vulnerable: Inheriting from two concrete classes
class VulnerableRemoteLogger : public FileLogger, public NetworkClient {
public:
// Diamond problem potential if both parents share a common base
// Ambiguity: which parent's destructor is called first?
// Complexity: maintaining state from two parents
VulnerableRemoteLogger(const std::string& path, const std::string& server)
: FileLogger(path), NetworkClient(server) {}
void log(const std::string& message) override {
// Which parent's method takes precedence?
// What if both have conflicting state?
FileLogger::log(message);
NetworkClient::send(message);
}
// Problems:
// 1. Tight coupling to two concrete implementations
// 2. Both parents manage resources (files, sockets)
// 3. Destruction order may cause issues
// 4. Changes to either parent may break this class
};
# Vulnerable: Multiple inheritance from concrete classes in Python
class DatabaseConnection:
"""Concrete class with state and implementation"""
def __init__(self, connection_string):
self.connection_string = connection_string
self.connection = self._connect()
self._transaction_active = False
def _connect(self):
# Establish database connection
return create_connection(self.connection_string)
def execute(self, query):
return self.connection.execute(query)
def begin_transaction(self):
self._transaction_active = True
self.connection.begin()
class CacheManager:
"""Another concrete class with state"""
def __init__(self, cache_size):
self.cache = {}
self.max_size = cache_size
self._hits = 0
self._misses = 0
def get(self, key):
if key in self.cache:
self._hits += 1
return self.cache[key]
self._misses += 1
return None
def set(self, key, value):
if len(self.cache) >= self.max_size:
self._evict()
self.cache[key] = value
# Vulnerable: Inheriting from two concrete classes
class VulnerableCachedDatabase(DatabaseConnection, CacheManager):
"""
Problems:
1. Both parents have __init__ with different parameters
2. MRO complexity - which __init__ is called?
3. Both parents have internal state that may conflict
4. Changes to either parent class affects this class
"""
def __init__(self, connection_string, cache_size):
# Must explicitly call both parent constructors
DatabaseConnection.__init__(self, connection_string)
CacheManager.__init__(self, cache_size)
# Easy to forget one, leading to uninitialized state
def execute(self, query):
# Ambiguity: is this DatabaseConnection.execute?
# What if CacheManager also had an execute method?
cached = self.get(query) # From CacheManager
if cached:
return cached
result = DatabaseConnection.execute(self, query)
self.set(query, result)
return result
// Note: Java doesn't support multiple class inheritance, but shows the problem
// This example uses interfaces to demonstrate preferred approach
// Anti-pattern: If Java allowed multiple inheritance (hypothetical)
/*
class VulnerableService extends DatabaseHandler, CacheHandler {
// Would have all the problems of C++ multiple inheritance
}
*/
// Even with Java's interface approach, implementation inheritance via
// default methods can cause similar issues:
interface LoggerInterface {
default void log(String message) {
System.out.println("[LOG] " + message);
saveToFile(message); // Assumes method exists
}
void saveToFile(String message);
}
interface AuditInterface {
default void log(String message) {
System.out.println("[AUDIT] " + message);
saveToAuditTrail(message); // Assumes method exists
}
void saveToAuditTrail(String message);
}
// Vulnerable: Ambiguous default method
class VulnerableAuditLogger implements LoggerInterface, AuditInterface {
// Compiler error! Both interfaces have default log() method
// Must explicitly override to resolve ambiguity
@Override
public void log(String message) {
// Which one to call? Easy to make wrong choice
LoggerInterface.super.log(message); // Or AuditInterface.super.log?
}
@Override
public void saveToFile(String message) { /* ... */ }
@Override
public void saveToAuditTrail(String message) { /* ... */ }
}
Fixed Code
// Fixed: Composition over multiple inheritance
// Interface for logging
class ILogger {
public:
virtual void log(const std::string& message) = 0;
virtual ~ILogger() = default;
};
// Interface for network sending
class INetworkSender {
public:
virtual void send(const std::string& data) = 0;
virtual ~INetworkSender() = default;
};
// Concrete implementation of logger
class FileLogger : public ILogger {
private:
std::ofstream logFile;
public:
explicit FileLogger(const std::string& path) {
logFile.open(path, std::ios::app);
}
void log(const std::string& message) override {
logFile << "[LOG] " << message << std::endl;
}
};
// Concrete implementation of network sender
class NetworkClient : public INetworkSender {
private:
int socket_fd;
public:
explicit NetworkClient(const std::string& server) {
// Connect to server
}
void send(const std::string& data) override {
// Send data
}
};
// Fixed: Composition instead of multiple inheritance
class FixedRemoteLogger : public ILogger {
private:
std::unique_ptr<ILogger> localLogger;
std::unique_ptr<INetworkSender> networkSender;
public:
FixedRemoteLogger(
std::unique_ptr<ILogger> logger,
std::unique_ptr<INetworkSender> sender)
: localLogger(std::move(logger))
, networkSender(std::move(sender)) {}
void log(const std::string& message) override {
// Clear, predictable behavior
localLogger->log(message);
networkSender->send(message);
}
};
// Usage:
// auto logger = std::make_unique<FixedRemoteLogger>(
// std::make_unique<FileLogger>("/var/log/app.log"),
// std::make_unique<NetworkClient>("log-server:514")
// );
# Fixed: Composition over inheritance in Python
from abc import ABC, abstractmethod
from typing import Optional
# Interface/Protocol for database operations
class DatabaseInterface(ABC):
@abstractmethod
def execute(self, query: str) -> list:
pass
@abstractmethod
def begin_transaction(self) -> None:
pass
# Interface for caching
class CacheInterface(ABC):
@abstractmethod
def get(self, key: str) -> Optional[any]:
pass
@abstractmethod
def set(self, key: str, value: any) -> None:
pass
# Concrete database implementation
class DatabaseConnection(DatabaseInterface):
def __init__(self, connection_string: str):
self.connection_string = connection_string
self.connection = self._connect()
def _connect(self):
return create_connection(self.connection_string)
def execute(self, query: str) -> list:
return self.connection.execute(query)
def begin_transaction(self) -> None:
self.connection.begin()
# Concrete cache implementation
class CacheManager(CacheInterface):
def __init__(self, max_size: int):
self.cache = {}
self.max_size = max_size
def get(self, key: str) -> Optional[any]:
return self.cache.get(key)
def set(self, key: str, value: any) -> None:
if len(self.cache) >= self.max_size:
self._evict()
self.cache[key] = value
def _evict(self):
# LRU eviction
if self.cache:
self.cache.pop(next(iter(self.cache)))
# Fixed: Composition instead of multiple inheritance
class FixedCachedDatabase(DatabaseInterface):
"""Uses composition to combine database and cache functionality"""
def __init__(self,
database: DatabaseInterface,
cache: CacheInterface):
self._database = database
self._cache = cache
def execute(self, query: str) -> list:
# Check cache first
cached = self._cache.get(query)
if cached is not None:
return cached
# Execute and cache
result = self._database.execute(query)
self._cache.set(query, result)
return result
def begin_transaction(self) -> None:
self._database.begin_transaction()
# Usage with dependency injection
database = DatabaseConnection("postgresql://localhost/mydb")
cache = CacheManager(max_size=1000)
cached_db = FixedCachedDatabase(database, cache)
// Fixed: Proper interface-based design in Java
// Separate, focused interfaces
interface Logger {
void log(String message);
}
interface AuditTrail {
void audit(String event, String details);
}
// Concrete implementations
class FileLogger implements Logger {
private final Path logPath;
public FileLogger(Path logPath) {
this.logPath = logPath;
}
@Override
public void log(String message) {
// Write to file
}
}
class DatabaseAuditTrail implements AuditTrail {
private final DataSource dataSource;
public DatabaseAuditTrail(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void audit(String event, String details) {
// Write to database
}
}
// Fixed: Composition to combine capabilities
class FixedAuditingLogger implements Logger, AuditTrail {
private final Logger logger;
private final AuditTrail auditTrail;
public FixedAuditingLogger(Logger logger, AuditTrail auditTrail) {
this.logger = logger;
this.auditTrail = auditTrail;
}
@Override
public void log(String message) {
logger.log(message);
}
@Override
public void audit(String event, String details) {
auditTrail.audit(event, details);
}
// Combined functionality
public void logAndAudit(String event, String message) {
log(message);
audit(event, message);
}
}
CVE Examples
This CWE is marked as PROHIBITED for direct CVE mapping as it represents a code quality concern rather than a direct security vulnerability.
Related CWEs
- CWE-1093: Excessively Complex Data Representation (parent)
- CWE-1226: Complexity Issues (category member)
- CWE-1120: Excessive Code Complexity (related)
References
- MITRE Corporation. "CWE-1055: Multiple Inheritance from Concrete Classes." https://cwe.mitre.org/data/definitions/1055.html
- Gamma, E. et al. "Design Patterns: Elements of Reusable Object-Oriented Software."
- Martin, Robert C. "Clean Architecture."