A function like this one does not look dangerous at first. It compiles without any error, works fine in testing, and runs correctly for months — until one day someone sends a longer piece of text than expected, and things break. Below is the function as it was originally written, and then we go through it line by line to understand what it is actually doing.
#include <stdio.h> #include <string.h> void log_token(char *token) { char buffer[16]; int i = 0; while (token[i] != '\0') { buffer[i] = token[i]; i++; } buffer[i] = '\0'; printf("Logged token: %s\n", buffer); }
Line by line
These two lines just bring in some ready-made tools — one for printing text (printf), one for handling strings. Nothing to worry about here, just the basic toolbox.
This starts a function called log_token. It takes one input, token — some text given to it. The function does not return anything back.
This creates a box named buffer that can hold exactly 16 characters — not one more. Remember this number, 16, because everything below has to respect this limit.
This is just a counter, starting at 0, to keep track of how far we have copied.
This loop keeps running until it reaches the end of token (marked by '\0'). But notice — it only checks whether token has ended. It never checks whether buffer is already full. That is the real problem.
This copies one character from token into buffer, then moves the counter forward by one. Simple copying, nothing fancy.
Once copying is done, this adds an end-marker to buffer, so it becomes a proper, complete piece of text.
This simply prints out whatever is inside buffer.
Verdict
The loop copies every character from token into buffer, without ever checking whether buffer (which holds only 16 characters) is already full. If token is longer than 15 characters, the extra characters spill over into the memory sitting right next to buffer — memory that can even include important information the program needs, like where to jump back to after this function finishes. This is exactly why such bugs feel "random" in production: short inputs never trigger it, so it passes normal testing quietly, and only shows up once someone (by mistake or on purpose) sends a longer piece of text.
- Class
- Stack buffer overflow
- Reference
- CWE-121
- Trigger
- Any
tokenlonger than 15 characters - Impact
- Memory corruption; potential control-flow hijack if the overflow reaches the return address
What it looks like in memory
Here is the same idea, drawn out. On the left, buffer holds a short token — everything fits, nothing is touched outside the box. On the right, a longer token is copied in without any check, and the extra characters spill past buffer's edge into memory that belongs to something else — in this case, the saved return address the function needs to jump back to safely.
The fix
The loop needs one more check — not just "has token ended," but also "is buffer already full."
void log_token(char *token) { char buffer[16]; int i = 0; while (token[i] != '\0' && i < 15) { buffer[i] = token[i]; i++; } buffer[i] = '\0'; printf("Logged token: %s\n", buffer); }
Why 15, not 16
buffer has 16 boxes in total. But we cannot fill all 16 with actual text — one box must stay empty, kept aside for the end-marker ('\0') that gets added after the loop. So we stop the loop at 15, allowing only 15 characters of real text, leaving the 16th box free for that end-marker. If we had used 16 instead of 15, the loop would fill every box, and then trying to add the end-marker afterward would spill one byte outside the box — same mistake, just smaller.
strncpy(buffer, token, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; — this does the same job in two lines, and there is no counter to manage by hand, so less chance of making this mistake again. If cutting off long tokens silently is itself not acceptable for your use-case, then instead check the length of token right at the start, and reject it or handle it properly, rather than silently cutting it short.