Buffer not guaranteed null-terminated
| Vulnerability potential | High |
| DDoS potential | Low |
strncpy / strncat does not write a terminating NUL when the source reaches the size limit; subsequent strlen / printf-style use can read past the buffer
Impact
strncpy(dst, src, n) copies at most n bytes and writes a terminating '\0'
only if src is shorter than n. When the source is n bytes or longer,
the destination is filled but left without a terminator. Any later use that
relies on a terminator — strlen, printf("%s"), strcat, passing the buffer
to a C API — then reads past the end of the buffer, scanning memory until it
happens upon a zero byte. The consequences are an out-of-bounds read that
returns adjacent stack/heap contents, a wrong (too-long) length, or a crash
when the scan walks into an unmapped page.
Vulnerability potential
This is a real memory-safety defect (CWE-170 missing null termination / CWE-125 out-of-bounds read).
- Information disclosure.
printf/sendof a non-terminated buffer emits everything from the buffer up to the next zero byte, leaking adjacent stack or heap memory — other variables, pointers (defeating ASLR), or secrets — to an output the attacker can observe. - Secondary overflow. A bogus over-long
strlenresult fed into a subsequentmemcpy/allocation size turns the over-read into an over-write, i.e. a buffer overflow on the next operation. - Crash. When the unterminated scan reaches an unmapped page it segfaults, an availability impact (the secondary DoS weight); the disclosure/corruption risk is the dominant one, hence the high rating.
Technical details
strncpy is not a safe strcpy
strncpy was designed for fixed-width fields (old UNIX directory entries), not
for safe string copying. Its two documented hazards are: it does not
NUL-terminate on truncation, and it zero-pads the entire remainder when the
source is short (a performance, not safety, surprise). strncat has a related
trap: its n is the number of bytes to append, not the destination size, so it
always writes a terminator but can still overflow if n is miscomputed.
Correct alternatives
- Explicitly terminate:
strncpy(dst, src, n - 1); dst[n - 1] = '\0';. - Use size-aware copies that always terminate:
snprintf(dst, n, "%s", src), or the boundedstrlcpy/strscpy(BSD/Linux) which guarantee a terminator. - In C++, avoid raw buffers entirely:
std::string/std::string_viewcarry their own length and never depend on a terminator.
Why it passes tests
With short test inputs the source is always under the limit, so the terminator is written and the bug is invisible; it only triggers at exactly the boundary length, which fuzzing or adversarial input reaches but unit tests rarely do.
Catching the issue
Sanitizers
AddressSanitizer reports the out-of-bounds read when strlen/printf scans
past the buffer (especially with a heap-allocated destination). Valgrind
Memcheck flags the invalid read similarly.
Fortify / compiler
_FORTIFY_SOURCE=2/3 with optimization adds runtime checks to the str*
family; GCC/Clang -Wstringop-truncation warns specifically about a strncpy
whose result may be unterminated, and -Wstringop-overflow covers strncat
miscalculations.
Static analysis
clang-tidy bugprone-not-null-terminated-result and the CERT rules
(cert-str32-c, “do not pass a non-null-terminated character sequence to a
library function”) flag the pattern; Coverity has a dedicated
STRING_NULL checker.
How to reproduce
Build with -fsanitize=address; the name exactly fills dst so no
terminator is written, and printf("%s") over-reads (ASan reports
stack-buffer-overflow / heap-buffer-overflow).
#include <cstring>
#include <cstdio>
#include <cstdlib>
int main() {
const char* name = "0123456789ABCDEF"; // 16 chars, no room for NUL
char* dst = (char*)malloc(16);
strncpy(dst, name, 16); // BUG: fills 16 bytes, no '\0'
printf("%s\n", dst); // over-reads past dst
free(dst);
// Fix: char* dst = malloc(17); strncpy(dst, name, 16); dst[16] = '\0';
}