`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.
A critical buffer overflow in `create_hex_string()` within `hmlangw.cpp` let an unconditional 16-iteration loop write past the bounds of a 100-byte `hex` buffer using unchecked `sprintf` calls. The fix replaces `sprintf` with `snprintf` and caps the loop iterations based on the actual destination buffer size, closing off a memory corruption path reachable from serial or network input.
A high-severity vulnerability was identified in `plugin/bin/install.c` where `strcpy()` and `strncpy()` were used to handle path strings without proper bounds checking or guaranteed null-termination. The fix replaces `strcpy()` with direct character assignment and `strncpy()` with `snprintf()`, eliminating both buffer overflow and missing null-terminator risks in the plugin installation workflow.
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 high-severity buffer overflow vulnerability was discovered in `tools/claude-vscode-wrapper.c`, where an unbounded `strcpy()` call copied a file path into a fixed-size `MAX_PATH` buffer without any size validation. The fix replaces `strcpy()` with `snprintf()` and swaps `malloc()` for `calloc()`, ensuring both string operations and memory allocation are bounds-safe and zero-initialized.
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