CWE-193, Off-by-one Error, means a product calculates or uses a maximum or minimum value that is one more, or one less, than the correct value. It is a base level weakness MITRE associates mostly with C, though the logic can occur anywhere.
How it happens
Classic sources are loop bounds with the wrong comparison, array indexes at the length instead of length minus one, and forgetting space for a terminating null byte.
// Vulnerable: needs len + 1 bytes for the terminator
char *copy = malloc(strlen(src));
strcpy(copy, src); // writes one byte past the end
// Fixed
size_t n = strlen(src) + 1;
char *copy = malloc(n);
memcpy(copy, src, n);
// Vulnerable loop bound
for (int i = 0; i <= N; i++) { a[i] = 0; } // a has N elements
MITRE's observed examples show how serious a single byte can be. CVE-2003-0252 allowed remote attackers to cause a denial of service and possibly execute arbitrary code via requests that do not contain newlines. CVE-2001-1391 was an off-by-one vulnerability in a driver that allowed users to modify kernel memory. CVE-2002-0083 let local users or remote malicious servers gain privileges. CVE-2002-0653 was an off-by-one buffer overflow in a server function that allowed local users to execute code as the server user via .htaccess files with long entries.
What an attacker gains
MITRE lists:
- Availability: crashes and instability, with infinite loops likely when loop indexes are involved.
- Integrity: modified memory.
- Confidentiality, availability and access control: execution of unauthorized code, though MITRE says this is usually outside the implicit security policy.
How to find it
MITRE says automated static analysis (SAST) can find some instances by modelling data flow. In review, look at every boundary comparison and every allocation with a string length, and ask whether the terminator is counted. Run tests with a memory sanitizer, since single byte overruns often pass silently otherwise.
How to fix it
The record lists no mitigations, so these are general steps.
- Use bounded, length aware APIs, and pass the destination size, not the source length.
- Prefer half open ranges, written i less than N, and be consistent.
- Write boundary tests at length minus one, length, and length plus one.
- Use memory safe languages or containers where you can.
Quick check
What is the most common off-by-one with strings?
Forgetting that C strings need one extra byte for the terminating null.