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

high

How heap buffer overflow happens in C JMA archive extraction and how to fix it

A heap buffer overflow vulnerability in `jma/jma.cpp` allowed a crafted JMA ROM archive to trigger out-of-bounds memory writes during file extraction. The flaw existed at line 446, where `memcpy` was called with `first_chunk_offset` and `copy_amount` values derived directly from archive header metadata without any validation that those values stayed within the bounds of either the source or destination buffer. The fix adds a pre-copy bounds check that rejects malformed archives before the danger

critical

How unsafe buffer copying happens in C credential storage and how to fix it

A critical vulnerability in `lib/server.c` allowed attackers to trigger out-of-bounds memory reads when copying credentials via unsafe `memcpy()` calls. By replacing `memcpy()` with bounds-safe `strlcpy()`, the fix ensures credentials are safely stored without buffer overruns or null-termination issues.

critical

How buffer overflow happens in C patches.c sprintf macros and how to fix it

A critical buffer overflow vulnerability was discovered in `src/patches.c` where the `_EPRINT_I`, `_EPRINT_F`, and `_EPRINT_COEF` macros used `sprintf()` to write formatted AMY event data into a fixed-size buffer without any bounds checking. By replacing every `sprintf()` call with `snprintf()` and tracking remaining buffer space using a `s_entry` base pointer, the fix ensures that formatting 22 event fields — even at maximum values — can never write beyond the buffer boundary.

critical

How buffer overflow happens in C dcraw_lz.c nikon_3700() and how to fix it

A critical buffer overflow vulnerability was discovered in `lightcrafts/coprocesses/dcraw/dcraw_lz.c` at line 1334, where the `nikon_3700()` function used `strcpy()` to copy camera make and model strings into fixed 64-byte buffers without any bounds checking. A crafted RAW image file with oversized make/model metadata could trigger a heap or stack corruption, potentially enabling arbitrary code execution. The fix replaces both `strcpy()` calls with `strncpy()` and explicit null-termination, enfo

critical

How buffer overflow in modxo_queue.c memcpy happens in C embedded systems and how to fix it

A critical buffer overflow vulnerability was discovered in `modxo/modxo_queue.c`, where two `memcpy` operations in the `modxo_queue_insert` and `modxo_queue_remove` functions used `queue->item_size` as the copy length without validating it against the destination buffer's bounds. If `item_size` was corrupted or maliciously set to an oversized value, both the enqueue (line 49) and dequeue (line 61) operations could overflow adjacent heap or stack memory on the embedded target. The fix adds bounds

critical

How SQL injection happens in PostgreSQL dictionary synchronization and how to fix it

A critical SQL injection vulnerability in `zhparser--2.1.sql` allowed attackers to execute arbitrary SQL commands by crafting malicious database names. The vulnerability existed because the dictionary synchronization function constructed COPY commands using string concatenation without proper escaping. This fix implements parameterized queries to safely handle database identifiers.