Safeguard
Application Security•

CWE-820 Missing Synchronization: Shared State Without a Lock

CWE-820 is a shared resource used by concurrent code with no synchronization at all. Here is why it matters for security and how to catch it.

Safeguard Security Team
3 min read

CWE-820 is simple to state. The product uses a shared resource in a concurrent way and makes no attempt to synchronize access to it. Two threads, requests or processes touch the same thing, and nothing says who goes first.

MITRE marks it as not language specific. Any concurrent system can have it: web servers handling parallel requests, background workers, mobile apps, native services.

How it happens

Concurrency bugs come from an assumption that code runs one step at a time. It does not. If a resource, such as a counter, a cache entry, a balance or a file, is read and changed by more than one actor, the state may not be what the code expects at the moment it uses it.

# Vulnerable: no lock around a check then update
balance = 100

def withdraw(amount):
    global balance
    if balance >= amount:
        balance -= amount   # two threads can both pass the check

Both threads see a balance of 100, both pass the check, and both subtract. The balance ends up lower than the rules allow.

What an attacker gains

MITRE says the shared resource may end up in an unexpected state, which leads to unexpected or insecure behavior, especially if an attacker can influence the shared resource. The record lists effects on integrity and confidentiality: modifying application data, reading application data and altering execution logic.

How to find it

The record has no detection methods for this entry, so rely on engineering practice. Review every variable, file or object reachable from more than one thread or request, and ask what protects it. Race detectors and thread sanitizers, where your toolchain offers them, can surface unsynchronized access during tests. Stress tests with many parallel callers expose timing bugs that single user testing never will.

How to fix it

MITRE's page lists no formal mitigations, so these are general practices.

  1. Identify shared mutable state and give it one owner.
  2. Protect check and update sequences with a lock, or use an atomic operation that does both in one step.
  3. Prefer immutable data and message passing where the design allows.
  4. For data stored in a database, use transactions or constraints so the database enforces the rule.
import threading
lock = threading.Lock()

def withdraw(amount):
    global balance
    with lock:
        if balance >= amount:
            balance -= amount

Quick check

How is CWE-820 different from CWE-821?

CWE-820 is no synchronization at all. CWE-821 is synchronization that is present but done incorrectly.

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.