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

high

How Authentication Bypass happens in PyJWT and how to fix it

A critical authentication bypass vulnerability in PyJWT 2.12.1 allowed attackers to forge valid JSON Web Tokens, potentially bypassing application authentication mechanisms entirely. The vulnerability was fixed in PyJWT 2.13.0 through security improvements to token validation logic. This fix is essential for any application relying on JWT-based authentication.

high

How unsafe pickle deserialization happens in Keras/TensorFlow notebooks and how to fix it

A high-severity untrusted deserialization vulnerability was discovered in `TransferLearningTF.ipynb`, a transfer learning tutorial notebook that loads VGG16 model weights from the internet without verifying their integrity. Because Keras relies on Python's pickle-based serialization format under the hood, a tampered or substituted weights file could execute arbitrary code with the full privileges of the notebook user. The fix adds a SHA-256 checksum verification step immediately after the weight

critical

How Chromium launch-argument injection happens in Python Crawl4AI and how to fix it

A critical unauthenticated remote code execution vulnerability in Crawl4AI 0.8.9 allowed attackers to inject arbitrary Chromium launch arguments through the `browser_config.extra_args` parameter, potentially taking full control of the host process. The fix upgrades to Crawl4AI 0.9.0 and refactors the crawler initialization in `agent/tools/crawler.py` to use the new `BrowserConfig` and `CrawlerRunConfig` APIs, which enforce proper argument validation. This change eliminates the injection surface

critical

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

A critical integer overflow vulnerability was discovered in OpenCV's HAL filter implementation where multiplying image dimensions without overflow protection could allocate dangerously undersized buffers. An attacker supplying crafted image dimensions (e.g., 65536×65536) could trigger heap corruption through out-of-bounds writes. The fix promotes the calculation to 64-bit arithmetic with a single cast.

critical

How buffer overflow via strcpy() happens in C zlib and how to fix it

A critical buffer overflow vulnerability was discovered in `general/libzlib/gzlib.c` where multiple `strcpy()` and `strcat()` calls operated without bounds checking. An attacker controlling file paths or error messages could overflow destination buffers, potentially achieving arbitrary code execution. The fix replaces these unsafe string operations with bounded `memcpy()` calls that respect pre-calculated buffer lengths.

critical

How hardcoded API key placeholders in documentation happen in Python and how to fix it

A high-severity security issue was discovered in the Context7 API documentation where a hardcoded API key placeholder (`CONTEXT7_API_KEY`) could be copied directly into production code. The fix replaced the static string with a proper environment variable reference using `os.environ`, preventing developers from accidentally deploying exposed credentials.