Secure Coding in C · beginner · ~20 min

Buffer safety — bounded copies, every time

Replace every unbounded string write with a bounded equivalent.

Overview

'Buffer safety' is the discipline that every write to a buffer is bounded by the destination's size. The opposite — writing until the source ends — is what makes C famous for crashes and exploits.

Why it matters

Roughly half the CVEs in C tools historically trace back to unbounded string writes. Replacing them all is the single highest-leverage hardening change.

Core concepts

The forbidden list. gets, strcpy, strcat, sprintf, scanf("%s") — never call these on attacker-controlled data.

The safe list. fgets(buf, sizeof buf, stdin), snprintf(buf, sizeof buf, ...), strlcpy, strlcat. Each takes the destination size; each NUL-terminates.

strncpy is NOT strlcpy. strncpy will not NUL-terminate when the source is exactly as long as the buffer. Worst of both worlds; avoid.

Pentester mindset. A strcpy call into a fixed-size local buffer with attacker-controlled input is the textbook stack-overflow primitive. Auditors scan for it.

Defensive coding habit. Add -Wformat-security to your CFLAGS and treat warnings as errors. Compile with -D_FORTIFY_SOURCE=2 to catch some unsafe calls at compile time.

Syntax notes

/* good */
snprintf(dst, sizeof dst, fmt, ...);
strlcpy(dst, src, sizeof dst);
fgets(buf, sizeof buf, stdin);

Lesson

C's classic string functions (strcpy, strcat, sprintf, gets, scanf "%s") write until they hit a NUL byte in the source, without checking the destination size. Every CVE in the OWASP Top 10 'memory-safety' bucket traces back to one of those. The fix is mechanical: use the bounded equivalent everywhere.

Code examples

char buf[16];
snprintf(buf, sizeof buf, "%s", user_input);    /* truncates safely */
/* never: strcpy(buf, user_input); */

Line by line

char dst[64];
int n = snprintf(dst, sizeof dst, "User: %s (%d)", name, age);
if (n < 0 || (size_t)n >= sizeof dst){
    /* truncation — handle defensively */
}

Common mistakes

  • Using strncpy and thinking it's safe. It is not — won't NUL-terminate when src == n.
  • Forgetting the +1 for the NUL when malloc'ing strings.

Debugging tips

Compile every test build with -fsanitize=address. Any unbounded copy bug fires immediately with the offending line.

Memory safety

Bounded copies don't fix logic bugs (still need to check what's IN the buffer), but they eliminate the entire memory-corruption class.

Real-world uses

Every CGI script, every command-line tool that takes a username, every HTTP server. The single most-audited C bug class.

Practice tasks

  1. Audit a small C program for unsafe calls. 2. Rewrite each with snprintf. 3. Implement your own strlcpy and convince yourself the trailing NUL is always written.

Summary

Never use the forbidden list. Always use the safe list. Compile with -D_FORTIFY_SOURCE=2 -Wformat-security -fsanitize=address.

Practice with these exercises