Back to Blog
critical SEVERITY7 min read

How buffer overflow happens in C Bluetooth device handling and how to fix it

A critical buffer overflow vulnerability in `src/wiiuse.c` allowed attackers within Bluetooth range to trigger heap corruption by sending specially crafted HID packets with oversized length values. The fix adds strict bounds checking to validate that data lengths don't exceed buffer capacity before performing memory operations, preventing exploitation by malicious or intercepted Bluetooth devices.

O
By Orbis AppSec
Published June 7, 2026Reviewed June 7, 2026

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in C's wiiuse Bluetooth library where unchecked `memcpy()` operations at lines 627 and 666 copy attacker-controlled byte counts into fixed-size buffers without validation. The fix adds explicit length bounds checking (maximum 16 bytes) and uses `calloc()` instead of `malloc()` to prevent integer underflow, ensuring malicious Bluetooth devices cannot trigger heap corruption.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixAdd explicit length bounds checking and clamp values to safe maximum before memcpy()
riskHeap corruption, denial of service, potential code execution from malicious Bluetooth devices
languageC
root causememcpy() called with user-controlled length parameter without validation against buffer capacity
vulnerabilityUnbounded buffer overflow in memcpy() with attacker-controlled length

How Buffer Overflow Happens in C Bluetooth Device Handling and How to Fix It

The Incident

In the wiiuse Bluetooth library (src/wiiuse.c), a critical buffer overflow vulnerability was discovered in the data handling functions used to communicate with Wii Remote controllers. The vulnerability existed at lines 627 and 666, where memcpy() operations copied data from untrusted Bluetooth packets into fixed-size buffers without validating that the requested length actually fit within the destination buffer.

An attacker within Bluetooth range (10–100 meters) could pair a malicious device or intercept Bluetooth HID traffic, sending a specially crafted packet with an oversized len value. This would cause memcpy() to write far beyond the buffer boundary, corrupting the heap and potentially crashing the application or enabling code execution.

Why This Matters

Bluetooth libraries are low-level components that handle raw data from external devices. Unlike HTTP APIs where you might validate JSON structure, Bluetooth HID (Human Interface Device) packets are binary and come from devices that may be compromised or spoofed. When a library blindly trusts the length field in these packets, it creates a direct path for attackers to corrupt memory.


The Vulnerability Explained

The Vulnerable Code

The original code in src/wiiuse.c had two dangerous patterns:

Line 627 (original):

int wiiuse_write_data(struct wiimote_t *wm, unsigned int addr, const byte *data, unsigned short len)
{
    // ... validation code ...

    // NO LENGTH CHECK HERE
    memcpy(bufPtr, data, len);  // len is attacker-controlled via Bluetooth packet
}

Line 666 (original):

int wiiuse_write_data_cb(struct wiimote_t *wm, unsigned int addr, byte *data, byte write_cb)
{
    req = (struct data_req_t *)malloc(sizeof(struct data_req_t));
    req->cb = write_cb;
    req->len = len;  // No bounds check
    memcpy(req->data, data, req->len);  // Copies req->len bytes into req->data
}

The Attack

A malicious Bluetooth device sends a write request with len = 0xFFFF (65,535 bytes). The req->data buffer is only ~23 bytes (standard HID payload), but memcpy() writes 65,535 bytes into it anyway. This overwrites:
- Adjacent heap structures
- Function pointers
- Other allocated objects

The heap corruption can cause:
- Denial of Service: Crash when corrupted metadata is accessed
- Information Disclosure: Leak adjacent heap contents
- Code Execution: Overwrite function pointers or vtables

Why It Happened

The developers assumed that Bluetooth devices would send valid packets. They didn't account for:
1. Malicious devices pairing with the system
2. Man-in-the-middle attacks intercepting and modifying Bluetooth traffic
3. Fuzzing or intentional malformed packets during testing

The Wii Remote protocol doesn't have cryptographic authentication, so any device can claim to be a Wii Remote.


The Fix

The fix adds explicit bounds checking before every memcpy() call and uses safer memory allocation practices:

Change 1: Safer Memory Allocation (Line 141)

Before:

wm = (struct wiimote_t **)malloc(sizeof(struct wiimote_t *) * wiimotes);

After:

wm = (struct wiimote_t **)calloc((size_t)wiimotes, sizeof(struct wiimote_t *));

Why: calloc() initializes memory to zero and performs overflow checking internally. It also casts wiimotes to size_t to catch negative values (which could cause integer underflow in the multiplication).

Change 2: Validate Input Length (Lines 605–609)

Before:

int wiiuse_write_data(struct wiimote_t *wm, unsigned int addr, const byte *data, unsigned short len)
{
    if (!data || !len)
    {
        WIIUSE_ERROR("Attempt to write, but no data or length == 0");
        return 0;
    }
    // NO MAXIMUM LENGTH CHECK
    memcpy(bufPtr, data, len);
}

After:

int wiiuse_write_data(struct wiimote_t *wm, unsigned int addr, const byte *data, unsigned short len)
{
    if (!data || !len)
    {
        WIIUSE_ERROR("Attempt to write, but no data or length == 0");
        return 0;
    }
    if (len > 16)  // NEW: Wii Remote protocol max is 16 bytes per write
    {
        WIIUSE_ERROR("Attempt to write more than 16 bytes at once (len=%d)", len);
        return 0;
    }
    memcpy(bufPtr, data, len);
}

Why: The Wii Remote protocol specification limits writes to 16 bytes. Any request exceeding this is invalid and must be rejected.

Change 3: Clamp Length in Callback Handler (Line 670)

Before:

req->len = len;  // Direct assignment, no validation
memcpy(req->data, data, req->len);

After:

req->len = (len > 16) ? 16 : len;  // Clamp to maximum
memcpy(req->data, data, req->len);

Why: Defense in depth. Even if a caller somehow bypasses the check in wiiuse_write_data(), the callback handler clamps the length to prevent overflow.

Change 4: Regression Test (New File)

A security-focused regression test was added to verify the invariant:

START_TEST(test_read_data_length_bounds)
{
    struct wiimote_t **wm = wiiuse_init(1);

    unsigned short test_lens[] = {
        0xFFFF,                    /* Exploit: massive length */
        WIIUSE_MAX_PAYLOAD + 100,  /* Boundary: exceeds payload buffer */
        16,                        /* Valid: fits in buffer */
    };

    for (int i = 0; i < num_tests; i++) {
        unsigned short len = test_lens[i];
        int ret = wiiuse_read_data(wm[0], NULL, 0, 0x0000, len);
        if (len > WIIUSE_MAX_PAYLOAD) {
            ck_assert_msg(ret == 0,
                "read_data accepted dangerous len=%u without bounds check", len);
        }
    }
}
END_TEST

This test ensures that oversized requests are always rejected, preventing regressions.


Prevention & Best Practices

1. Never Trust External Length Values

When parsing binary protocols (Bluetooth, USB, network packets), always validate that declared lengths don't exceed protocol limits or buffer capacity:

// Bad
memcpy(buffer, packet->data, packet->len);

// Good
if (packet->len > MAX_BUFFER_SIZE) {
    return error("Invalid packet length");
}
memcpy(buffer, packet->data, packet->len);

2. Use Protocol-Specific Limits

Understand the protocol specification. Wii Remote writes are limited to 16 bytes—enforce this limit in code:

#define WIIUSE_MAX_WRITE_SIZE 16

if (len > WIIUSE_MAX_WRITE_SIZE) {
    WIIUSE_ERROR("Write exceeds protocol maximum: %d > %d", len, WIIUSE_MAX_WRITE_SIZE);
    return 0;
}

3. Prefer Safe Memory Functions

  • Use calloc() instead of malloc() for zero-initialization and overflow checking
  • Use memcpy_s() on Windows (Microsoft's bounds-checked variant)
  • On Linux, consider -D_FORTIFY_SOURCE=2 compiler flag for runtime checks
  • Use strlcpy() instead of strcpy() for string operations

4. Enable Compiler Protections

gcc -fstack-protector-strong \
    -D_FORTIFY_SOURCE=2 \
    -Wl,-z,relro,-z,now \
    -fPIE -pie \
    src/wiiuse.c

These flags add:
- Stack canaries to detect buffer overflows
- Runtime checks for buffer operations
- Address Space Layout Randomization (ASLR)
- Position Independent Executable (PIE)

5. Use Static Analysis

Tools that can detect this vulnerability:
- Clang Static Analyzer: scan-build gcc src/wiiuse.c
- Coverity: Commercial tool used by many open-source projects
- Semgrep: Open-source pattern-based scanner
- AddressSanitizer: Runtime detector (use during testing)

# Compile with AddressSanitizer
gcc -fsanitize=address -g src/wiiuse.c -o wiiuse_test

# Run tests—ASAN will catch heap overflow attempts
./wiiuse_test

6. Fuzz Test Protocols

Bluetooth and USB protocols benefit from fuzzing:

# Use libFuzzer to generate malformed packets
clang -fsanitize=fuzzer src/wiiuse_fuzzer.c src/wiiuse.c -o wiiuse_fuzzer
./wiiuse_fuzzer

Key Takeaways

  • Buffer overflow via unchecked memcpy(): The wiiuse_write_data() function accepted arbitrary length values from Bluetooth packets without validating them against the 16-byte protocol maximum, allowing heap corruption.

  • Defense in depth matters: The fix adds bounds checking in two places (wiiuse_write_data() and wiiuse_write_data_cb()) so that even if one check is bypassed, the other prevents overflow.

  • Use calloc() for safer allocation: Switching from malloc() to calloc() provides zero-initialization and internal overflow detection, catching integer underflow bugs.

  • Protocol specifications are security boundaries: The Wii Remote HID protocol limits writes to 16 bytes—enforcing this limit in code is not optional, it's a security requirement.

  • Regression tests prevent re-introduction: The new test_invariant_wiiuse.c test ensures that oversized length values are always rejected, preventing future developers from accidentally removing the fix.


How Orbis AppSec Detected This

Source: Bluetooth HID packet received from external device, containing the len field in a write request

Sink: memcpy(bufPtr, data, len) at line 627 and memcpy(req->data, data, req->len) at line 666 in src/wiiuse.c

Missing Control: No validation that len doesn't exceed the protocol maximum (16 bytes) or buffer capacity before the memcpy call

CWE: CWE-120 (Buffer Copy without Checking Size of Input)

Fix: Added explicit bounds checking (if (len > 16) return error;) before memcpy operations and replaced malloc() with calloc() for safer allocation

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 in low-level protocols like Bluetooth are particularly dangerous because the data source (external devices) is untrusted and often outside your control. The wiiuse library's vulnerability demonstrates a critical lesson: never assume that external input respects protocol limits—always validate it in code.

The fix is straightforward but essential: validate that len doesn't exceed 16 before calling memcpy(). This simple check prevents attackers within Bluetooth range from corrupting the heap.

As you work with binary protocols, USB, Bluetooth, or network code, apply these principles:
1. Know your protocol's limits
2. Validate all external length values
3. Use safe memory functions
4. Enable compiler protections
5. Test with malformed input

Security isn't a feature—it's a requirement for code that handles untrusted external input.


References

Frequently Asked Questions

What is a buffer overflow in C memcpy()?

A buffer overflow occurs when memcpy() copies more bytes than the destination buffer can hold, overwriting adjacent heap memory. If the length parameter comes from untrusted input (like a Bluetooth packet), attackers can control the overflow.

How do you prevent buffer overflow in C Bluetooth code?

Always validate that the length parameter doesn't exceed the maximum safe size for your buffer before calling memcpy(). Use bounds checking, size_t casts to detect integer underflow, and prefer safer APIs like memcpy_s() on Windows or explicit length validation.

What CWE is this buffer overflow?

CWE-120 (Buffer Copy without Checking Size of Input) and CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer).

Is input sanitization enough to prevent this?

Not by itself—you must also validate that the length doesn't exceed your buffer capacity. Sanitization removes dangerous characters, but buffer overflow is about size, not content. Both checks are needed.

Can static analysis detect this buffer overflow?

Yes. Tools like Clang Static Analyzer, Coverity, and multi-agent AI scanners can flag memcpy() calls where the length parameter is not validated against buffer bounds, especially when the length comes from external data structures.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #168

Related Articles

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.

critical

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

A critical buffer overflow vulnerability in `include/html_parse.h` allowed attackers to overflow buffers by providing malicious HTML input exceeding buffer capacity. The fix adds proper bounds checking before memcpy() operations to prevent memory corruption and potential code execution.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.