C: preventing buffer overflows with snprintf

Copy text into a fixed buffer without writing past its end, and detect truncation.

C
#include <stdio.h>

static int copy_name(char *out, size_t capacity, const char *name) {
  int needed = snprintf(out, capacity, "%s", name);
  return needed >= 0 && (size_t)needed < capacity;
}

int main(void) {
  char buffer[8];

  if (copy_name(buffer, sizeof buffer, "Ada")) {
    printf("stored: %s\n", buffer);
  }

  if (!copy_name(buffer, sizeof buffer, "Programonic")) {
    printf("rejected: 11 characters do not fit in %zu bytes\n", sizeof buffer);
    printf("buffer still holds: %s\n", buffer);
  }
  return 0;
}
Output
stored: Ada
rejected: 11 characters do not fit in 8 bytes
buffer still holds: Program

Explanation

A buffer overflow writes beyond the end of an array. In C it is one of the most common causes of serious security flaws, because the extra bytes can overwrite neighbouring variables or a function's return address. Functions such as gets, strcpy, and sprintf do not know the size of the destination.

snprintf takes the buffer size and never writes more than that many bytes, including the terminating zero. It returns the length the full text would have needed, so a result at least as large as the buffer means the text was truncated. A program should check for that and treat it as an error or allocate more space.

Explore the dry run
Guided walkthroughReady
1#include <stdio.h>
2
3static int copy_name(char *out, size_t capacity, const char *name) {
4 int needed = snprintf(out, capacity, "%s", name);
5 return needed >= 0 && (size_t)needed < capacity;
6}
7
8int main(void) {
9 char buffer[8];
10
11 if (copy_name(buffer, sizeof buffer, "Ada")) {
12 printf("stored: %s\n", buffer);
13 }
14
15 if (!copy_name(buffer, sizeof buffer, "Programonic")) {
16 printf("rejected: 11 characters do not fit in %zu bytes\n", sizeof buffer);
17 printf("buffer still holds: %s\n", buffer);
18 }
19 return 0;
20}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
snprintf writes at most capacity bytes and returns the length the whole text needed.—
—
The text fits only if its length is smaller than the capacity, leaving room for the zero byte.—
—
Ada needs 4 bytes with the terminator, which fits in 8.—
stored: Ada
snprintf truncated the text to 7 characters plus the terminator, so no memory outside the buffer was touched. The program still reports the failure.—
stored: Ada
rejected: 11 characters do not fit in 8 bytes
buffer still holds: Program

Cost and common mistakes

O(n) for copying n characters; the size check is O(1).

  • strncpy does not always add a terminating zero, so a truncated result is not a valid string. Prefer snprintf, or add the terminator yourself.
  • Truncated data can be wrong in a security-relevant way, for example a cut-off file path. Treat truncation as an error.
  • Modern compilers and sanitizers (for example AddressSanitizer) help find overflows, but correct size checks come first.

Try it yourself

Why compare needed with capacity using < and not <=?

Reveal answer and reasoning

The terminating zero needs one byte, so a text of length capacity would need capacity + 1 bytes in total and does not fit.

Further reading