Author: Nihal, V12
Published: August 26, 2026
Source: https://v12.sh/blog/signal
Summary
Researchers at V12 report two critical object-lifetime vulnerabilities in Signal’s Contact Discovery Service (CDSI) that let the untrusted host server break the Intel SGX enclave boundary the service relies on for privacy. CDSI lets a Signal client learn which of its phone contacts use Signal without revealing its address book to Signal’s servers, by processing queries inside an attested SGX enclave whose memory is meant to be unreadable even to the machine operator. The first bug yields an arbitrary enclave-memory read; the second yields full control of the enclave’s register context and code execution inside the enclave. The team demonstrated both on real SGX hardware matching the Azure machine types in Signal’s production deployment, extracting the enclave’s Noise responder private key and confirming it against the attested public key — a full enclave compromise. No CVE is assigned. Both issues were responsibly disclosed to Signal and have been fixed. Importantly, this is a threat-model attack by (or against) the party that controls the server host — it is not a remote attack on Signal clients, and it does not concern end-to-end-encrypted message content; the asset at risk is the confidentiality of contact-discovery queries (users’ address books) that SGX was supposed to shield from the server operator.
Technical Details
Threat model and primitives. In SGX the host operator cannot read enclave memory or alter trusted code, but it still controls scheduling, page mappings, and object lifetime/provisioning, and it can clear page-permission bits to force page faults that pause an enclave thread at a chosen instruction. That pausing turns rare race conditions into deterministic ones and simplifies heap grooming — the enabling primitive behind both bugs.
Vulnerability 1 — duplicate shard-worker use-after-free → arbitrary read. CDSI parallelizes ORAM queries across shards, each shard served by one long-running worker draining a FIFO queue. A query enqueues a lookup then a wait; because a single in-order worker always processes the wait after the lookup, wait-completion is used as the signal that the result buffer can be freed. The enclave_run_shard() ECALL, however, does not check whether a shard already has a worker, so a malicious host can start a duplicate worker. With two workers, the second can consume the wait while the first is still processing the lookup, so the first later writes its result into an already-freed buffer (a use-after-free stale write). The researchers chain that stale write into an arbitrary read by steering it to overwrite a protobuf bytes field’s pointer and length, and use the resulting read to extract the 32-byte Noise responder private key.
Vulnerability 2 — client-handle TOCTOU → code execution. The client_get() routine validates a canary and acquires the client’s state non-atomically (a separate check and compare-and-swap), leaving a time-of-check-to-time-of-use window. The host pauses the thread inside that window (via the page-fault primitive), frees the client, and allocates a replacement object under its control. The replacement’s send/recv fields point at attacker-crafted NoiseCipherState structures whose function pointers target oe_continue_execution, giving the host control of the enclave’s register context and thus code execution within the trusted context. The write-up includes proof-of-concept code; this summary describes the mechanisms at a conceptual level rather than reproducing it.
Impact
Either bug defeats the core guarantee CDSI exists to provide: that the server operator cannot see users’ contact-discovery queries. Extracting the enclave’s Noise private key lets a malicious (or compromised) host impersonate the enclave to clients and decrypt their queries — i.e. recover the address-book data that SGX was meant to keep private — and the second bug additionally grants arbitrary code execution inside the enclave. Because the attack is exercised by whoever controls the host, the practical risk is a malicious insider at the service operator or an attacker who has compromised the server infrastructure; it is not something a remote peer or ordinary network attacker can trigger against a user’s device, and it does not expose Signal’s end-to-end-encrypted message contents. The researchers validated the attacks on production-equivalent SGX hardware, so the findings are demonstrated rather than theoretical. No client or server version numbers are given; the affected component is Signal’s CDSI enclave as built from official sources.
Mitigation
Both issues are fixed upstream by Signal, so there is no user action required beyond keeping Signal current:
- Shard-worker UAF: the enclave now enforces a single worker per shard inside the trusted boundary, closing the duplicate-worker path (Signal commit
df22988b). - Client-handle TOCTOU: the canary and state were merged into a single atomic acquisition word, eliminating the check/use window (Signal commit
b1c5ac44).
The transferable lessons for anyone writing enclave (or any host-adversarial trusted) code: treat the host as fully malicious over scheduling and object lifetime; make security-relevant validation and state acquisition atomic so a paused thread cannot be raced; enforce invariants like “one worker per shard” inside the trusted boundary rather than trusting host-driven ECALL sequencing; and remember that page-fault-driven thread pausing makes otherwise-improbable UAF/TOCTOU races deterministically exploitable.
References
- Compromising Signal’s Contact Discovery Enclave
- Signal fix commit df22988b — enforce single worker per shard
- Signal fix commit b1c5ac44 — atomic canary/state acquisition
- V12 — proof-of-concept / bug disclosures repository
- Oblix — oblivious search index (referenced ORAM research)
- Snoopy — scalable oblivious storage (referenced ORAM research)
- SONIC — newer oblivious-storage design (referenced research)