Safeguard
Application Security•

CWE-1096 Singleton Creation Without Synchronization

CWE-1096 is a Singleton that can be created twice when two threads race. Duplicates, partial objects and deadlocks follow. Here is the broken pattern and the fix.

Safeguard Security Team
4 min read

CWE-1096, Singleton Class Instance Creation without Proper Locking or Synchronization, applies when a product implements the Singleton design pattern but does not use a proper locking or other synchronization mechanism to ensure the class is instantiated only once.

It is a Variant level entry, specific enough that MITRE allows it for mapping real vulnerabilities. It affects any language with threads, including Java, C#, C++, Python and Go.

How it happens

The Singleton pattern says there is one shared instance. The usual lazy version checks whether the instance exists and creates it if not. With one thread that is fine. With two, both can see "not yet created" at the same moment, and both create one.

// Vulnerable pattern: check then create, with no synchronization
public class Config {
    private static Config instance;

    public static Config get() {
        if (instance == null) {
            instance = new Config();   // two threads can both reach this line
        }
        return instance;
    }
}

Now two threads hold different Config objects. If the class holds state, such as a connection pool or an in memory cache of permissions, each thread sees a different view. A thread can also read a reference to an object whose constructor is still running.

The simplest fix is to let the language create the instance safely.

// Fix 1: initialization on demand holder, thread safe by class loading rules
public class Config {
    private Config() {}

    private static class Holder {
        static final Config INSTANCE = new Config();
    }

    public static Config get() {
        return Holder.INSTANCE;
    }
}

If you must keep lazy creation with explicit locking, guard it properly.

// Fix 2: double checked locking, which needs a volatile field
public class Config {
    private static volatile Config instance;

    public static Config get() {
        Config local = instance;
        if (local == null) {
            synchronized (Config.class) {
                local = instance;
                if (local == null) {
                    instance = local = new Config();
                }
            }
        }
        return local;
    }
}

Without volatile, double checked locking is itself broken in Java, which is a good reminder that clever locking is easy to get wrong.

What an attacker gains

MITRE lists reduced reliability: the instantiation can be non thread safe, introducing deadlock or livelock conditions, and if the code is reachable by an attacker the reliability problem might introduce a vulnerability.

The practical risk is a race that an attacker can provoke by sending many concurrent requests. If the singleton holds security relevant state, such as a loaded policy or a key store, two copies can disagree about who is allowed to do what. A stalled or deadlocked service is a denial of service.

How to find it

The record gives no detection methods for this entry, so use general techniques.

  • Search the code base for static instance fields with a null check in an accessor.
  • Review any shared lazy initialization for locking.
  • Use thread sanitizers and race detectors in tests, such as ThreadSanitizer for native code or the Go race detector.
  • Stress test startup with many threads calling the accessor at once.

How to fix it

  1. Prefer eager initialization when creation is cheap and safe.
  2. Use the language's built in safe lazy mechanism: holder idiom in Java, Lazy in C#, std::call_once or a function local static in C++11 and later, sync.Once in Go.
  3. If you write explicit locking, keep the locked section small and make the shared field volatile or atomic where the language requires it.
  4. Question whether you need a singleton. Passing a dependency in explicitly avoids the problem and makes testing easier.
  5. Document which objects are shared across threads.

Try it yourself

Run a sample code scan and see how static analysis reports concurrency and logic findings.

Click through it at your own pace, or open it full screen.

Quick check

Why can a Singleton be created twice?

Two threads can both check that the instance is empty before either has stored a new one, so each creates its own.

What is the safest way to write a lazy Singleton?

Use a language provided mechanism designed for it, such as the holder idiom in Java or call once in C++, rather than hand written locking.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.