Intermediate · Dynamic memory · topic 29 of 60 · 30 min
Memory leaks and other heap bugs
Explanation
The bug catalogue
/* 1. Leak: the only pointer is lost */
char *buf = malloc(100);
buf = malloc(200); /* the first 100 bytes can never be freed */
/* 2. Use after free */
int *p = malloc(sizeof *p);
free(p);
*p = 5; /* writes into memory that may belong to someone else now */
/* 3. Double free */
free(p);
free(p); /* corrupts the allocator; often an immediate crash */
/* 4. Heap buffer overflow */
int *a = malloc(4 * sizeof *a);
a[4] = 1; /* one past the end */
/* 5. Freeing what was not allocated */
int x;
free(&x); /* x is on the stack */
/* 6. Uninitialised read */
int *q = malloc(sizeof *q);
if (*q > 0) { } /* garbage decides the branch */valgrind
gcc -g -O0 prog.c -o prog
valgrind --leak-check=full ./prog==12345== Invalid write of size 4
==12345== at 0x10916D: main (prog.c:14)
==12345== Address 0x4a8e050 is 0 bytes after a block of size 16 alloc'd
==12345== at 0x483B7F3: malloc (vg_replace_malloc.c:309)
==12345== by 0x109156: main (prog.c:13)
…
==12345== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345== at 0x483B7F3: malloc (vg_replace_malloc.c:309)
==12345== by 0x109146: main (prog.c:9)AddressSanitizer
gcc -g -fsanitize=address prog.c -o prog && ./prog
==… ==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010
WRITE of size 4 at 0x602000000010 thread T0
#0 main prog.c:20
freed by thread T0 here:
#1 main prog.c:19
previously allocated by thread T0 here:
#1 main prog.c:18Habits that prevent heap bugs
- Pair every allocation with exactly one free, ideally in the same function or a matching destroy function.
- Set pointers to NULL after free; free(NULL) is safe, double free of NULL cannot happen.
- Keep sizes next to buffers and check indexes.
- On error paths, free what was already allocated (a single clean-up section at the end of the function, sometimes with goto, is common in C).
- Run tests under ASan or valgrind regularly, for example in CI.
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
Memory leaks and other heap bugs · 1/6
In this lesson you will learn the classic heap bugs and the tools that find them.
Prefer a video by a teacher? Find free videos on “valgrind memcheck tutorial memory leak use after free addresssanitizer” on YouTube ↗
In real life: A leak in a long-running logger
A logging daemon on a payment gateway formatted each log line into a freshly malloc'd buffer and forgot to free it on one error path. Memory grew by a few MB per hour, and the service restarted every two days. valgrind --leak-check=full pointed to the exact malloc line; adding the missing free fixed it.
char *line = format_line(event); /* allocates */
if (!line) return -1;
if (write_log(line) != 0) {
free(line); /* the missing free on the error path */
return -1;
}
free(line);
return 0;Practice (0/3 done)
valgrind reports "100 bytes in 1 blocks are definitely lost". What kind of bug is it?
Which line is a use-after-free? 1: char *s = malloc(8); 2: strcpy(s, "upi"); 3: free(s); 4: s[0] = 'U';
Write a deliberately buggy C program containing one leak, one heap buffer overflow and one use-after-free. Run it with -fsanitize=address and with valgrind (on Linux or WSL), and copy the key line of each report. Then fix the three bugs and show clean reports.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Dynamic strings and 2D arraysDynamic memory · same module
- malloc, calloc and freeDynamic memory · same module
- Finding memory bugs with valgrindDebugging and tools
- Memory 101: the stack and the heapRust Programming · Ownership: Rust's big idea
- Sanitizers and warningsDebugging and tools
- Pointers, the stack and the heapC++ Programming (modern C++ and DSA) · References, pointers, arrays and strings
Questions people ask about this
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 does a double free usually cause?
Answer: Heap corruption, often a crash or a security hole
The allocator's bookkeeping is corrupted.
Which tool is typically faster: valgrind or AddressSanitizer?
Answer: AddressSanitizer
ASan instruments at compile time.
Why compile with -g when using these tools?
Answer: So reports show file names and line numbers
Debug info maps addresses to source lines.
Which is NOT a heap bug?
Answer: Integer division by zero
That is an arithmetic error, not a heap bug.
On which systems does valgrind run best?Related
How does Rust decide when to free heap memory?Related
Answer: Ownership rules let the compiler insert the free at the right point
Rust frees a value when its owner goes out of scope, decided at compile time.
Roughly how much does ASan slow a program down?Related
What is a buffer overflow?Related
Answer: Writing past the end of an array into neighbouring memory
It corrupts other data.
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 more topic in “Dynamic memory” before its exam: Dynamic strings and 2D arrays.
- Module 6 exam: Dynamic memory · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Structs, unions, enums and files. 60 topics left in the course before the final exam.