CS210 · Block 2 · Lesson 16

Memory Bugs and valgrind

Memory leaks · Use-after-free · Out-of-bounds · Uninitialized reads · realloc pitfalls · Double-free · Reading valgrind output
At a glance: Yesterday you allocated and freed heap memory by hand; today you learn what happens when you do it wrong. The six classic memory bug classes — leak, use-after-free, out-of-bounds, uninitialized read, realloc-loses-original, double-free — and the tool that catches them: valgrind.
Contents
  1. Lesson Objectives
  2. Assigned Readings
  3. Pre-Class Work
  4. Lesson Materials and Overview
  5. In-Class Work
  6. After Class
1. Lesson Objectives

By the end of this lesson, cadets will be able to:

  1. Identify common C memory bugs: leaks, double-frees, use-after-free, out-of-bounds access, uninitialized reads, and realloc misuse. (Outcome 3)
  2. Run a C program under valgrind to detect memory errors. (Outcome 3, 7)
  3. Interpret valgrind output to locate and resolve memory bugs. (Outcome 3, 7)
2. Assigned Readings

Complete before class. The Beej sections set up the realloc material; the supplementary notes introduce valgrind, which is new to CS210.

Source Sections Why
Beej-C Chapter 12, §12.5 (realloc) and §12.6 (typical bugs) Continues from L15. realloc is the one heap call not yet covered; §12.6 lists the bug classes we will hunt today.
Supplementary notes valgrind primer (linked in Section 4) valgrind is new to CS210. The primer covers what it does, how to run it, and how to read the report it produces.
3. Pre-Class Work
Do this first
Plan on 25 to 35 minutes for the reading and the toolchain check. valgrind needs to be installed and runnable before class starts.

Pre-Class Checklist

1
Read Beej-C §12.5 and §12.6 plus the valgrind primer
Beej finishes up the heap with realloc, then lists the typical bugs. The valgrind primer (linked in Section 4) shows you what the tool looks like in action.
  • realloc and the loses-original pattern
  • Leak, use-after-free, out-of-bounds, uninitialized read, double-free
  • What valgrind does and the shape of its output
2
Install valgrind and verify it runs
In your terminal, install valgrind, then make sure it can run on any small program. Test with your hello.c from Lesson 1 if you still have it.
Run:
$ sudo apt update && sudo apt install valgrind
$ valgrind --version
$ gcc -g hello.c -o hello
$ valgrind ./hello
If anything goes wrong, schedule EI before class. valgrind setup problems will block today's lab.
3
Skim the In-Class section below
You will see a valgrind walkthrough widget. No need to use it yet; just know it is there and what it looks like.
4. Lesson Materials and Overview

Yesterday you learned the rules of the heap. Today is the day those rules get broken, and the day you learn to find the breaks. valgrind is a runtime tool that watches every allocation and every memory access your program makes. When something goes wrong, it tells you what kind of wrong, where it happened in your source, and (often) which earlier allocation is involved. Reading its output is a skill on its own.

Jump to
Slides Six Bug Classes realloc valgrind Workflow What valgrind Misses

Slides

The Lesson 16 deck covers the six bug classes, one slide per class, with the valgrind output for each. Then a workflow slide on reading the output, then an honest look at what valgrind cannot catch.

Open Lesson 16 slides → replace href with your SharePoint link

The six bug classes

These are the six classes valgrind catches reliably. They cover almost every heap-related crash you will write in this course. Memorize the names; the bug log in the lab asks for them.

Bug class What it means valgrind signal
Memory leak Allocated with malloc, never freed. No live pointer at program exit. definitely lost
Use-after-free Read or write through a pointer after that block was freed. Invalid read/write of size N
Out-of-bounds write Wrote past the end (or before the start) of an allocation. N bytes after a block
Uninitialized read Used a heap value before storing anything into it. Garbage in, garbage out. uninitialised value
realloc loses original Assigned realloc's result directly to the only pointer to the old block. On failure, the old block leaks AND the pointer is NULL. leak; pattern visible in source
Double-free Called free on the same block twice. Corrupts the heap. Invalid free / double free

realloc and the loses-original pattern

realloc(ptr, new_size) resizes an existing heap block. It may grow the block in place or copy the contents to a new larger block and free the old one. Either way, you must use its return value.

The trap is the natural-feeling pattern:

// WRONG: if realloc fails, ptr becomes NULL and the old block leaks
ptr = realloc(ptr, new_size);

If realloc fails, it returns NULL. The old block is still valid and still allocated; you just lost the only pointer to it. Now you cannot read from it and you cannot free it.

// RIGHT: use a temp pointer, check before assigning
char *tmp = realloc(ptr, new_size);
if (tmp == NULL) {
  // handle failure; ptr is still valid
  return -1;
}
ptr = tmp;

This is one of the bugs in today's lab.

The valgrind workflow

valgrind is not a debugger. You do not step through code with it. You run your program under it, and it watches every memory access. When it sees something suspicious, it prints a report.

$ gcc -g -Wall -Werror -o myprog myprog.c
$ valgrind --leak-check=full ./myprog

Two things matter. First, compile with -g so valgrind can show you file names and line numbers. Second, read the output from the top. Errors cascade. The first one is almost always the most useful; the rest are often consequences of the first.

The loop:

  1. Read the first error in the report.
  2. Follow the stack trace to the file:line where it happened.
  3. Change one thing.
  4. Recompile and rerun valgrind.
  5. Did the error class change? Good. Repeat until valgrind is silent.

What valgrind cannot catch

valgrind is a runtime tool, so it only sees the code paths your test inputs actually exercise. A bug that lives in an error-handling branch never visited by your tests stays hidden. Reading the code matters too.

valgrind also does not catch:

  • Stack overflows. Recursion that runs too deep.
  • Logic bugs. If your loop iterates 99 times instead of 100, valgrind is happy. Your output is wrong.
  • Race conditions. Two threads stepping on each other. Different tool (helgrind).
  • Reads of valid garbage. A heap value that WAS initialized, just to the wrong value. That is a logic bug.
  • File and socket leaks. Forgetting to close a file. Different tool (lsof).

A clean valgrind report does not mean your program is bug-free. It means your program is not making the specific class of mistakes valgrind watches for, on the specific inputs you ran.

5. In-Class Work

Two things to do in class. The widget builds your pattern recognition; the lab applies it under valgrind.

  • Work through the valgrind walkthrough below. Four buggy programs, three tasks each: classify the bug, locate it in the source, propose a fix. About 25 minutes.
  • Complete Lab 16: compliments.c. A small C program with six memory bugs, one of each class. Find them with valgrind, fix them, document them. Auto-graded through GitHub Classroom.

valgrind Walkthrough

Twelve tasks across four mini-programs. Read the source, read the valgrind output, and classify what is wrong. The point is to build the pattern: what does each bug class look like in the report?

Before you begin

Enter your name. It will appear on the completion screen so you can include it in your screenshot for the Lesson 16 reflection.

CS210 Lesson 16 · valgrind Walkthrough

Four small C programs. Each one has exactly one memory bug. Read the source on the left and the valgrind output on the right, then classify the bug, point to the line, and propose a fix.
Task 0 of 12
Program 1: the leaky greeter
Current task
Loading...
greeter.c
valgrind output
valgrind>
How this works: Each program is short enough to read in one sitting. The valgrind output on the right is what you'd see if you actually ran the program with valgrind --leak-check=full ./prog. Each task asks one specific thing: classify the bug (one or two words), point to the line in the source, or describe the fix. Mistype twice and the hint button will pulse.
6. After Class
  • Finish Lab 16 if you did not complete it in class. Push to GitHub Classroom before Lesson 17.
  • Submit the Lesson 16 reflection in Blackboard. It asks for your widget completion code and one short reflection prompt.
  • GR2 is tomorrow. The midterm covers Lessons 1 through 15. Today's tool material (valgrind) is not on GR2; the bug classes may appear as pattern recognition.
  • Review your old labs. Rerun any of them under valgrind. You may find bugs we did not catch the first time.
Need help? Schedule EI with your instructor. The fastest path is to bring your valgrind_before.txt and point at the specific error you do not understand. Pointing at a specific message gets you unstuck in five minutes; "my code does not work" takes thirty. Check the course Resources section first for valgrind primers and Git troubleshooting before scheduling.