VNX-170: Improper Null Termination
Reports C/C++ strncpy, memcpy(dst, src, strlen(src)) and read/readlink/recv/fread calls that can leave a buffer without a NUL terminator when nothing terminates it before it is used as a string.
| Field | Value |
|---|---|
| Rule ID | VNX-170 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | C, C++ |
| CWE | CWE-170 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:H/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | null-termination memory cwe-170 |
| Source | internal/sast/rules/vnx-170.rego |
Overview
VNX-170 maps to CWE-170: Improper Null Termination. It reports three shapes in C and C++ files:
strncpy(dst, src, n)with a count that can consume the whole buffer (sizeof dst,strlen(src), a variable or a constant) and nodst[...] = '\0'(or*p = '\0') within the next four lines. Withn - 1it is reported only when the buffer was not zeroed first (memset,bzero,= {0},calloc, static storage).memcpy(dst, src, strlen(src)), ormemcpy(dst, src, len)withlen = strlen(src)a few lines above and unchanged, which copies the characters but not the terminator, with no terminator after it.read/readlink/recv/recvfrom/freadthat fill achararray withsizeof(buf)bytes, after which the buffer is used as a string (strlen,strcmp,strstr,atoi/strtol,sscanf,printf("%s"),std::string,std::string s(buf),<<) within eight lines withoutbuf[n] = '\0'.
Not reported: a string-literal source, a count of strlen(src) + 1, a length that is clamped before the copy or returned/stored with the bytes (a counted buffer, such as an OpenSSL password callback), an n - 1 strncpy whose last byte was terminated just before it, strlcpy/snprintf, a read of sizeof(buf) - 1, a buffer read as bytes only, strncpy wrapper functions, strncat (which always terminates), comments and string literals.
Why This Matters
strncpy does not write a terminator when the source is at least as long as the count, and read never writes one. The next strlen, printf("%s") or strcpy then runs past the end of the buffer, reading or copying adjacent memory: an information leak at best, an overflow at worst.
What Gets Flagged
// FLAGGED: no room for, and no write of, the terminator
strncpy(u->name, input, sizeof(u->name));
// FLAGGED: buffer filled completely, then parsed as a string
if (read(fd, buf, sizeof(buf)) < 0)
return -1;
return atoi(buf);
// FLAGGED
memcpy(out, tok, strlen(tok));
// FLAGGED: readlink never terminates
ssize_t n = readlink("/proc/self/exe", target, sizeof(target));
printf("running %s\n", target);
// NOT FLAGGED: terminated
strncpy(dst, src, dstlen);
dst[dstlen - 1] = '\0';
// NOT FLAGGED: zeroed buffer, copy leaves the last byte
char local[40] = {0};
strncpy(local, src, sizeof(local) - 1);
Remediation
- Use
strlcpyorsnprintf(dst, size, "%s", src), which always terminate. - After
strncpy, writedst[size - 1] = '\0'. - Read at most
sizeof(buf) - 1bytes and setbuf[n] = '\0'with the returned count.
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-170
// nosec vnx-170
Or record an ignore rule: vulnetix ignore add --rule vnx-170 --reason "...". See Suppressing findings.