Safeguard
Application Security•

CWE-543 Singleton Without Synchronization: Thread Safety Bugs

A lazily created singleton in Java or C++ can be built twice or seen half built. How CWE-543 happens, with fixes that actually hold.

Safeguard Security Team
4 min read

CWE-543, Use of Singleton Pattern Without Synchronization in a Multithreaded Context, is a concurrency bug. MITRE says the product uses the singleton pattern when creating a resource in a multithreaded environment, and notes that the singleton pattern may not be thread-safe. The record lists Java and C++ as affected platforms.

It is not a flashy weakness. It produces the kind of failure that appears under load, cannot be reproduced on a laptop, and corrupts state quietly.

How it happens

A singleton is one shared instance of a class. The common lazy version creates it on first use:

// Vulnerable: two threads can both see null and both construct
public class Config {
    private static Config instance;

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

With one thread, this works. With two, both can pass the null check before either assigns, so two instances exist. Code that assumed there is only one now has two, each with its own state.

The obvious repair is double-checked locking, which avoids the cost of a synchronized call on every access. MITRE warns about it directly. In certain versions of Java it has been shown to be unsafe because it still introduces a race condition. MITRE rates this mitigation as having limited effectiveness for that reason.

A related mistake in servlets: MITRE says not to use member fields to store information in the servlet. In multithreaded environments, storing user data in servlet member fields introduces a data access race condition. A servlet instance is shared between requests, so a member field is shared too. One user's data can show up in another user's response.

// Vulnerable: one servlet instance serves many threads
public class AccountServlet extends HttpServlet {
    private String currentUser;   // shared between requests

    protected void doGet(HttpServletRequest req, HttpServletResponse res) {
        currentUser = req.getParameter("user");
        // another request can overwrite currentUser before this one finishes
    }
}

What an attacker gains

MITRE lists integrity impacts: modifying application data, along with other effects. The security angle is state confusion. If a shared object holds a user, a permission or a setting, a race can make it hold the wrong one at the wrong moment. An attacker who can send many parallel requests has a way to try for that moment.

How to find it

  • Search for static fields that hold lazily created objects, and for getInstance methods without synchronization.
  • Search servlets for instance fields that store request or user data.
  • Look for double-checked locking in code that targets older Java versions.
  • MITRE notes static analysis can find some instances of this weakness.
  • Test with concurrent requests. A load test that checks responses for the right user is a simple probe.

How to fix it

MITRE's mitigations:

  1. Use the Thread-Specific Storage Pattern, so each thread has its own copy of per-request data.
  2. Do not use member fields to store information in the servlet.
  3. Avoid double-checked locking in language versions that cannot guarantee thread safety.

For the singleton itself, the standard safe choices are to initialize eagerly, use a holder class that the language loads once, or synchronize the accessor:

// Safer: initialization-on-demand holder
public class Config {
    private Config() {}

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

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

For servlet data, keep values in local variables inside the method, so each request has its own copy.

Quick check

Why is a servlet member field risky?

One servlet instance handles many requests at once, so the field is shared. Data from one request can overwrite or leak into another.

Is double-checked locking a safe fix?

Not in every Java version. MITRE says it can still introduce a race condition and rates the mitigation as limited.

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.