Back to Blog
critical SEVERITY8 min read

How buffer overflow happens in C SGX enclave memcpy and how to fix it

A critical buffer overflow vulnerability was discovered in `backend/sgx/enclave/enclave.c` where the `ecall_store_data` function performed `memcpy` operations without proper bounds checking against the actual destination buffer size. An attacker could supply a malicious `data_len` parameter to overflow the enclave's secure storage buffer, potentially corrupting trusted execution environment memory. The fix replaces a hardcoded magic number check with a precise size comparison against the actual

O
By Orbis AppSec
Published July 26, 2026Reviewed July 26, 2026

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in C code inside an Intel SGX enclave (`backend/sgx/enclave/enclave.c`). The `ecall_store_data` function checked `data_len > 256` — a hardcoded magic number — instead of comparing against the actual destination buffer size (`sizeof(secure_storage[storage_count].data)`). This meant an attacker could pass a `data_len` that exceeds the real buffer boundary, triggering a heap/stack overflow inside the trusted execution environment. The fix adds a two-part check: reject zero-length inputs and reject any length exceeding `sizeof(secure_storage[storage_count].data)`, ensuring the memcpy can never write past the buffer.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixReplace `data_len > 256` with `data_len == 0 || data_len > sizeof(secure_storage[storage_count].data)`
riskAttacker-controlled data_len can overflow secure enclave memory, corrupting trusted execution environment state
languageC
root causeBounds check used hardcoded constant 256 instead of actual destination buffer size
vulnerabilityBuffer Overflow via Unchecked memcpy in SGX Enclave

How Buffer Overflow Happens in C SGX Enclave memcpy and How to Fix It

Summary

A critical buffer overflow vulnerability was discovered in backend/sgx/enclave/enclave.c where the ecall_store_data function performed memcpy operations without proper bounds checking against the actual destination buffer size. An attacker could supply a malicious data_len parameter to overflow the enclave's secure storage buffer, potentially corrupting trusted execution environment memory. The fix replaces a hardcoded magic number check with a precise size comparison against the actual destination buffer, closing the vulnerability entirely.


Introduction

The backend/sgx/enclave/enclave.c file sits at the heart of a trusted execution environment: an Intel SGX enclave responsible for securely storing sensitive data. The ecall_store_data function is an ECALL — an entry point that untrusted application code calls to pass data into the enclave for protected storage. This is exactly the boundary where input validation is most critical.

But a subtle flaw in the bounds check at line 78 created a critical security gap. The check read:

if (data_len > 256) {
    return SGX_ERROR_INVALID_PARAMETER;
}

This looks reasonable at a glance. But 256 is a hardcoded magic number with no structural relationship to the actual size of the destination buffer (secure_storage[storage_count].data). If the real buffer is smaller than 256 bytes — or if it was ever resized — this check silently becomes incorrect, and a subsequent memcpy will write past the end of the buffer.

For developers writing C code that interfaces with trusted execution environments, this is a textbook example of why hardcoded size limits are dangerous.


The Vulnerability Explained

What Went Wrong

Inside ecall_store_data, data passed from untrusted code is copied into the enclave's internal secure_storage structure using memcpy. Before the copy, the code validates data_len to prevent overflows:

// VULNERABLE CODE (before fix)
if (data_len > 256) {
    return SGX_ERROR_INVALID_PARAMETER;
}

The problem: the check is against the literal integer 256, not against sizeof(secure_storage[storage_count].data). These two values may or may not be equal. If the data field in the storage struct is, say, 128 bytes, then an attacker can pass data_len = 200 — which passes the > 256 check — and the subsequent memcpy writes 200 bytes into a 128-byte buffer, overflowing by 72 bytes.

There's a second issue the fix also addresses: a data_len of 0 was not rejected. Passing zero length to memcpy is a no-op in standard C, but it can cause logic errors downstream and represents malformed input that should be explicitly rejected.

How an Attacker Could Exploit This

The attack surface here is the ECALL interface itself. An attacker (or a malicious application running outside the enclave) calls ecall_store_data with a crafted data_len that is:

  • Greater than the actual buffer size (e.g., 200 when the buffer is 128 bytes)
  • But less than or equal to 256 (so it passes the broken check)

The memcpy then writes attacker-controlled bytes past the end of secure_storage[storage_count].data, corrupting adjacent fields in the secure_storage array or other enclave memory. In an SGX context, this is particularly severe:

  1. Enclave memory corruption can subvert the integrity guarantees SGX is designed to provide.
  2. Sensitive data leakage: adjacent memory in the enclave may contain cryptographic keys, decrypted secrets, or other protected values.
  3. Control flow hijacking: if a function pointer or return address is adjacent to the overflowed buffer, an attacker may redirect execution within the trusted context.

The PR notes that similar patterns exist at lines 95 and 115 of the same file, suggesting this vulnerability class was applied inconsistently throughout the enclave's input-handling code.

Real-World Impact

SGX enclaves are used specifically because they are supposed to be more trustworthy than regular application memory. A buffer overflow that corrupts enclave memory doesn't just crash the program — it can silently undermine the security model that the entire system is built on. Secrets that were supposed to be protected by the enclave could be exposed or manipulated.


The Fix

What Changed

The fix is a single-line change at line 78 of backend/sgx/enclave/enclave.c:

// BEFORE (vulnerable)
if (data_len > 256) {
    return SGX_ERROR_INVALID_PARAMETER;
}

// AFTER (fixed)
if (data_len == 0 || data_len > sizeof(secure_storage[storage_count].data)) {
    return SGX_ERROR_INVALID_PARAMETER;
}

Why This Fix Works

The new check does two things:

  1. data_len == 0: Rejects zero-length inputs explicitly. This closes an edge case where zero-length copies could cause downstream logic errors.

  2. data_len > sizeof(secure_storage[storage_count].data): This is the critical improvement. Instead of comparing against the magic number 256, the check now uses sizeof() to query the actual compile-time size of the destination buffer. This means:
    - If the struct is ever refactored and data changes size, the check automatically stays correct.
    - There is no gap between what the check allows and what the buffer can hold.
    - The bounds check is structurally coupled to the allocation it protects.

This is the canonical safe pattern for bounds-checking before memcpy in C.

Before vs. After

Before After
Check data_len > 256 data_len == 0 \|\| data_len > sizeof(...)
Zero-length rejected? No Yes
Tied to actual buffer size? No (magic number) Yes (sizeof)
Safe if struct is refactored? No Yes

Prevention & Best Practices

1. Always Use sizeof() for Buffer Bounds Checks

Never hardcode buffer sizes in validation logic. Use sizeof(destination) so the check is always in sync with the allocation:

// UNSAFE
if (len > 1024) { ... }

// SAFE
if (len == 0 || len > sizeof(destination_buffer)) { ... }

2. Use Safe Memory Functions Where Available

Consider memcpy_s (from C11 Annex K) or platform-safe equivalents that take an explicit destination size parameter:

// memcpy_s takes the destination size as a separate argument
errno_t err = memcpy_s(dest, sizeof(dest), src, data_len);
if (err != 0) {
    // handle error
}

3. Apply Extra Scrutiny at Trust Boundaries

ECALL functions (SGX enclave entry points) are trust boundaries — they accept data from untrusted code. Every parameter that crosses this boundary should be validated as if it were attacker-controlled, because it is.

4. Enable Compiler and Runtime Protections

  • AddressSanitizer (ASan): Detects out-of-bounds memory accesses at runtime during testing.
  • Stack Canaries (-fstack-protector-strong): Detect stack-based buffer overflows.
  • FORTIFY_SOURCE: Enables compile-time and runtime checks for memcpy, strcpy, and similar functions.

5. Audit Similar Patterns

The PR itself flags lines 95 and 115 as potentially using the same vulnerable pattern. When fixing a bounds-check issue, always search the entire file — and related files — for the same anti-pattern.

Relevant Standards

  • CWE-120: Buffer Copy without Checking Size of Input
  • CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
  • OWASP: Input Validation Cheat Sheet
  • CERT C: Rule ARR38-C — Guarantee that library functions do not form invalid pointers

Key Takeaways

  • data_len > 256 is not a safe bounds check if the actual destination buffer (secure_storage[storage_count].data) is not guaranteed to be exactly 256 bytes — use sizeof() instead.
  • ECALL parameters in SGX enclaves must be treated as untrusted input, even if the enclave itself is trusted; the data crossing the boundary is not.
  • Magic numbers in security-critical checks are a code smell: if the buffer size ever changes, the hardcoded check silently becomes wrong.
  • Zero-length inputs should be explicitly rejected in memcpy contexts to prevent logic errors downstream.
  • Lines 95 and 115 in enclave.c use similar patterns and should be reviewed for the same class of vulnerability.

How Orbis AppSec Detected This

  • Source: The data_len parameter of ecall_store_data — supplied by untrusted application code calling into the SGX enclave via the ECALL interface.
  • Sink: The memcpy call in backend/sgx/enclave/enclave.c:78 that copies data_len bytes into secure_storage[storage_count].data without validating against the actual buffer size.
  • Missing control: The existing check (data_len > 256) used a hardcoded magic number rather than sizeof(secure_storage[storage_count].data), creating a gap between the allowed length and the actual buffer capacity.
  • CWE: CWE-120 — Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
  • Fix: The condition was updated to data_len == 0 || data_len > sizeof(secure_storage[storage_count].data), structurally tying the bounds check to the actual destination buffer size.

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

Buffer overflows from unchecked memcpy calls are one of the oldest vulnerability classes in C programming — and one of the most dangerous when they occur inside a trusted execution environment like an Intel SGX enclave. The root cause here wasn't a complex algorithmic flaw; it was a single hardcoded magic number (256) that had no structural relationship to the actual destination buffer size.

The fix is equally simple: replace the magic number with sizeof(secure_storage[storage_count].data). This one-line change ensures the bounds check is always accurate, regardless of how the underlying struct evolves over time.

For developers writing C code — especially code that runs inside security-sensitive contexts like SGX enclaves — the lesson is clear: always tie your bounds checks to the actual allocation, never to a constant you think is right.


References

Frequently Asked Questions

What is a buffer overflow in an SGX enclave?

A buffer overflow in an SGX enclave occurs when code inside the trusted execution environment writes more data than a buffer can hold, corrupting adjacent memory. Because SGX enclaves are meant to be highly trusted, a buffer overflow there is especially dangerous — it can undermine the security guarantees the enclave is designed to provide.

How do you prevent buffer overflows in C memcpy calls?

Always validate the length parameter against the actual size of the destination buffer using `sizeof()` before calling `memcpy`. Never use hardcoded magic numbers as size limits; use `sizeof(dest)` so the check automatically stays in sync if the buffer size changes.

What CWE is a buffer overflow from unchecked memcpy?

CWE-120 — "Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')". Related identifiers include CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) and CWE-787 (Out-of-bounds Write).

Is checking data_len > 256 enough to prevent buffer overflows?

No. Hardcoded magic numbers are fragile. If the actual buffer size is smaller than 256, or if the buffer is later resized, the check becomes incorrect. Always use `sizeof(destination_buffer)` so the validation is structurally tied to the actual allocation.

Can static analysis detect this type of buffer overflow?

Yes. Tools like Semgrep, CodeQL, Coverity, and clang's AddressSanitizer can detect memcpy calls where the length argument is not validated against the destination buffer size. Orbis AppSec's multi-agent AI scanner flagged this exact pattern in `enclave.c`.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #4908

Related Articles

medium

How integer underflow in array splice operations happens in C and how to fix it

A critical integer underflow vulnerability was discovered in tree-sitter's array.h header file, where the `_array__splice()` function calculated array sizes without proper bounds checking. The vulnerable code relied on assert() statements that are disabled in release builds, allowing arithmetic underflow when `old_count > *size + new_count`, potentially causing memory corruption through out-of-bounds memcpy operations.

high

How buffer overflow via sprintf() happens in C string formatting and how to fix it

A high-severity buffer overflow vulnerability was discovered in `bench/strbuild/strbuild.c` where `sprintf()` wrote formatted output into a 64-byte stack buffer (`line[64]`) without any bounds checking. An attacker who could influence the values in the `NAMES[]`, `c[]`, or `v[]` arrays could overflow this buffer, potentially corrupting the stack and hijacking control flow. The fix replaces `sprintf()` with `snprintf(line, sizeof(line), ...)` to enforce a strict 64-byte write limit.

critical

How buffer overflow happens in C tar header parsing and how to fix it

A critical buffer overflow vulnerability was discovered in `microtar/microtar.c` where the `raw_to_header()` and `header_to_raw()` functions used unbounded `strcpy()` and `sprintf()` calls to copy tar header fields. Malicious tar files with non-null-terminated name fields could overflow destination buffers, potentially leading to code execution. The fix replaces all unsafe string operations with bounded alternatives: `memcpy()` with explicit null-termination and `snprintf()` instead of `sprintf(

critical

How buffer overflow happens in C ieee80211_input() and how to fix it

A critical buffer overflow vulnerability was discovered in `src/firmware/src/net/ieee80211.c` at line 1584, where the `ieee80211_input()` function processed raw 802.11 data frames without verifying that the incoming frame was large enough to contain a valid `ieee80211_frame` header. An attacker within wireless range could craft undersized or malformed frames to trigger memory corruption, potentially leading to remote code execution on the firmware. The fix adds a single, targeted bounds check th

high

How buffer overflow from unsafe string copy functions happens in C network interface code and how to fix it

A high-severity buffer overflow vulnerability was discovered in `generic/eth-impl.c`, where unsafe `strncpy()` and `sprintf()` calls could write beyond buffer boundaries when handling network interface names and device filenames. The fix replaced these dangerous functions with bounded `snprintf()` calls that guarantee null-termination and prevent memory corruption.

critical

How buffer overflow in FuzzIxml.c sprintf() happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in `fuzzer/FuzzIxml.c` where `sprintf()` wrote a PID-formatted filename into a fixed 256-byte stack buffer without any bounds checking. The fix replaces `sprintf()` with `snprintf()`, explicitly passing the buffer size to prevent any overflow. While exploitation in this specific fuzzer context requires local access, the pattern is a textbook example of CWE-120 that developers should recognize and eliminate everywhere it appears.