Skip to content

Beginner · Strings in C · topic 20 of 60 · 30 min

Buffer overflows and safe string handling

Explanation

A buffer overflow happens when a program writes past the end of an array, overwriting other data. In C it is undefined behaviour and one of the most exploited classes of security bugs. Prevent it by tracking sizes everywhere, using bounded functions (fgets, snprintf), checking lengths before copying, and turning on compiler protections and AddressSanitizer while testing.

What happens in memory

output
void greet(const char *input) {
    char name[8];
    strcpy(name, input);     /* no size check */
    printf("Hi %s\n", name);
}
/* greet("Ravishankar Iyer") writes 17 bytes into an 8-byte array */

stack (simplified, addresses grow downward):
| other local data      |
| name[8]               |  ← the copy starts here…
| saved frame pointer   |  ← …and continues over these
| return address        |  ← overwriting this can redirect the program
Writing past name corrupts whatever lies next to it, possibly the address the function returns to.

If an attacker controls the input, overwriting the return address can make the program jump to code of the attacker's choosing. This "stack smashing" technique has been behind many famous incidents, from the 1988 Morris worm to bugs found in routers and media players today. Heap overflows (module 6) are just as dangerous.

Unsafe and safer functions

AvoidUse insteadWhy
gets(buf)fgets(buf, sizeof buf, stdin)gets has no size limit (removed in C11)
strcpy(dst, src)snprintf(dst, size, "%s", src), or check strlen firststrcpy never checks
strcat(dst, src)snprintf, or strncat with the REMAINING spacestrcat never checks
sprintf(buf, …)snprintf(buf, sizeof buf, …)sprintf has no size
scanf("%s", buf)scanf("%19s", buf) or fgetsunbounded %s

Habits that prevent overflows

  1. Pass the buffer size along with every buffer: void copy_name(char *dst, size_t dst_size, const char *src).
  2. Check lengths before copying: if (strlen(src) >= dst_size) reject or truncate deliberately.
  3. Use sizeof buf at the point where buf is declared as an array; inside functions you only have the size you were passed.
  4. Validate input lengths at the boundary (where data enters the program).
  5. Handle truncation: snprintf returns the length it wanted to write; compare with the size.
c
int copy_name(char *dst, size_t dst_size, const char *src) {
    size_t len = strlen(src);
    if (len >= dst_size) return -1;       /* would not fit: refuse */
    memcpy(dst, src, len + 1);            /* copy including '\0' */
    return 0;
}

Compiler and runtime protections

bash
gcc -std=c17 -Wall -Wextra -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-strong prog.c -o prog
gcc -std=c17 -g -fsanitize=address,undefined prog.c -o prog_test   # while testing
  • -fstack-protector-strong places a "canary" value before the return address and aborts if it changes ("stack smashing detected").
  • -D_FORTIFY_SOURCE=2 (needs optimisation, -O1 or higher) makes glibc check sizes in strcpy, memcpy and friends where the size is known at compile time.
  • AddressSanitizer (-fsanitize=address) reports the exact line of any out-of-bounds read or write during testing (module 11).
  • Operating-system defences (ASLR, non-executable stacks) make exploitation harder, but they do not fix the bug.
Careful: Protections are a safety net, not a fix. Code with an overflow is still broken: it may corrupt data silently where no canary is checked. Fix the size handling itself, and test with sanitizers. Only test exploits on your own programs or on legal practice platforms; attacking systems you do not own is a crime (for example under India's IT Act, 2000, sections 43 and 66).
Think of it like this: A buffer overflow is like pouring 2 litres of water into a 1-litre bottle placed on top of your phone and wallet: the extra does not disappear, it spills onto whatever is next to the bottle. A canary is like a tissue placed around the bottle: if it gets wet, you know something spilled, but the phone may already be damaged.
Tip: Some coding standards used in industry (for example SEI CERT C and MISRA C) ban the unbounded functions outright. Adopting their string rules early makes your code review-ready for embedded and security-sensitive jobs.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Buffer overflows and safe string handling · 1/6

Buffer overflows

In this lesson you will learn what buffer overflows are and how to prevent them.

Prefer a video by a teacher? Find free videos on “buffer overflow explained c stack smashing prevention” on YouTube ↗

In real life: A router firmware bug

Security researchers have repeatedly found home-router web interfaces that copy a form field (such as a device name) into a fixed-size stack buffer with strcpy. A long device name crashes the router, and a carefully crafted one can take it over. The fix in each case was simple: check the length and use a bounded copy.

c
/* vulnerable */
char devname[32];
strcpy(devname, form_value);

/* fixed */
if (strlen(form_value) >= sizeof devname) return HTTP_400_BAD_REQUEST;
memcpy(devname, form_value, strlen(form_value) + 1);

Practice (0/3 done)

Task 1 · Question

Which call can overflow buf (declared char buf[16])?

Task 2 · Question

A program aborts with "*** stack smashing detected ***". What does it mean?

Task 3 · Practice

Take this function: void save_city(char *out, const char *in) { strcpy(out, in); }. Rewrite it as int save_city(char *out, size_t out_size, const char *in) that refuses (returns -1) when the input does not fit, and test it with a 10-byte buffer and inputs "Pune", "Thiruvananthapuram" and "". Then compile the ORIGINAL version with -fsanitize=address and call it with the long input to see the report.

Quick quiz

Question 1

1.What is a buffer overflow?

Question 2

2.Why is overwriting the return address dangerous?

Question 3

3.What does -D_FORTIFY_SOURCE=2 need to work?

Question 4

4.Which is the best fix for an overflow found by a sanitizer?

Read more (free)

Before this, learn

This topic builds on these. If something here feels new, open them first.

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is a buffer overflow?

Answer: Writing past the end of an array into neighbouring memory

It corrupts other data.

Why is overwriting the return address dangerous?

Answer: It can redirect execution to an attacker's choice

Classic stack smashing.

What does -D_FORTIFY_SOURCE=2 need to work?

Answer: Optimisation (-O1 or higher)

The checks are added during optimisation.

Which is the best fix for an overflow found by a sanitizer?

Answer: Track sizes and use bounded functions with length checks

Fix the size handling.

Which tool is typically faster: valgrind or AddressSanitizer?Related

Answer: AddressSanitizer

ASan instruments at compile time.

From Memory leaks and other heap bugs

Should sanitizer builds be shipped to customers?Related

Answer: No; use them in development and testing

Ship normal hardened builds.

From Sanitizers and warnings

Does unsafe turn off the borrow checker?Related

Answer: No; it only allows five specific extra actions

References are still checked inside unsafe.

From Unsafe Rust: what it is and is not

fgets returns NULL when…Related

Answer: End of input is reached (or an error occurs) before reading anything

Check it before using the buffer.

From Reading strings safely

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Strings in C”. The module exam comes next.
  2. Module 4 exam: Strings in C · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Pointers. 60 topics left in the course before the final exam.

← Previous: Characters and <ctype.h>