Worked examples — Valgrind — detecting memory errors
5.1.19 · D3· Coding › C Programming › Valgrind — detecting memory errors
Shuru karne se pehle, vocabulary ka ek picture. malloc tumhe ek block deta hai — heap bytes ki ek contiguous strip (dekho Stack vs Heap). Us strip ke har byte pe do invisible tags hain jo Memcheck maintain karta hai:
Upar wali strip dekho. Green = A-bit set (tum ise touch kar sakte ho). Hatching = V-bit undefined (byte addressable hai lekin garbage hold karta hai — tum ise touch kar sakte ho, lekin value meaningless hai jab tak tum kuch likhte nahi). Red = A-bit clear (ise touch karna ek error hai). Neeche har example bas yahi hai — "code ne kaunse coloured region ko poke kiya, aur kaise?"
The scenario matrix
Har Memcheck bug is table ka ek cell hai. Direction column geometric axis hai — "block ki kaunsi side" (sign, hamaara "quadrant" yahaan); Kind column batata hai ki teeno sawaalon mein se kaunsa fail hua.
| # | Cell class | Concrete trigger | Question failed |
|---|---|---|---|
| 1 | Overflow — past the end (positive side) | N-element array pe a[N] |
A-bit |
| 2 | Underflow — before the start (negative side) | a[-1] |
A-bit |
| 3 | Use-after-free (red block ko touch karna) | free(p) ke baad *p read karna |
A-bit |
| 4 | Double free (red ko do baar free karna) | free(p); free(p); |
A-bit (freelist) |
| 5 | Uninitialised value control flow mein use karna | unwritten heap pe if (*p) |
V-bit |
| 6 | Uninitialised value sirf copy karna (the no-error case) | int y = x; unused |
none (V travels) |
| 7 | Zero / degenerate: malloc(0), free(NULL) |
boundary allocation sizes | none / defined |
| 8 | Leak — definitely lost | single pointer ko overwrite karna | Leak |
| 9 | Leak — indirectly lost | ek struct jo children own karta tha use lose karna | Leak |
| 10 | Leak — still reachable (the harmless case) | global pointer left unfreed | Leak (benign) |
| 11 | Real-world word problem | ek parser jo ek branch bhool jaata hai | mixed |
| 12 | Exam twist | kyun -O2 bug ko chhupaata hai |
tooling |
Do "no-error" cells (6, 7, 10-benign) utni hi zaruri hain jitni red wali: tumhe pata hona chahiye ki Valgrind jaanboojhkar kab silent rehta hai, warna tum ghosts ke peeche bhaagoge.
Example 1 — Overflow past the end (Cell 1, positive side)
Forecast: aage padhne se pehle andaaza lagao ki bad write kis byte offset pe padti hai.
- Block ke bytes count karo.
5 * sizeof(int)jabsizeof(int) = 4ho toh bytes milte hain, offsets se tak. Yeh step kyun? A-bits exactly pe set hain — us side pe baaki sab red hai. a[5]locate karo. Index matlab byte offset , 4 bytes likhna: offsets . Yeh step kyun? Offset block ke baad ka pehla byte hai — positive side pe pehla red byte.- A-bits check karo. Invalid write of size 4. Yeh step kyun? Parent ka rule: access OK har touched byte accessible ho. Byte 20 nahi hai.
Valgrind print karta hai: Address 0x... is 0 bytes after a block of size 20 alloc'd.
Verify: offset ; block size ; gap bytes after. ✓
Example 2 — Underflow before the start (Cell 2, negative side)
Forecast: a[-1] kaunsa byte likhta hai, aur kya yeh pehle hai ya baad mein?
- Offset compute karo. Index → byte offset , yaani offsets . Yeh step kyun? Negative index backwards chalta hai — Example 1 ka mirror image. Bahut se students is poore quadrant ko bhool jaate hain.
- A-bits check karo. Offset pe bytes block se pehle hain, A-bit → Invalid write of size 4. Yeh step kyun? Same rule, opposite direction; Memcheck dono edges guard karta hai (figure dekho).
Valgrind print karta hai: Address 0x... is 4 bytes before a block of size 20 alloc'd.
Verify: offset ; magnitude bytes before the block. ✓
Example 3 — Use-after-free (Cell 3, touching red)
Forecast: tumne pehle ek defined value likhi thi — kya yeh read ko safe banata hai?
free(p)ke baad. Parent ka rule:freeset karta hai aur block ko quarantine karta hai taaki reuse na ho. Yeh step kyun? Quarantine A-bit ko 0 pe rakhta hai itni der tak ki hum pakde jaayein; iske bina freshmallocbytes ko green recolour kar sakta tha aur bug ko chhupaata.*pread karo. Byte offset 0 ab A-bit rakhta hai → Invalid read of size 4. Yeh step kyun? Yeh ek A-bit error hai — "yeh tumhara touch karne ka haq nahi" — chahe value ek baar defined thi. Defined-ness irrelevant ho jaati hai jab touch karna hi forbidden ho.
Valgrind print karta hai: Invalid read of size 4 ... Address 0x... is 0 bytes inside a block of size 4 free'd.
Verify: block size bytes; offset pe read, jo ki freed block ke andar bytes hai. ✓
Example 4 — Double free (Cell 4)
Forecast: pehla free successful tha — toh doosra kyun fail hota hai?
- Pehla
free(p). Block quarantine freelist pe chala jaata hai; A-bits . Yeh step kyun? Memcheck ab yaad rakhta hai ki yeh block dead hai. - Doosra
free(p). Valgrind check karta hai: kya yeh pointer kisi currently allocated block ki taraf hai? Nahi — yeh freelist pe hai → Invalid free() / delete / delete[]. Yeh step kyun? Already-freed pointer ko free karna real allocator ki bookkeeping corrupt karta hai (dekho malloc and free) — classic Undefined Behaviour in C.
Valgrind print karta hai: Invalid free() ... Address 0x... is 0 bytes inside a block of size 4 free'd.
Verify (logical): do frees ke baad allocated blocks , impossible → doosra free invalid hai. ✓
Example 5 — Uninitialised value in control flow (Cell 5, V-bit)
Forecast: byte addressable hai — toh crime kahan hai?
- A-bit theek hai. Tumne
mallockiya, toh ke upar — read khud allowed hai. Yeh step kyun? A-bit report rule out ho jaata hai; addressability ke baare mein silent rahega. - V-bit undefined hai. Tumne
*pkabhi nahi likha, toh uske V-bits "garbage" kehte hain. Yeh step kyun? Comparison*p == 0ek aisa result produce karta hai jiska truth garbage pe depend karta hai. ifus result ko control flow ke liye use karta hai. Ab Memcheck fire karta hai: Conditional jump or move depends on uninitialised value(s). Yeh step kyun? Memcheck undefined values ko silently travel karne deta hai; woh sirf us waqt complain karta hai jab garbage actually program ke kaam ko badalta hai — ek branch ya syscall.
Verify (logical): branch outcome undefined bits ka function hai ⇒ program behaviour non-deterministic hai ⇒ exactly wahi condition jo Memcheck flag karta hai. ✓
Example 6 — Uninitialised value merely copied (Cell 6, the NO-error case)
Forecast: humne undefined memory read ki — surely yeh bug hai?
int y = *p;undefined V-bits koymein copy karta hai. Koi error nahi. Yeh step kyun? Garbage copy karna Memcheck ke model mein legal hai — V-bits saath saath chale jaate hain value ke andarymein.ykabhi branch, index, ya output mein use nahi hota. Toh garbage kabhi behaviour influence nahi karta → silent. Yeh step kyun? Yeh deliberate design hai: har copy ko flag karna tumhe false positives mein dubaa deta.
Verify (logical): Memcheck errors yahaan (koi branch/output undefined bits consume nahi karta). ✓
Example 7 — Zero and degenerate inputs (Cell 7)
Forecast: kya tum kuch bhi nahi allocate kar sakte? Kya tum kuch bhi nahi free kar sakte?
malloc(0)ya tohNULLreturn karta hai ya ek unique pointer jo tumfreeko pass kar sakte ho lekin dereference nahi kar sakte (0 addressable bytes). Yeh step kyun? Block ka size 0 hai, toh koi bhi*qhota "0 bytes after a block of size 0" — lekin hum dereference nahi karte, toh koi error nahi.free(q)valid hai chaheqwoh zero-size block ho yaNULL. Yeh step kyun? C standard ise well-defined banata hai; Memcheck bhi agree karta hai.free(NULL)explicitly standard ke anusaar no-op hai. Yeh step kyun? Yeh degenerate limit hai — free operation ka "zero input" — aur yeh silent hai, error nahi.
Verify (logical): errors ; dono frees standard-defined no-ops / valid hain. ✓
Example 8 — Definitely lost (Cell 8)
Forecast: kitne blocks alive hain? Kitne pointers unhe reach karne ke liye survive karte hain?
buf = malloc(200)ke baad. 100-byte block ka address kahin nahi — usse hold karne wala single variable overwrite ho gaya. Yeh step kyun? Exit-time mark-and-sweep live pointers se shuru hota hai; agar koi block ke andar point nahi karta, toh unreachable hai.- Classify karo. Unreachable aur koi interior pointer nahi → definitely lost: 100 bytes in 1 block. Yeh step kyun? Parent ke leak-rule tree se exactly match karta hai.
- 200 block free ho jaata hai → theek hai.
Verify: leaked bytes ; cleanly freed . ✓
Example 9 — Indirectly lost (Cell 9)
Forecast: head chala gaya — lekin C ka pointer P ke andar abhi bhi hai. Kya woh C ko bachata hai?
Pdrop karo.head = NULLset karne se blockPunreachable ho jaata hai → definitely lost, 8 bytes. Yeh step kyun? Koi live variablePki taraf point nahi karta.Cke baare mein kya?Cka ek hi pointerPke andar rehta hai — lekinPkhud lost hai. TohCsirf ek lost block ke through reachable hai → indirectly lost, 8 bytes. Yeh step kyun? Yahi "indirectly lost" category ka poora point hai: tumne root khoya, aur jo kuch uska tha sab saath gir gaya.
Verify: definitely lost ; indirectly lost ; total unreachable bytes. ✓
Example 10 — Still reachable, the benign case (Cell 10)
Forecast: humne kabhi free(pool) nahi kiya — kya yeh "definitely lost" bug hai?
- Exit pe,
poolabhi bhi address hold karta hai. Ek live (global) pointer block ke andar point karta hai. Yeh step kyun? Global se reachability count hoti hai — block findable hai. - Still reachable classify karo: 50 bytes. "Definitely lost" nahi. Yeh step kyun? OS process exit pe ise reclaim kar leta hai; yeh harmless leak category hai jise parent ne over-prioritise na karne ki warning di thi.
Verify: still-reachable bytes ; definitely-lost . ✓
Example 11 — Real-world word problem (Cell 11)
Forecast: kaunsi lines lost blocks produce karti hain?
- Valid lines (7). Pointer array mein store hota hai → exit pe reachable → definitely lost nahi. Yeh step kyun? Array root unhe findable rakhta hai.
- Blank lines (3). Buffer allocate hota hai, next iteration pe pointer overwrite hota hai, kabhi store nahi hota, kabhi free nahi hota → har ek unreachable.
Yeh step kyun?
continuestorage aurfreedono skip karta hai, block ko orphan kar deta hai. - Total. bytes definitely lost 3 blocks mein. Yeh step kyun? Har blank line ek orphaned 64-byte block contribute karti hai.
Verify: leaked bytes blocks mein; reachable bytes. ✓
Example 12 — Exam twist: kyun -O2 bug chhupaata hai (Cell 12)
Forecast: bug gayab nahi hua — toh report clean kyun hai?
- Dead-store elimination. Optimiser dekhta hai ki
a[5]pe likhi value kabhi read nahi hoti, toh store ko poora delete kar deta hai. Yeh step kyun? Undefined Behaviour in C ke under compiler assume kar sakta hai ki koi out-of-bounds write nahi hoti; koi observable effect nahi, toh instruction remove kar deta hai. - No memory access ⇒ Memcheck ke liye instrument karne ko kuch nahi. Valgrind sirf wahi accesses check kar sakta hai jo run tak survive karte hain. Yeh step kyun? Memcheck emitted machine code watch karta hai, tumhare source ko nahi.
- Fix: pehle hamesha
-O0 -gke saath debug karo (dekho Debugging with GDB), phir release build alag se re-audit karo. AddressSanitizer (ASan) se cross-check karo, jo compile time pe instrument karta hai aur kuch aise cases pakad sakta hai jinhein optimiser reshape kar deta hai.
Verify (logical): eliminated accesses ; Memcheck jo accesses dekh sakta hai ⇒ zero reports. ✓
Recall Cover check: har symptom ke liye cell batao
"Invalid read, 0 bytes inside a free'd block" ::: Cell 3, use-after-free (A-bit) "4 bytes before a block of size 20" ::: Cell 2, underflow (A-bit, negative side) "Conditional jump depends on uninitialised value" ::: Cell 5, V-bit in control flow Unused variable mein garbage copy karne ke baad silent report ::: Cell 6, V-bits travel, no use "indirectly lost" 8 bytes ::: Cell 9, child sirf lost parent ke through reachable -O0 pe fail hone wala clean report under -O2 ::: Cell 12, dead-store elimination