Singleton Class Instance Creation without Proper Locking or Synchronization

Description

Singleton Class Instance Creation without Proper Locking or Synchronization occurs when a product implements the Singleton design pattern but fails to use appropriate locking or synchronization mechanisms to ensure the singleton class is instantiated only once. In multi-threaded environments, multiple threads may simultaneously attempt to create the singleton instance, resulting in multiple instances being created. This violates the singleton pattern's fundamental guarantee and can lead to inconsistent state, resource leaks, and race conditions.

Risk

Missing synchronization in singleton creation has security implications. Multiple singleton instances can hold inconsistent state, leading to security bypasses. Race conditions during instantiation can corrupt shared data. Resource initialization may occur multiple times, causing leaks. Security-critical singletons (like security managers) may have multiple conflicting instances. Configuration singletons may have different configurations in different parts of the application. The unpredictable behavior in concurrent scenarios can be exploited. Deadlock or livelock conditions may occur with incorrect synchronization.

Solution

Use proper synchronization mechanisms when implementing singletons. In Java, use static initialization (eager singleton), synchronized methods, or double-checked locking with volatile. In C++, use local static variables (guaranteed thread-safe since C++11) or proper mutex locking. Consider using framework-provided singleton implementations. Use the enum singleton pattern in Java for simplicity and thread safety. Apply static analysis tools to detect unsynchronized singletons. Test singleton implementations under concurrent load. Document thread-safety guarantees for singleton classes.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Unexpected State - Multiple instances may hold different state, causing inconsistencies.
AvailabilityScope: Availability

DoS: Resource Consumption - Resources may be allocated multiple times.
OtherScope: Other

Reduce Reliability - Race conditions make behavior unpredictable.

Example Code

Vulnerable Code

// Vulnerable: Classic unsynchronized singleton
public class VulnerableSingleton {
    private static VulnerableSingleton instance;
    private Configuration config;

    private VulnerableSingleton() {
        // Expensive initialization
        config = loadConfiguration();
    }

    // Vulnerable: No synchronization!
    public static VulnerableSingleton getInstance() {
        if (instance == null) {  // Thread A checks: null
            // Thread B also checks: null (race condition!)
            instance = new VulnerableSingleton();
            // Both threads create instances!
        }
        return instance;
    }

    // Result: Multiple instances with potentially different configurations
}

// Vulnerable: Broken double-checked locking (pre-Java 5)
public class VulnerableDoubleChecked {
    private static VulnerableDoubleChecked instance;  // Not volatile!

    private VulnerableDoubleChecked() {
        initializeResources();
    }

    public static VulnerableDoubleChecked getInstance() {
        if (instance == null) {
            synchronized (VulnerableDoubleChecked.class) {
                if (instance == null) {
                    // Vulnerable: Without volatile, another thread may see
                    // partially constructed object due to reordering
                    instance = new VulnerableDoubleChecked();
                }
            }
        }
        return instance;
    }
}

// Vulnerable: Security manager singleton without synchronization
public class VulnerableSecurityManager {
    private static VulnerableSecurityManager instance;
    private Set<String> permissions;
    private boolean initialized = false;

    private VulnerableSecurityManager() {
        permissions = new HashSet<>();
        loadPermissions();
        initialized = true;
    }

    public static VulnerableSecurityManager getInstance() {
        if (instance == null) {
            instance = new VulnerableSecurityManager();
        }
        return instance;
    }

    public boolean hasPermission(String permission) {
        // Vulnerable: May access uninitialized permissions
        // if another thread gets partially constructed instance
        return permissions.contains(permission);
    }
}
# Vulnerable: Python singleton without thread safety
class VulnerableSingleton:
    _instance = None

    def __new__(cls):
        # Vulnerable: Race condition between check and assignment
        if cls._instance is None:
            # Thread context switch here could cause multiple instances
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if not self._initialized:
            # Vulnerable: Initialization also has race condition
            self.config = self._load_config()
            self.connections = []
            self._initialized = True


# Vulnerable: Module-level singleton pattern with lazy init
class VulnerableConnectionPool:
    _instance = None

    @classmethod
    def get_instance(cls):
        # Vulnerable: No lock protection
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    def __init__(self):
        self.pool = []
        self._create_connections()

    def _create_connections(self):
        # Each instance creates its own connections
        for _ in range(10):
            self.pool.append(create_connection())
        # Multiple instances = connection leak!
// Vulnerable: C++ singleton without synchronization
class VulnerableSingleton {
private:
    static VulnerableSingleton* instance;
    Config* config;

    VulnerableSingleton() {
        config = new Config();
        config->load();
    }

public:
    // Vulnerable: Classic race condition
    static VulnerableSingleton* getInstance() {
        if (instance == nullptr) {  // Race condition!
            instance = new VulnerableSingleton();
        }
        return instance;
    }

    ~VulnerableSingleton() {
        delete config;
    }
};

VulnerableSingleton* VulnerableSingleton::instance = nullptr;

// Vulnerable: Broken double-checked locking in C++03
class VulnerableDCL {
private:
    static VulnerableDCL* instance;
    static std::mutex mtx;

public:
    static VulnerableDCL* getInstance() {
        if (instance == nullptr) {  // First check without lock
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {
                // Vulnerable in C++03: No memory barrier
                // Another thread may see uninitialized object
                instance = new VulnerableDCL();
            }
        }
        return instance;
    }
};

Fixed Code

// Fixed: Multiple correct approaches in Java

// Approach 1: Eager initialization (simplest)
public class EagerSingleton {
    // Created at class loading time - thread safe
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {
        initializeResources();
    }

    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

// Approach 2: Initialization-on-demand holder (lazy + thread safe)
public class HolderSingleton {
    private HolderSingleton() {
        initializeResources();
    }

    // Inner class not loaded until getInstance() called
    private static class Holder {
        static final HolderSingleton INSTANCE = new HolderSingleton();
    }

    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;  // Class loading is thread safe
    }
}

// Approach 3: Enum singleton (recommended by Joshua Bloch)
public enum EnumSingleton {
    INSTANCE;

    private final Configuration config;

    EnumSingleton() {
        config = loadConfiguration();
    }

    public Configuration getConfig() {
        return config;
    }
}

// Approach 4: Correct double-checked locking (Java 5+)
public class FixedDoubleChecked {
    // volatile is REQUIRED for correct behavior
    private static volatile FixedDoubleChecked instance;

    private FixedDoubleChecked() {
        initializeResources();
    }

    public static FixedDoubleChecked getInstance() {
        // Local variable improves performance
        FixedDoubleChecked localRef = instance;
        if (localRef == null) {
            synchronized (FixedDoubleChecked.class) {
                localRef = instance;
                if (localRef == null) {
                    instance = localRef = new FixedDoubleChecked();
                }
            }
        }
        return localRef;
    }
}

// Approach 5: Thread-safe security manager
public class FixedSecurityManager {
    private static volatile FixedSecurityManager instance;
    private final Set<String> permissions;

    private FixedSecurityManager() {
        Set<String> perms = new HashSet<>();
        loadPermissions(perms);
        // Immutable view for thread safety
        permissions = Collections.unmodifiableSet(perms);
    }

    public static FixedSecurityManager getInstance() {
        if (instance == null) {
            synchronized (FixedSecurityManager.class) {
                if (instance == null) {
                    instance = new FixedSecurityManager();
                }
            }
        }
        return instance;
    }

    public boolean hasPermission(String permission) {
        return permissions.contains(permission);
    }
}
# Fixed: Thread-safe singleton patterns in Python
import threading
from functools import lru_cache


# Approach 1: Module-level instance (Python modules are singletons)
# my_singleton.py
class _Singleton:
    def __init__(self):
        self.config = self._load_config()

    def _load_config(self):
        return {"setting": "value"}

# Module-level instance - created once on import
singleton_instance = _Singleton()


# Approach 2: Thread-safe lazy singleton with lock
class ThreadSafeSingleton:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls):
        if cls._instance is None:
            with cls._lock:  # Acquire lock
                # Double-check inside lock
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        # Prevent re-initialization
        if self._initialized:
            return
        with self._lock:
            if not self._initialized:
                self.config = self._load_config()
                self._initialized = True


# Approach 3: Using metaclass
class SingletonMeta(type):
    _instances = {}
    _lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            with cls._lock:
                if cls not in cls._instances:
                    instance = super().__call__(*args, **kwargs)
                    cls._instances[cls] = instance
        return cls._instances[cls]


class MySingleton(metaclass=SingletonMeta):
    def __init__(self):
        self.config = {}


# Approach 4: Using lru_cache decorator
class ConfigManager:
    def __init__(self):
        self.settings = self._load_settings()

    def _load_settings(self):
        return {"db": "postgres://..."}


@lru_cache(maxsize=1)
def get_config_manager() -> ConfigManager:
    """Thread-safe singleton via lru_cache."""
    return ConfigManager()


# Approach 5: Thread-safe connection pool
class FixedConnectionPool:
    _instance = None
    _lock = threading.Lock()

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = cls()
        return cls._instance

    def __init__(self):
        self._pool_lock = threading.Lock()
        self.pool = []
        self._create_connections()

    def _create_connections(self):
        for _ in range(10):
            self.pool.append(create_connection())

    def get_connection(self):
        with self._pool_lock:
            if self.pool:
                return self.pool.pop()
        return create_connection()  # Fallback
// Fixed: Thread-safe singleton patterns in C++

// Approach 1: Meyers' Singleton (C++11 and later - guaranteed thread safe)
class MeyersSingleton {
public:
    static MeyersSingleton& getInstance() {
        // C++11 guarantees this is thread safe
        static MeyersSingleton instance;
        return instance;
    }

    // Delete copy and move
    MeyersSingleton(const MeyersSingleton&) = delete;
    MeyersSingleton& operator=(const MeyersSingleton&) = delete;

private:
    MeyersSingleton() {
        config = std::make_unique<Config>();
        config->load();
    }

    std::unique_ptr<Config> config;
};

// Approach 2: Double-checked locking with atomic (C++11)
class AtomicSingleton {
private:
    static std::atomic<AtomicSingleton*> instance;
    static std::mutex mtx;
    Config* config;

    AtomicSingleton() {
        config = new Config();
        config->load();
    }

public:
    static AtomicSingleton* getInstance() {
        AtomicSingleton* tmp = instance.load(std::memory_order_acquire);
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> lock(mtx);
            tmp = instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new AtomicSingleton();
                instance.store(tmp, std::memory_order_release);
            }
        }
        return tmp;
    }

    ~AtomicSingleton() {
        delete config;
    }
};

std::atomic<AtomicSingleton*> AtomicSingleton::instance{nullptr};
std::mutex AtomicSingleton::mtx;

// Approach 3: call_once (C++11)
class CallOnceSingleton {
private:
    static std::unique_ptr<CallOnceSingleton> instance;
    static std::once_flag initFlag;

    CallOnceSingleton() {
        // Initialize
    }

public:
    static CallOnceSingleton& getInstance() {
        std::call_once(initFlag, []() {
            instance.reset(new CallOnceSingleton());
        });
        return *instance;
    }
};

std::unique_ptr<CallOnceSingleton> CallOnceSingleton::instance;
std::once_flag CallOnceSingleton::initFlag;

// Approach 4: Template singleton for reuse
template<typename T>
class Singleton {
public:
    static T& getInstance() {
        static T instance;  // Thread-safe in C++11
        return instance;
    }

protected:
    Singleton() = default;
    ~Singleton() = default;

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

// Usage
class ConfigManager : public Singleton<ConfigManager> {
    friend class Singleton<ConfigManager>;

private:
    ConfigManager() {
        // Load config
    }

public:
    std::string getSetting(const std::string& key);
};

CVE Examples

Race conditions in singleton creation have contributed to various vulnerabilities, particularly in security-critical components where multiple instances could lead to inconsistent security state.


  • CWE-820: Missing Synchronization (parent)
  • CWE-662: Improper Synchronization (broader category)
  • CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition (related)

References

  1. MITRE Corporation. "CWE-1096: Singleton Class Instance Creation without Proper Locking or Synchronization." https://cwe.mitre.org/data/definitions/1096.html
  2. Bloch, Joshua. "Effective Java" - Item 3: Enforce the singleton property with a private constructor or an enum type.
  3. Meyers, Scott. "Effective C++" - Singleton implementation.