Back to Blog
critical SEVERITY4 min read

SGX Enclave ecall_store_data memcpy Buffer Overflow in 256-Byte

The Intel SGX enclave's trusted bridge functions `ecall_store_data` and `ecall_retrieve_data` used `memcpy()` to move data into and out of a fixed 256-byte `secure_storage` buffer without validating that `data_len` fit within destination boundaries. An attacker providing oversized `data_len` values could corrupt enclave memory, breaking SGX's confidentiality guarantees.

O
By Orbis AppSec
•Published September 29, 2026•Reviewed September 29, 2026

Answer Summary

The `ecall_store_data` and `ecall_retrieve_data` functions in the SGX enclave trusted interface are affected; these are first-party code with no assigned version. An attacker can overflow the 256-byte `secure_storage` buffer by passing a `data_len` value exceeding the buffer size, corrupting enclave heap memory and potentially extracting secrets or altering attestation results. The fix adds a `max_len` parameter to `ecall_retrieve_data` and enforces bounds checking before `memcpy` operations; fixed version not assigned. CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer.

Vulnerability at a Glance

cweCWE-119
fixAdded max_len parameter and explicit bounds check before memcpy in ecall_retrieve_data
riskMemory corruption in trusted execution environment, potential secret extraction or attestation bypass
languageC
root causememcpy copied data_len bytes without validating against destination buffer size
vulnerabilityBuffer overflow via unchecked memcpy in SGX enclave

Affected Versions

Affected First-party SGX enclave code with ecall_store_data/ecall_retrieve_data functions using unbounded memcpy
Fixed in Unreleased; fix available in proposed PR
Ecosystem N/A (first-party C code for Intel SGX)
CVE / GHSA not assigned
CWE CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer

The Vulnerability Explained

Intel SGX enclaves promise that even a compromised operating system cannot inspect or tamper with protected memory. This guarantee collapses when the enclave's own code contains memory safety bugs.

The ecall_store_data function receives untrusted data_len from the host application and copies that many bytes into secure_storage[index].data—a fixed 256-byte buffer. While the code checks data_len against some limit, the actual memcpy at line 84 trusts this value without secondary enforcement:

// Vulnerable pattern in ecall_store_data
memcpy(secure_storage[index].data, data, data_len);

The companion function ecall_retrieve_data mirrors this flaw on the output path. It copies secure_storage[index].length bytes into the caller-supplied data buffer without knowing if that buffer can hold the data:

// Vulnerable pattern in ecall_retrieve_data (before fix)
memcpy(data, secure_storage[index].data, secure_storage[index].length);

An attacker who compromises the host application can manipulate data_len during storage operations, or craft a stored entry with an inflated length field, then trigger retrieval into an undersized buffer. The result is heap corruption inside the trusted execution environment—precisely the scenario SGX was designed to prevent.

Real-world impact: A malicious cloud provider or compromised hypervisor could overflow secure_storage to overwrite adjacent enclave data structures, potentially altering attestation quotes or extracting cryptographic keys that should remain sealed.

The Fix

The patch introduces explicit contract enforcement through a new max_len parameter and runtime validation:

// Fixed ecall_retrieve_data signature and validation
sgx_status_t ecall_retrieve_data(
    uint32_t index,
    uint8_t* data,
    uint32_t max_len,        // NEW: caller declares buffer capacity
    uint32_t* data_len
) {
    if (index >= storage_count) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    // NEW: explicit bounds check before any copy
    if (secure_storage[index].length > max_len) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    memcpy(data, secure_storage[index].data, secure_storage[index].length);
    *data_len = secure_storage[index].length;
    // ...
}

This change transforms an implicit assumption—that callers never provide undersized buffers—into an explicit, enforced contract. The max_len parameter forces the caller to declare intent, and the enclave validates against that declaration before any memory operation.

Notably, the fix addresses the output path (ecall_retrieve_data) where the caller's buffer size is externally determined and thus most likely to be mismatched. The input path's validation remains dependent on caller-side checks, reflecting the asymmetric trust model where enclaves must distrust all host-provided sizes.

Key Takeaways

  • SGX enclaves are not magically memory-safe: The trusted boundary protects against external observation, not internal bugs. Every memcpy inside an enclave requires the same scrutiny as any other C code.

  • Output buffer sizing is the caller's responsibility, but the enclave's duty to enforce: When an enclave writes to caller-allocated memory, it must accept and validate a capacity declaration. Implicit assumptions about buffer sizes fail across trust boundaries.

  • The data_len parameter in ecall_store_data remains single-point-of-failure: The fix does not add redundant bounds checking on the input path. Defense in depth would validate data_len against sizeof(secure_storage[0].data) even when callers are expected to check first.

  • SGX status codes can signal policy violations: The fix returns SGX_ERROR_INVALID_PARAMETER for size violations, using the existing error taxonomy rather than inventing new codes. This preserves compatibility with existing host-side error handling.

How Orbis AppSec Detected This

Source: The data_len parameter in ecall_store_data and secure_storage[index].length field in ecall_retrieve_data, both controllable by the untrusted host application through the SGX edge interface.

Sink: memcpy() calls copying into secure_storage[index].data (256-byte fixed buffer) and into caller-supplied data output buffer without size validation.

Missing control: No comparison of source length against destination buffer capacity before memory copy operations; no max_len parameter to declare output buffer bounds.

CWE: CWE-119 — Improper Restriction of Operations within the Bounds of a Memory Buffer

Fix: Added max_len parameter to ecall_retrieve_data with explicit validation that secure_storage[index].length <= max_len before memcpy.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

This vulnerability demonstrates that trusted execution environments inherit all the memory safety risks of their implementation language. The SGX enclave's secure_storage mechanism—designed to protect sensitive data—became an attack surface when memcpy operations trusted externally-influenced length values. The fix enforces explicit contracts at the trust boundary: callers must declare buffer capacities, and enclaves must validate before copying. For developers building SGX applications, every edge call represents a potential decompression of the trusted computing base—treat each one with the defensive coding practices that the surrounding infrastructure cannot provide.

Prevention and further reading

Frequently Asked Questions

Does the fix change the signature of ecall_store_data or only ecall_retrieve_data?

Only `ecall_retrieve_data` changes signature—adding the `max_len` parameter. `ecall_store_data` retains its existing interface but relies on the caller to validate `data_len` against the 256-byte `secure_storage` limit.

Can an attacker exploit this without first passing the SGX attestation?

No. The vulnerability exists inside the enclave's trusted interface, so exploitation requires successfully establishing an enclave session. However, once attested, a compromised or malicious client could exploit the overflow to escape intended isolation boundaries.

Why was the bounds check added to ecall_retrieve_data rather than ecall_store_data?

The fix adds defensive validation on the output path where the caller provides a destination buffer (`data`) and must declare its capacity (`max_len`). The input path's `data_len` validation remains the caller's responsibility, following the principle that output buffers are more commonly undersized by callers.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #16736

Related Articles

critical

How buffer overflow happens in C++ and how to fix it

A critical buffer overflow in `create_hex_string()` within `hmlangw.cpp` let an unconditional 16-iteration loop write past the bounds of a 100-byte `hex` buffer using unchecked `sprintf` calls. The fix replaces `sprintf` with `snprintf` and caps the loop iterations based on the actual destination buffer size, closing off a memory corruption path reachable from serial or network input.

critical

How Buffer Overflow via strcpy() Happens in C++ XML Parsers and How to Fix It

A critical buffer overflow vulnerability was discovered in `buildroot-external/package/libxmlparser/xmlParser.cpp`, where the `toXMLString` function used `_tcscpy()` to write XML escape sequences into a destination buffer without any bounds checking. An attacker supplying a crafted XML document could overflow the buffer and potentially execute arbitrary code. The fix replaces all five unsafe `_tcscpy()` calls with `memcpy()` calls that copy only the exact number of bytes required for each escape

critical

How Heap Buffer Overflows Happen in C++ ZIP Extraction and How to Fix Them

A critical heap buffer overflow vulnerability was discovered in `TKLiveSync/unzip.cpp`, where ZIP archive entry names were copied into a `PATH_MAX`-sized heap buffer using `strcpy()` without any length validation. Since the ZIP specification allows entry names up to 65,535 bytes — far exceeding typical `PATH_MAX` values of 1,024 to 4,096 bytes — a crafted archive could overflow the buffer and corrupt heap memory. The fix replaces the unsafe `strcpy`/`dirname` pattern with `std::string` operation

medium

How Integer Overflow happens in C++ image processing and how to fix it

A signed integer overflow in OpenCV's `bilateralFilter.cpp` allowed the buffer size calculation `cal_width * cal_height * cn` to wrap around to a small or negative value, causing `padding.resize()` to allocate far less memory than needed. Subsequent `memcpy` operations would then write beyond the allocated buffer, creating a heap corruption primitive. The fix is a single targeted cast to `size_t` that promotes the multiplication to unsigned 64-bit arithmetic before any overflow can occur.

critical

How Stack Buffer Overflows Happen in C with sprintf() and How to Fix Them

A critical stack buffer overflow was discovered in `libuv/Learn-libuv/docs/code/tty-gravity/main.c` where `sprintf()` wrote ANSI escape sequences and user-controlled variables into a fixed 500-byte buffer without any bounds checking. An attacker controlling the `pos`, `width`, or `message` variables could overflow the stack, overwrite return addresses, and potentially achieve arbitrary code execution. The fix replaces `sprintf()` with `snprintf()` and adds explicit length validation to ensure wr

critical

Tung Tung Tracker Admin API: Missing Authentication in `/api/sync`

The Tung Tung Tracker administrative API endpoints (`/api/sync`, `/api/backfill`, `/api/show`, `/api/overlay/hide`) relied solely on localhost origin and a static `X-Tracker` header for protection, allowing any local process to execute privileged operations. The fix introduces proper session-based authentication and a random IPC secret for desktop-to-server communication.