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

ImpactDetails
OtherScope: Other

Reduce Maintainability - Multiple inheritance from concrete classes creates complexity that makes the codebase harder to understand and maintain securely.
OtherScope: 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.


  • CWE-1093: Excessively Complex Data Representation (parent)
  • CWE-1226: Complexity Issues (category member)
  • CWE-1120: Excessive Code Complexity (related)

References

  1. MITRE Corporation. "CWE-1055: Multiple Inheritance from Concrete Classes." https://cwe.mitre.org/data/definitions/1055.html
  2. Gamma, E. et al. "Design Patterns: Elements of Reusable Object-Oriented Software."
  3. Martin, Robert C. "Clean Architecture."