Improper Preservation of Consistency Between Independent Representations of Shared State
Description
Improper Preservation of Consistency Between Independent Representations of Shared State occurs when a product maintains multiple distributed components that each keep local copies of shared data but fails to ensure all copies remain synchronized across the system. In distributed environments and systems with independent physical components, each typically stores its own copy of critical data like state or cache. All components must maintain the same "view" to operate in coordination. Examples include social media platforms where users on different hosts need identical data, or processors with shadow Memory Management Units (MMUs) that must have matching accessible memory ranges.
Risk
Inconsistent state maintenance creates severe security implications. Components may operate under incorrect assumptions. Access control decisions may become inconsistent. Security tokens may be duplicated or invalidated incorrectly. Cache coherence issues may lead to privilege escalation. Transactions may execute out of order. Users may access stale or incorrect data. Attackers may exploit race conditions during synchronization windows. System integrity may be compromised.
Solution
Minimize out-of-sync time periods and make the update process as robust as possible. Implement strong consistency protocols for distributed components. Use transactional updates with atomic operations. Implement proper cache coherence protocols. Prioritize security-critical updates over general traffic. Add verification after state changes propagate. Implement conflict detection and resolution mechanisms. Use distributed consensus algorithms when necessary.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Unexpected State - One or more components/subsystems could operate under incorrect assumptions about the actual system state. |
Example Code
Vulnerable Code
# Vulnerable: Distributed cache without synchronization
import threading
import time
class VulnerableCacheNode:
def __init__(self, node_id):
self.node_id = node_id
self.local_cache = {}
self.version = 0
def get(self, key):
# VULNERABLE: Returns local copy without checking freshness
return self.local_cache.get(key)
def set(self, key, value):
# VULNERABLE: Updates local copy only
# No synchronization with other nodes
self.local_cache[key] = value
self.version += 1
def receive_update(self, key, value, version):
# VULNERABLE: No ordering guarantee
# Race condition: updates may arrive out of order
self.local_cache[key] = value
# Vulnerable: User permissions without consistency
class VulnerablePermissionSystem:
def __init__(self):
self.nodes = []
def revoke_permission(self, user_id, permission):
# VULNERABLE: Broadcasts revocation but doesn't wait for confirmation
for node in self.nodes:
# Fire and forget - no guarantee all nodes received update
threading.Thread(target=node.remove_permission,
args=(user_id, permission)).start()
# VULNERABLE: Returns immediately before propagation completes
# User may still have permission on some nodes
def check_permission(self, node, user_id, permission):
# VULNERABLE: Each node has potentially stale permission data
return node.has_permission(user_id, permission)
# Attack: Exploiting synchronization delay
def exploit_sync_delay():
system = VulnerablePermissionSystem()
# Admin revokes attacker's permission
system.revoke_permission("attacker", "admin_access")
# Attacker immediately tries all nodes
# Some nodes haven't received revocation yet
for node in system.nodes:
if system.check_permission(node, "attacker", "admin_access"):
# ATTACK: Found a node with stale permissions
perform_admin_action(node)
break
// Vulnerable: Shadow MMU without proper synchronization
module vulnerable_shadow_mmu (
input wire clk,
input wire reset_n,
input wire [31:0] update_addr,
input wire [31:0] update_data,
input wire update_valid,
input wire [31:0] check_addr,
output reg access_allowed
);
// Local copy of access permissions
reg [31:0] permission_table [0:255];
// VULNERABLE: No synchronization with master MMU
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
// Initialize permissions
integer i;
for (i = 0; i < 256; i = i + 1) begin
permission_table[i] <= 32'h0;
end
end
else if (update_valid) begin
// VULNERABLE: Updates arrive without ordering
// Race condition with access checks
permission_table[update_addr[7:0]] <= update_data;
end
end
// VULNERABLE: Check against potentially stale data
always @(*) begin
access_allowed = permission_table[check_addr[7:0]][0];
end
// No mechanism to:
// - Verify update came from legitimate source
// - Ensure all shadow MMUs received same update
// - Detect inconsistency between master and shadow
endmodule
// Vulnerable: Distributed session store without consistency
public class VulnerableSessionStore {
private Map<String, Session> localSessions = new ConcurrentHashMap<>();
private List<VulnerableSessionStore> peers;
public void invalidateSession(String sessionId) {
// Remove locally
localSessions.remove(sessionId);
// VULNERABLE: Async broadcast to peers without confirmation
for (VulnerableSessionStore peer : peers) {
CompletableFuture.runAsync(() -> {
peer.receiveInvalidation(sessionId);
});
}
// VULNERABLE: Returns before all peers confirmed invalidation
}
public boolean isSessionValid(String sessionId) {
// VULNERABLE: Only checks local copy
// May return true for invalidated session if update hasn't arrived
return localSessions.containsKey(sessionId);
}
public void receiveInvalidation(String sessionId) {
// VULNERABLE: No ordering guarantee
// Network delay could allow stale session to remain valid
localSessions.remove(sessionId);
}
}
Fixed Code
# Fixed: Distributed cache with strong consistency
import threading
import time
from collections import defaultdict
class SecureCacheNode:
def __init__(self, node_id, coordinator):
self.node_id = node_id
self.local_cache = {}
self.versions = {}
self.coordinator = coordinator
self.lock = threading.Lock()
def get(self, key):
with self.lock:
# FIXED: Check version with coordinator before returning
local_version = self.versions.get(key, 0)
current_version = self.coordinator.get_current_version(key)
if local_version < current_version:
# Stale data - fetch fresh copy
self.local_cache[key], self.versions[key] = \
self.coordinator.get_authoritative(key)
return self.local_cache.get(key)
def set(self, key, value):
# FIXED: Coordinate update through central authority
success = self.coordinator.request_update(key, value, self.node_id)
if success:
with self.lock:
self.local_cache[key] = value
self.versions[key] = self.coordinator.get_current_version(key)
return success
# Fixed: Permission system with strong consistency
class SecurePermissionSystem:
def __init__(self):
self.nodes = []
self.pending_revocations = {}
self.lock = threading.Lock()
def revoke_permission(self, user_id, permission):
revocation_id = generate_unique_id()
with self.lock:
# Track pending revocation
self.pending_revocations[revocation_id] = {
'user_id': user_id,
'permission': permission,
'confirmed_nodes': set(),
'total_nodes': len(self.nodes)
}
# FIXED: Wait for all nodes to confirm
confirmation_events = []
for node in self.nodes:
event = threading.Event()
confirmation_events.append(event)
node.remove_permission_with_ack(
user_id, permission, revocation_id, event)
# FIXED: Block until all nodes confirmed
all_confirmed = all(event.wait(timeout=30) for event in confirmation_events)
if not all_confirmed:
# Handle partial propagation - fail secure
self.emergency_revoke(user_id, permission)
raise SynchronizationError("Revocation failed to propagate to all nodes")
with self.lock:
del self.pending_revocations[revocation_id]
def check_permission(self, node, user_id, permission):
with self.lock:
# FIXED: Check for pending revocations first
for revocation in self.pending_revocations.values():
if (revocation['user_id'] == user_id and
revocation['permission'] == permission):
# Revocation in progress - deny access
return False
return node.has_permission(user_id, permission)
def emergency_revoke(self, user_id, permission):
# Block all access while synchronization is in progress
for node in self.nodes:
node.block_user(user_id)
// Fixed: Shadow MMU with synchronization verification
module secure_shadow_mmu (
input wire clk,
input wire reset_n,
input wire [31:0] update_addr,
input wire [31:0] update_data,
input wire [31:0] update_sequence, // Sequence number for ordering
input wire update_valid,
input wire [31:0] master_checksum, // Checksum from master MMU
input wire [31:0] check_addr,
output reg access_allowed,
output reg sync_error
);
reg [31:0] permission_table [0:255];
reg [31:0] expected_sequence;
reg [31:0] local_checksum;
// FIXED: Track synchronization state
reg synchronized;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
integer i;
for (i = 0; i < 256; i = i + 1) begin
permission_table[i] <= 32'h0;
end
expected_sequence <= 32'h0;
local_checksum <= 32'h0;
synchronized <= 1'b1;
sync_error <= 1'b0;
end
else if (update_valid) begin
// FIXED: Verify update sequence
if (update_sequence == expected_sequence) begin
permission_table[update_addr[7:0]] <= update_data;
expected_sequence <= expected_sequence + 1;
// Update checksum
local_checksum <= local_checksum ^ update_data;
end
else if (update_sequence > expected_sequence) begin
// FIXED: Missed update - mark as out of sync
synchronized <= 1'b0;
sync_error <= 1'b1;
end
// Ignore duplicate/old updates
end
end
// FIXED: Periodic consistency check
always @(posedge clk) begin
if (synchronized && (local_checksum != master_checksum)) begin
// Checksum mismatch - out of sync
synchronized <= 1'b0;
sync_error <= 1'b1;
end
end
// FIXED: Deny access when not synchronized (fail-secure)
always @(*) begin
if (!synchronized) begin
access_allowed = 1'b0; // Fail secure when out of sync
end
else begin
access_allowed = permission_table[check_addr[7:0]][0];
end
end
endmodule
// Fixed: Distributed session store with strong consistency
public class SecureSessionStore {
private Map<String, Session> localSessions = new ConcurrentHashMap<>();
private Map<String, Long> sessionVersions = new ConcurrentHashMap<>();
private List<SecureSessionStore> peers;
private final Object syncLock = new Object();
public void invalidateSession(String sessionId) throws SynchronizationException {
long invalidationVersion = System.nanoTime();
// FIXED: Use two-phase commit for session invalidation
List<CompletableFuture<Boolean>> preparePhase = new ArrayList<>();
// Phase 1: Prepare
for (SecureSessionStore peer : peers) {
preparePhase.add(CompletableFuture.supplyAsync(() ->
peer.prepareInvalidation(sessionId, invalidationVersion)));
}
// Wait for all peers to acknowledge prepare
boolean allPrepared = preparePhase.stream()
.allMatch(future -> {
try {
return future.get(10, TimeUnit.SECONDS);
} catch (Exception e) {
return false;
}
});
if (!allPrepared) {
// Abort - roll back
for (SecureSessionStore peer : peers) {
peer.abortInvalidation(sessionId);
}
throw new SynchronizationException("Failed to prepare all nodes");
}
// Phase 2: Commit
List<CompletableFuture<Void>> commitPhase = new ArrayList<>();
for (SecureSessionStore peer : peers) {
commitPhase.add(CompletableFuture.runAsync(() ->
peer.commitInvalidation(sessionId, invalidationVersion)));
}
// Remove locally after all commits
CompletableFuture.allOf(commitPhase.toArray(new CompletableFuture[0])).join();
localSessions.remove(sessionId);
sessionVersions.put(sessionId, invalidationVersion);
}
public boolean isSessionValid(String sessionId) {
synchronized (syncLock) {
// FIXED: Check if invalidation is in progress
if (pendingInvalidations.contains(sessionId)) {
return false;
}
// FIXED: Verify local version matches quorum
Long localVersion = sessionVersions.get(sessionId);
if (!verifyVersionWithQuorum(sessionId, localVersion)) {
// Version mismatch - refresh from peers
refreshSession(sessionId);
}
return localSessions.containsKey(sessionId);
}
}
private boolean verifyVersionWithQuorum(String sessionId, Long localVersion) {
int matches = 0;
int quorum = (peers.size() / 2) + 1;
for (SecureSessionStore peer : peers) {
if (Objects.equals(peer.getVersion(sessionId), localVersion)) {
matches++;
if (matches >= quorum) {
return true;
}
}
}
return false;
}
}
CVE Examples
State consistency vulnerabilities have been found in various distributed systems including cloud platforms, CDN networks, and multi-processor systems where attackers exploited synchronization windows to bypass security controls.
Related CWEs
- CWE-664: Improper Control of a Resource Through its Lifetime (parent)
- CWE-1249: Application-Level Admin Tool with Inconsistent View (child)
- CWE-1251: Mirrored Regions with Different Values (child)
- CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (related)
References
- MITRE Corporation. "CWE-1250: Improper Preservation of Consistency Between Independent Representations of Shared State." https://cwe.mitre.org/data/definitions/1250.html
- Lamport, Leslie. "The Part-Time Parliament" (Paxos Consensus)
- Ongaro, Diego. "In Search of an Understandable Consensus Algorithm" (Raft)