Exercises — Valgrind — detecting memory errors
5.1.19 · D4· Coding › C Programming › Valgrind — detecting memory errors
Prerequisites jo khule rakh sakte ho: malloc and free, Pointers in C, Undefined Behaviour in C, Stack vs Heap.
Level 1 — Recognition
Kya tum ek Valgrind report padh ke bug class ka naam bata sakte ho?
L1.1 — Error class ka naam batao
Valgrind print karta hai:
Invalid read of size 4
at main (prog.c:7)
Address 0x... is 0 bytes inside a block of size 20 free'd
A / V / L mein se kaun sa hai ye, aur iska everyday naam kya hai?
Recall Solution
Block free'd ho chuka tha, isliye uske A-bits 0 hain — aap usse touch karne ki ijazat nahi rakhte. Yeh ek A-bit error hai. Everyday naam: use-after-free. Yeh V-bit error nahi hai kyunki issue yeh nahi ki "value garbage hai", balki yeh hai ki "yeh byte off-limits hai".
L1.2 — Read vs write, inside vs after
Do reports:
(a) Invalid write of size 1
Address 0x... is 0 bytes after a block of size 10 alloc'd
(b) Conditional jump or move depends on uninitialised value(s)
Dono ko A ya V classify karo, aur ek line mein batao ki code mein kya hua hoga.
Recall Solution
(a) "0 bytes after a block of size 10" → tumne ek 10-byte buffer ke paar ek byte write ki, jaise buf[10] = 'x';. End ke baad wali byte addressable nahi hai → A-bit (heap buffer overflow).
(b) "depends on uninitialised value(s)" → tumne ek aisi variable par branch kiya (if/while) jiski value kabhi set hi nahi hui → V-bit.
Level 2 — Application
Code dekhke exact Valgrind verdict aur numbers predict karo.
L2.1 — Off-by-one index
int *a = malloc(4 * sizeof(int)); // 4 ints
a[4] = 99;
free(a);Ek aisi system par jahan sizeof(int) == 4 hai, Valgrind kaun sa block size report karega, aur faulting address block ke kitne bytes baad hai?
Recall Solution
Block size bytes. Index 4 byte ko address karta hai, yaani 16-byte block ke bilkul 0 bytes baad. Report:
Invalid write of size 4
Address 0x... is 0 bytes after a block of size 16 alloc'd
L2.2 — Leaked bytes count karo
char *p = malloc(50);
char *q = malloc(80);
p = q; // 50-byte block ab orphaned hai
free(q); // 80-byte block ko free karta haiKitne bytes definitely lost hain?
Recall Solution
p = q ke baad, dono pointers 80-block ka address hold karte hain; koi bhi pointer 50-block ka address nahi rakhta, toh exit par scan use unreachable pata hai. free(q) 80-block clean kar deta hai. Leaked = 50 bytes in 1 block. (Dekho Stack vs Heap: heap block lost pointer se zyada jeeta hai — block leak hota hai, pointer nahi.)
L2.3 — strcpy mein kitne invalid bytes hain?
char *dst = malloc(3); // "ab" + '\0' ke liye jagah -> lekin hum zyada copy karte hain
strcpy(dst, "hello"); // "hello" ko 6 bytes chahiye
free(dst);Block ke baad kitne bytes write ho jaate hain?
Recall Solution
"hello" hai h e l l o \0 = 6 bytes. Buffer mein 3 hain. Bytes 0,1,2 legal hain; bytes 3,4,5 block ke baad hain → 3 invalid bytes write hue. Valgrind pehle out-of-bounds byte (offset 3) par Invalid write of size 1 report karta hai.
Level 3 — Analysis
Sirf label nahi, mechanism explain karo.
L3.1 — Quarantine kyun?
Ek student ek block free karta hai, phir ussi se read karta hai. Woh complain karta hai: "Maine free kiya aur phir same size ka re-malloc kiya aur Valgrind kabhi kabhi use-after-free report karna band kar deta hai." Freelist quarantine ke terms mein explain karo kya ho raha hai.
Recall Solution
free par, Memcheck block ke A-bits ko 0 set karta hai aur use ek quarantine freelist mein park karta hai taaki use turant wapas na diya jaaye. Isliye ek stray read pakdi jaati hai: A-bits abhi bhi 0 hain. Lekin quarantine ka ek finite size hai (default ~16 MB freed blocks). Agar tum baad mein kaafi memory free karo, toh purana block evict ho jaata hai aur eventually ek baad wale malloc se recycle ho jaata hai — uske A-bits wapas 1 flip ho jaate hain aur use-after-free invisible ho jaata hai. Reliable detection ke liye fix: ise --freelist-vol= se badha do aur --freelist-big-blocks se recycling delay karo.
L3.2 — Garbage copy karna error kyun nahi, lekin branch karna kyun hai?
int *p = malloc(sizeof(int)); // V-bits undefined
int x = *p; // line A: copy
int y = x + 1; // line B: arithmetic
if (y > 0) // line C: branch
puts("pos");
free(p);Valgrind actually kaun si line par complain karta hai, aur pehle kyun nahi?
Recall Solution
Valgrind line C par complain karta hai. Reason: V-bits value ke saath travel karte hain. Copying (line A) aur arithmetic (line B) sirf "undefined" mark ko aage propagate karte hain — abhi tak koi decision nahi liya gaya, toh koi observable behaviour garbage par depend nahi karta. Sirf conditional jump (line C) par undefined value yeh change karti hai ki program kaunsa path leta hai — yeh pehla point hai jahan garbage alag real behaviour produce karta hai, isliye Memcheck wahan fire karta hai. (Yeh Undefined Behaviour in C hai jo exactly branch par observable banta hai.)
L3.3 — Charon leak kinds classify karo
struct Node { struct Node *next; int v; };
struct Node *head = malloc(sizeof(struct Node)); // block H
head->next = malloc(sizeof(struct Node)); // block T
head = NULL; // H ka eklauta pointer kho gaya
// program yahan exit karta hai (koi free nahi)Block H aur block T ko definitely / indirectly / still reachable / possibly lost classify karo.
Recall Solution
- H —
head = NULLke baad koi bhi pointer H ki taraf point nahi karta → definitely lost. - T — T ka eklauta pointer H ke andar rehta hai (
head->next). T sirf ek lost block ke through reachable hai → indirectly lost. - Kuch bhi still reachable nahi hai (koi live pointer nahi bachta) aur kuch bhi possibly lost nahi hai (yahan interior-only pointer ka case nahi hai).
Valgrind report karta hai:
definitely lost: <sizeof Node> in 1 blockplusindirectly lost: <sizeof Node> in 1 block.
Level 4 — Synthesis
Tools, flags, aur reasoning combine karke ek diagnosis design karo.
L4.1 — Invocation design karo
Tumhare paas ek leak aur ek uninitialised-value bug hai, aur tumhe jaanna hai ki garbage value kahan janmi aur har leak kind dikhni chahiye. Ek single Valgrind command line likho aur har flag justify karo.
Recall Solution
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./prog
--leak-check=full→ full per-block leak listing (sirf summary nahi).--show-leak-kinds=all→ indirectly / possibly / still reachable bhi dikhata hai, sirf definitely lost nahi.--track-origins=yes→ har V-bit error ke liye, woh allocation site report karta hai jahan undefined value originate hui (speed cost lagta hai — isliye by default off hai). Aurgcc -g -O0se compile karo taaki lines/names dikhein aur optimiser bug elide na kar de.
L4.2 — Valgrind vs GDB vs ASan
Har tool ko us situation se match karo jis mein woh jeet jaata hai: (a) intermittent heap corruption, source changes nahi chahiye, 10–30× slowdown accept hai; (b) ek reproducible crash jahan tum line-by-line step karna chahte ho aur variables live inspect karna chahte ho; (c) fast CI runs jahan small (~2×) slowdown aur ek recompile dono acceptable hain.
Recall Solution
- (a) → Valgrind/Memcheck — tumhari logic ka recompile nahi chahiye (sirf
-g), shadow memory se heap errors pakad leta hai, slowness expected hai. Parent note dekho. - (b) → Debugging with GDB — interactive stepping, breakpoints, live variable inspection; yeh har byte track nahi karta, lekin tumhe investigate karne deta hai.
- (c) → AddressSanitizer (ASan) — compile-time instrumentation, roughly 2× slowdown, continuous integration ke liye ideal jahan tum rebuild afford kar sakte ho.
Level 5 — Mastery
Model ke edges aur failure modes ke baare mein sochho.
L5.1 — Ek leak jo Valgrind "possibly lost" bolta hai
int *base = malloc(10 * sizeof(int));
base += 3; // 'base' ab block ke BEECH mein point karta hai
// koi free nahi; exitYeh possibly lost kyun hai, definitely lost kyun nahi? Kaun sa ek edit ise definitely lost bana deta hai?
Recall Solution
Exit par scan ek aisa pointer (base) pata hai jo block ke andar point karta hai lekin uski start par nahi — ek interior pointer. Memcheck sure nahi ho sakta ki yeh genuinely "main abhi bhi yeh own karta hoon" pointer hai ya ek coincidental value, isliye woh hedge karta hai: possibly lost.
Ise definitely lost banane ke liye interior pointer bhi destroy karo: exit se pehle base = NULL; → ab koi pointer block ko reference nahi karta → definitely lost.
L5.2 — Ek real-memory tool overflow kyun miss karta
int *a = malloc(4 * sizeof(int)); // 16 bytes
a[4] = 7; // end ke baad ek intHeap allocator aksar malloc(16) ko ek bade real chunk tak round up karta hai (maan lo 32 bytes). Explain karo kyun a[4] write karna real hardware par crash nahi karta phir bhi Valgrind ise report karta hai, shadow memory reference karte hue.
Recall Solution
Hardware sirf rounded allocation dekhta hai (32 real bytes), toh byte 16 ek aisi page ke andar hai jo OS ne di thi → koi segfault nahi; write silently allocator bookkeeping corrupt kar deta hai — classic silent Undefined Behaviour in C. Memcheck hardware pages par rely nahi karta. Yeh har byte ke liye ek shadow A-bit rakhta hai aur A-bits = 1 sirf un 16 bytes ke liye mark karta hai jo tumne request ki, padding ke liye nahi. Byte 16 likhne par A-bit 0 milta hai → Invalid write report hota hai. Yahi reason hai ki tool exist karta hai: yeh tumhari intended boundaries track karta hai, jo CPU dekh hi nahi sakta.
L5.3 — Memcheck kahan andha hai
Ek aisi memory bug class ka naam batao jo Memcheck nahi pakad sakta, aur batao ki A/V/L model use kyun miss karta hai.
Recall Solution
Stack buffer overflows jo process ke apne stack ke andar rahte hain, aur ek hi allocation ke andar do adjacent live heap fields ke beech overflows (jaise ek struct ke field 1 ke baad field 2 mein write karna).
Kyun: poori allocation ke A-bits 1 hain (yeh ek legal block hai) aur overwritten bytes addressable aur defined hain — toh na A-model violate hota hai na V-model. Memcheck ki boundaries allocation boundaries hain, sub-object boundaries nahi. Sub-object/stack overflows ke liye, AddressSanitizer (ASan) (jo stack objects ke around redzones add karta hai) behtar tool hai.
Recall Self-test recap (hidden)
Upar har bug ek letter par reduce hota hai. Fix bolne se pehle letter bolo. Use-after-free ::: A Heap buffer overflow ::: A Branch on uninitialised value ::: V Block ka eklauta pointer bhool jaana ::: L Exit par interior-only pointer ::: L (possibly lost)