Off-by-one Error
Summary
A loop, index calculation, or buffer-size computation is off by exactly one element - the classic <= where < was intended, or vice versa - causing either an out-of-bounds access at exactly the boundary or a legitimate final element being silently skipped. It's one of the most common native-code logic mistakes precisely because it's subtle: the code is "almost" correct, and works fine for every input except the one that lands exactly on the boundary.
Why This Requires More Than a Black-Box Scan
Confirming this requires exercising the exact boundary condition against the compiled code and observing the resulting out-of-bounds access or skipped element - not visible in an HTTP request/response exchange.
Where This Is Actually Caught
Boundary-focused fuzzing (specifically testing inputs at exact size limits) and manual code review of loop and index bounds, particularly around buffer and array operations.
Tip: Fuzzing with a sanitizer attached is disproportionately effective for this specific family — an overflow or out-of-bounds access that would otherwise silently corrupt memory instead crashes immediately with a stack trace pointing at the exact allocation, which is what makes fuzz-testing dramatically more efficient here than for logic-level bug classes.
Real-World Impact
Real-World Impact
Buffer, heap, and stack overflows all share the same core mechanism: writing (or reading) more data into a fixed-size memory region than it was allocated to hold, spilling into adjacent memory. A stack overflow can overwrite a saved return address, redirecting program execution to attacker-controlled code the moment the function returns. A heap overflow corrupts heap metadata or adjacent objects, which is harder to weaponize directly but routinely still leads to code execution through heap-grooming techniques. An out-of-bounds read, the less immediately destructive sibling, still leaks adjacent memory contents — which is exactly how bugs like Heartbleed turned a "just a read" bug into mass credential and key disclosure.
Integer overflow and underflow are frequently the trigger rather than the payload: an arithmetic result that wraps around unexpectedly can produce a buffer size calculation that's far smaller (or larger) than intended, turning what looks like a harmless integer bug into a full memory-corruption primitive one step later.
In any of these variants, successful exploitation in a network-facing service means remote code execution with the privileges of the vulnerable process — historically one of the most severe outcomes in software security, and the reason this whole family still commands top-tier bounties despite being a well-understood bug class.
Prevention & Remediation
Prevention and Secure Design
Preventing Off-by-one Error takes a defense-in-depth approach — no single control below is sufficient alone, but together they close off both the primary path and the most common bypasses.
Bounds-check every buffer operation explicitly. Never assume a length value is safe because it came from a trusted-looking source — validate it against the actual allocated size immediately before the operation that uses it.
Use safe, bounds-checked APIs. Replace strcpy/sprintf/gets-style functions with their bounds-checked equivalents (strncpy, snprintf, and similar) throughout, not just where a specific report pointed.
Check arithmetic before it feeds a size calculation. Validate that a computed buffer size or index can't overflow or underflow before it's used to allocate or index memory — this is what stops an integer bug from becoming a memory-corruption bug.
Fuzz with sanitizers attached. AddressSanitizer and similar tools turn what would otherwise be a silent, hard-to-reproduce corruption into an immediate, debuggable crash during testing.
Keep compiler exploit mitigations enabled. Stack canaries, ASLR, and DEP/NX don't prevent the bug but substantially raise the bar for turning it into reliable exploitation.