Back to Blog
critical SEVERITY6 min read

How buffer overflow in memcpy happens in C bios_disk.h and how to fix it

A critical buffer overflow vulnerability was discovered in `include/bios_disk.h` at line 474, where a `memcpy` operation copies 512 bytes from a source buffer without properly validating that the calculated offset from the `sectnum` parameter stays within bounds. An attacker controlling the `sectnum` parameter could trigger an out-of-bounds read, potentially leaking sensitive memory contents or causing a crash. The fix adds a proper bounds check before the memcpy call to ensure the source offset

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

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in C where the `memcpy` operation in `include/bios_disk.h:474` copies 512 bytes from a source buffer using an attacker-controllable `sectnum` parameter without validating that the computed offset stays within the buffer's bounds. The fix adds an explicit bounds check that verifies `(sectnum * 512) + 512 <= buffer_size` before performing the copy, preventing out-of-bounds reads and potential memory disclosure.

Vulnerability at a Glance

cweCWE-120
fixAdd bounds check verifying offset + copy size ≤ buffer size before memcpy
riskOut-of-bounds read leading to memory disclosure or crash
languageC
root causeMissing validation that sectnum-derived offset plus 512 bytes stays within source buffer bounds
vulnerabilityBuffer overflow via unchecked memcpy

How Buffer Overflow in memcpy Happens in C bios_disk.h and How to Fix It

Introduction

In the DOSBox-X codebase, a critical buffer overflow vulnerability was discovered in include/bios_disk.h at line 474. The bios_disk_read function uses memcpy to copy 512 bytes (one disk sector) from a source buffer into a destination data buffer, but the offset calculation derived from the sectnum parameter lacked sufficient validation. While the existing code checked whether the offset exceeded the buffer size, it failed to verify that the offset plus the 512-byte copy length remained within bounds — a classic off-by-one category error that left the door open to out-of-bounds memory reads.

This vulnerability is particularly dangerous because sectnum can be influenced by disk I/O requests, meaning an attacker with the ability to craft malicious disk images or manipulate sector parameters could trigger memory disclosure or denial of service.

The Vulnerability Explained

The vulnerable code in include/bios_disk.h:474 performs a sector read operation that looks conceptually like this:

// Vulnerable code (before fix)
uint32_t offset = sectnum * 512;
// Insufficient check: only validates offset, not offset + copy_size
if (offset >= buffer_size) {
    return error_code;
}
memcpy(data, buffer + offset, 512);

The problem is subtle but critical. Consider what happens when sectnum is at the boundary:

  • Buffer size: 2048 bytes
  • sectnum = 4 → offset = 2048
  • The check offset >= buffer_size catches this case (2048 >= 2048 is true) ✓
  • But sectnum = 3 → offset = 1536
  • The check 1536 >= 2048 is false, so memcpy proceeds
  • memcpy(data, buffer + 1536, 512) reads bytes 1536–2047 ✓

Now consider a larger buffer scenario or integer overflow:

  • If sectnum = 0xFFFFFFFF, the multiplication sectnum * 512 overflows a 32-bit integer
  • The resulting offset wraps around to a small value
  • The bounds check passes because the wrapped value is less than buffer_size
  • memcpy then reads from an arbitrary memory location relative to buffer

Attack Scenario: An attacker who can control the sectnum parameter — for example, by providing a crafted disk image with manipulated partition table entries or by exploiting a BIOS interrupt handler — can cause the emulator to:

  1. Read out-of-bounds memory: Leaking adjacent heap or stack data that may contain sensitive information
  2. Crash the application: Accessing unmapped memory pages triggers a segmentation fault
  3. Bypass ASLR: Leaked memory addresses from adjacent allocations can defeat address space layout randomization

The regression test included in the PR demonstrates these exact scenarios:

uint32_t test_cases[] = {
    0xFFFFFFFF,  // Exploit: large sectnum causing overflow
    0x00000004,  // Boundary: exactly at buffer limit
    0x00000000   // Valid: within bounds
};

The Fix

The fix adds a proper bounds check that validates both the offset calculation and the total read range before the memcpy operation executes:

Before (vulnerable):

uint32_t offset = sectnum * 512;
if (offset >= buffer_size) {
    return error_code;
}
memcpy(data, buffer + offset, 512);

After (fixed):

uint32_t offset = sectnum * 512;
// Check for integer overflow in multiplication
if (sectnum != 0 && offset / sectnum != 512) {
    return error_code;
}
// Check that offset + copy size stays within buffer bounds
if (offset + 512 > buffer_size) {
    return error_code;
}
memcpy(data, buffer + offset, 512);

The fix addresses two distinct issues:

  1. Integer overflow protection: By verifying that offset / sectnum == 512, the code detects when the multiplication has wrapped around. This prevents attackers from using large sectnum values to bypass the subsequent bounds check.

  2. Complete bounds validation: The check offset + 512 > buffer_size ensures that the entire 512-byte read window falls within the buffer, not just the starting offset. This closes the gap where the old check would allow reads that started within bounds but extended past the buffer's end.

The PR also disabled a potentially problematic CI step (if: false on the Windows build run step) and updated actions/checkout from v6 to v7 across multiple workflow files, which are maintenance changes unrelated to the security fix but included in the same PR for CI stability.

Prevention & Best Practices

1. Always validate the full range, not just the start

When performing buffer copies with computed offsets, check that offset + length <= buffer_size. Never assume that a valid starting offset implies a valid read range.

2. Guard against integer overflow in offset calculations

In C, unsigned integer overflow is defined behavior (it wraps), but it's almost never what you want. Use overflow-safe arithmetic:

// Safe multiplication check
if (sectnum > (SIZE_MAX / 512)) {
    return error_code;  // Would overflow
}

3. Use size_t for buffer arithmetic

Using uint32_t for offset calculations on 64-bit systems can mask overflows. Prefer size_t which matches the platform's address space.

4. Consider using safe memory copy wrappers

Create a project-wide safe_memcpy function that takes source buffer size as a parameter and validates bounds internally:

int safe_memcpy(void *dst, size_t dst_size, const void *src, 
                size_t src_size, size_t offset, size_t count) {
    if (offset + count > src_size || count > dst_size) return -1;
    memcpy(dst, (const char*)src + offset, count);
    return 0;
}

5. Static analysis integration

Run tools like Coverity, PVS-Studio, or Semgrep in CI to catch unchecked memcpy patterns before they reach production.

Key Takeaways

  • The sectnum * 512 calculation in bios_disk.h:474 could overflow a 32-bit integer, wrapping around to bypass the existing bounds check — always validate arithmetic before using the result.
  • Checking only the offset without the copy length is insufficient — the old check offset >= buffer_size missed cases where the read window extended past the buffer end.
  • Disk I/O emulation code is security-critical because sector numbers originate from potentially untrusted disk images that users load.
  • Line 475 in the same file uses a similar pattern and should be reviewed with the same bounds-checking approach.
  • The regression test with sectnum = 0xFFFFFFFF demonstrates that integer overflow is the primary exploitation vector, not just large-but-valid sector numbers.

How Orbis AppSec Detected This

  • Source: The sectnum parameter passed to the BIOS disk read function, which originates from disk image metadata or emulated BIOS interrupt calls
  • Sink: memcpy(data, buffer + offset, 512) in include/bios_disk.h:474
  • Missing control: No validation that (sectnum * 512) + 512 stays within the source buffer bounds, and no integer overflow check on the multiplication
  • CWE: CWE-120 (Buffer Copy without Checking Size of Input)
  • Fix: Added bounds check verifying that the computed offset plus 512 bytes does not exceed the buffer size, with integer overflow detection on the sector-to-offset multiplication

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 memcpy operations remain one of the most dangerous vulnerability classes in C code. This specific instance in bios_disk.h demonstrates how a seemingly reasonable bounds check can be insufficient when it doesn't account for the full copy range or integer overflow in offset calculations. The fix is straightforward — validate the complete read window and check for arithmetic overflow — but the consequences of missing it are severe: memory disclosure, crashes, and potential code execution. When writing low-level buffer manipulation code, always ask: "Does my check cover the entire operation, not just the starting point?"

References

Frequently Asked Questions

What is a buffer overflow in memcpy?

A buffer overflow in memcpy occurs when the function copies data beyond the allocated boundaries of either the source or destination buffer, typically because the offset or size parameters are not properly validated against actual buffer dimensions.

How do you prevent buffer overflow in C memcpy calls?

Always validate that the source offset plus the number of bytes to copy does not exceed the source buffer size, and that the destination buffer is large enough to hold the copied data. Use explicit bounds checks immediately before every memcpy call.

What CWE is buffer overflow?

Buffer overflow is classified as CWE-120 (Buffer Copy without Checking Size of Input), which is part of the broader CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) family.

Is checking only the offset enough to prevent buffer overflow in memcpy?

No. You must check that the offset plus the number of bytes being copied does not exceed the buffer size. Checking only the offset without accounting for the copy length still allows reading or writing past the buffer boundary.

Can static analysis detect buffer overflow in memcpy?

Yes. Static analysis tools like Coverity, PVS-Studio, and Semgrep can detect missing bounds checks before memcpy calls, especially when the offset is derived from user-controllable input like sector numbers or indices.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #6371

Related Articles

critical

How unlimited batch API calls happen in React JSX and how to fix it

A missing batch size limit in `BatchModeRunner.jsx` allowed users to trigger unlimited LLM API calls by pasting thousands of items into the batch input field. This could exhaust shared API quotas in organizational settings where a single API key is distributed across multiple users. The fix introduces a hard cap of 25 items (`MAX_BATCH_SIZE = 25`) enforced directly in the `canRun()` validation function.

high

How NO_PROXY bypass via crafted URL happens in Node.js axios and how to fix it

A high-severity vulnerability (CVE-2026-42043) was discovered in axios versions prior to 1.15.1, allowing attackers to bypass NO_PROXY configurations using specially crafted URLs. This could expose sensitive internal traffic to proxy servers, potentially leaking credentials or internal data. The fix upgrades axios to version 1.15.1, which properly validates URLs before applying proxy exclusion rules.

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.

high

How Middleware/Proxy Bypass Happens in Next.js App Router with Turbopack and Single Locale and How to Fix It

A critical middleware and proxy bypass vulnerability (CVE-2026-64642) was discovered in Next.js 16.0.7 when using the App Router with Turbopack and single locale configurations. This vulnerability could allow attackers to circumvent security middleware and access protected routes. The fix requires upgrading Next.js from version 16.0.7 to 16.2.11, which patches the routing logic that handles locale-based request interception.

high

How missing Dependabot cooldown happens in GitHub Actions and how to fix it

A high-severity configuration flaw was discovered in a Dependabot configuration file where no cooldown period was set for package updates. This meant newly published—and potentially malicious or unstable—package versions could be immediately proposed for updates, exposing the project to supply chain attacks. The fix adds a 7-day cooldown period to allow the community to identify compromised packages before they're adopted.

critical

How arbitrary code execution via injected protobuf definition type fields happens in Node.js and how to fix it

A critical arbitrary code execution vulnerability (CVE-2026-41242) was discovered in protobufjs versions prior to 7.5.5 and 8.0.1, allowing attackers to inject malicious code through crafted protobuf definition type fields. The fix upgrades the dependency from the vulnerable version 6.11.4 to 7.5.5, which properly sanitizes type field inputs during protobuf parsing. This vulnerability is especially dangerous because protobufjs is widely used in Node.js applications for serialization, meaning a s