`strcpy`, `strcat` and `sprintf` take no destination size, so they write until they find a NUL byte in the source — an attacker-controlled length becomes a stack or heap overwrite. Replace them with `snprintf` for formatting, and `strlcpy`/`strlcat` (or `snprintf(dst, size, "%s", src)` where those are unavailable) for copies. The bounded functions are only safe if you check the result: `snprintf` returns the length it *would* have written, `strlcpy` returns the source length, and both silently truncate when that exceeds the buffer. `strncpy` is not a safe `strcpy` — it does not NUL-terminate on truncation.
| Languages | C, C++ (and any FFI boundary that hands a raw buffer to C) |
| Unbounded functions | strcpy, strcat, sprintf, vsprintf, gets, scanf("%s") |
| Bounded replacements | snprintf, vsnprintf, strlcpy, strlcat, memcpy_s, fgets |
| Deceptively unsafe | strncpy (no NUL on truncation), strncat (bound is remaining space, not total), sizeof on a pointer parameter |
| Typical impact | Return-address or vtable overwrite leading to code execution; adjacent-field corruption; crash |
| Compiler help | -D_FORTIFY_SOURCE=3 -fstack-protector-strong -Wformat-security -fsanitize=address |
Vulnerable
void handle(const char *user_input) {
char name[64];
strcpy(name, user_input); /* writes strlen(user_input)+1 bytes */
log_name(name);
}Secure
int handle(const char *user_input) {
char name[64];
int n = snprintf(name, sizeof name, "%s", user_input);
if (n < 0 || (size_t)n >= sizeof name) {
return -1; /* truncated — reject rather than proceed */
}
log_name(name);
return 0;
}snprintf always NUL-terminates and returns the length it would have written, so `n >= sizeof name` is the truncation test. Ignoring the return value converts an overflow into silent data loss, which is better but still a bug.
Vulnerable
char dst[16];
strncpy(dst, src, sizeof dst); /* no NUL if strlen(src) >= 16 */
printf("%s\n", dst); /* reads past the buffer */Secure
char dst[16];
if (strlcpy(dst, src, sizeof dst) >= sizeof dst) {
return -1; /* source did not fit */
}
/* Where strlcpy is unavailable (glibc before 2.38, MSVC): */
char dst2[16];
dst2[0] = '\0';
if (snprintf(dst2, sizeof dst2, "%s", src) >= (int)sizeof dst2) {
return -1;
}strncpy was designed for fixed-width records, not for strings: it pads with NUL when the source is short and omits the terminator when the source is long. Neither behaviour is what a caller replacing strcpy expects.
Vulnerable
void copy_into(char *dst, const char *src) {
/* dst is a pointer here; sizeof dst is 8, not the array's size. */
snprintf(dst, sizeof dst, "%s", src);
}Secure
void copy_into(char *dst, size_t dst_size, const char *src) {
snprintf(dst, dst_size, "%s", src);
}
/* Call site keeps the size next to the array, where sizeof still works. */
char buf[128];
copy_into(buf, sizeof buf, src);This is the most common way a correctly-written bounded call still overflows. Pass the size alongside every buffer; `-Wsizeof-pointer-memaccess` catches some cases but not all.
Each of these is a pull request Orbis AppSec opened against a real open-source repository.
The shared memory handler in Aardvark.Cef.Process.Core failed to validate that the `length` parameter passed from JavaScript did not exceed the destination buffer size before calling `memcpy`. This allowed a compromised Chromium renderer process to corrupt heap memory by supplying an oversized `handle->length` value to the `openMapping` function. The fix adds explicit bounds checking using `GetArrayBufferByteLength()` before the copy operation.
The `XShmGetImage` function in the X11 shared memory image path copies pixel data row-by-row using `memcpy` without validating that the source image height matches the destination buffer height. An attacker or compromised server could provide an oversized source image, causing writes beyond the allocated heap buffer and triggering heap corruption or code execution.
The Intel SGX enclave's trusted bridge functions `ecall_store_data` and `ecall_retrieve_data` used `memcpy()` to move data into and out of a fixed 256-byte `secure_storage` buffer without validating that `data_len` fit within destination boundaries. An attacker providing oversized `data_len` values could corrupt enclave memory, breaking SGX's confidentiality guarantees.
A critical buffer overflow vulnerability was discovered in `buildroot-external/package/libxmlparser/xmlParser.cpp`, where the `toXMLString` function used `_tcscpy()` to write XML escape sequences into a destination buffer without any bounds checking. An attacker supplying a crafted XML document could overflow the buffer and potentially execute arbitrary code. The fix replaces all five unsafe `_tcscpy()` calls with `memcpy()` calls that copy only the exact number of bytes required for each escape
A critical heap buffer overflow vulnerability was discovered in `TKLiveSync/unzip.cpp`, where ZIP archive entry names were copied into a `PATH_MAX`-sized heap buffer using `strcpy()` without any length validation. Since the ZIP specification allows entry names up to 65,535 bytes — far exceeding typical `PATH_MAX` values of 1,024 to 4,096 bytes — a crafted archive could overflow the buffer and corrupt heap memory. The fix replaces the unsafe `strcpy`/`dirname` pattern with `std::string` operation
A signed integer overflow in OpenCV's `bilateralFilter.cpp` allowed the buffer size calculation `cal_width * cal_height * cn` to wrap around to a small or negative value, causing `padding.resize()` to allocate far less memory than needed. Subsequent `memcpy` operations would then write beyond the allocated buffer, creating a heap corruption primitive. The fix is a single targeted cast to `size_t` that promotes the multiplication to unsigned 64-bit arithmetic before any overflow can occur.
A critical stack buffer overflow was discovered in `libuv/Learn-libuv/docs/code/tty-gravity/main.c` where `sprintf()` wrote ANSI escape sequences and user-controlled variables into a fixed 500-byte buffer without any bounds checking. An attacker controlling the `pos`, `width`, or `message` variables could overflow the stack, overwrite return addresses, and potentially achieve arbitrary code execution. The fix replaces `sprintf()` with `snprintf()` and adds explicit length validation to ensure wr
A high-severity buffer overflow risk was discovered in `login/main.c` where `strcpy()` was used to copy the `HOME` environment variable into a fixed-size 512-byte buffer without any bounds checking. An attacker controlling the `HOME` environment variable could overflow `pwd_file_name`, potentially corrupting memory or hijacking execution. The fix replaces the two-step `strcpy`/`strcat` pattern with a single, bounds-safe `snprintf` call.
No. `strncpy` copies at most n bytes but does not append a NUL when the source is n bytes or longer, so the destination is left unterminated and the next `strlen` or `printf("%s")` reads past the buffer. It also pads short sources with NUL bytes up to n, which makes it slow for large buffers. Use `snprintf` or `strlcpy`, both of which always terminate.
They are safe when available, but Annex K of C11 is optional and glibc has never implemented it, so portable code cannot rely on them. On MSVC they are the natural choice. Elsewhere `snprintf` is in every C99 implementation and `strlcpy` is in glibc 2.38+, musl, and the BSDs.
It turns some of them into aborts instead of memory corruption, which converts a possible code-execution bug into a denial of service. That is a real improvement and worth enabling everywhere, but it only covers cases where the compiler can see the destination's size, and an abort in production is still an outage. Fix the call.
It depends on what the string is for. A truncated log line is cosmetic. A truncated file path can resolve to a different file than the one that was checked, and a truncated hostname or permission string can pass a comparison it should have failed. Treat truncation as an error unless you can state why the shorter value is equivalent.
Orbis AppSec scans your GitHub repositories, traces the taint from source to sink, and opens a pull request with the fix applied and verified.
Try Orbis AppSecSee also: Buffer overflow fixes we shipped