C Basics: Detect Truncated Formatting with snprintf
A bounded write can still produce an incomplete identifier. Format a short and a long name, accepting the result only when it fits.
Prepare a scratch directory
Tested on Linux x86-64 with GCC 14.2.0. You need GCC, the C development headers, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. This example uses the C standard library and no external package.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as snprintf-truncation-demo.c:
#include <stdio.h>
#include <stdlib.h>
static int format_label(char *destination, size_t capacity, const char *name) {
return snprintf(destination, capacity, "user:%s", name);
}
int main(void) {
char label[12];
const char *names[] = {"Ada", "Alexandria"};
for (size_t index = 0; index < 2; ++index) {
int required = format_label(label, sizeof label, names[index]);
if (required < 0) {
fputs("Formatting failed\n", stderr);
return EXIT_FAILURE;
}
if ((size_t)required >= sizeof label) {
printf("Rejected: needs %d bytes plus terminator\n", required);
} else {
printf("Accepted: %s\n", label);
}
}
return EXIT_SUCCESS;
}Compile and run it. The && prevents running an old executable if compilation fails:
gcc -std=c17 -Wall -Wextra -Werror -pedantic snprintf-truncation-demo.c -o snprintf-truncation-demo &&
./snprintf-truncation-demoObserved output:
Accepted: user:Ada
Rejected: needs 15 bytes plus terminatorRead the result
The return value excludes the terminating null byte and describes the complete result that was needed. A negative return is an error. Only after that check is conversion to size_t safe; a value at least as large as the buffer capacity means truncation.
Apply it carefully
The capacity must describe the real writable array. sizeof works here because label is an array in this scope; sizeof a pointer does not measure its allocation. Keep the format string fixed, pass untrusted text through %s, and do not overlap input and destination. Truncating text can split an encoded character or change an identifier, so this example rejects it.
The library reference describes the relevant behavior.
Clean up
Remove only the source and executable created here, then remove the empty scratch directory:
rm -- snprintf-truncation-demo.c snprintf-truncation-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect what remains before removing anything else.
Acknowledgements
This post was created with assistance from AI.