অপ্রয়োজনীয় বিশ্লেষণ সহ ন্যূনতম প্রজনন উদাহরণ
main.c
void myfunc(char *const src, int len) {
int i;
for (i = 0; i < len; ++i) {
src[i] = 42;
}
}
int main(void) {
char arr[] = {'a', 'b', 'c', 'd'};
int len = sizeof(arr);
myfunc(arr, len + 1);
return 0;
}
গিটহাব উজানের দিকে ।
সংকলন এবং চালান:
gcc -fstack-protector -g -O0 -std=c99 main.c
ulimit -c unlimited && rm -f core
./a.out
কাঙ্ক্ষিত হিসাবে ব্যর্থ:
*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)
উবুন্টু 16.04, জিসিসি 6.4.0 এ পরীক্ষিত।
disassembly
এখন আমরা বিচ্ছিন্নতা তাকান:
objdump -D a.out
যেটা বহন করে:
int main (void){
400579: 55 push %rbp
40057a: 48 89 e5 mov %rsp,%rbp
# Allocate 0x10 of stack space.
40057d: 48 83 ec 10 sub $0x10,%rsp
# Put the 8 byte canary from %fs:0x28 to -0x8(%rbp),
# which is right at the bottom of the stack.
400581: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400588: 00 00
40058a: 48 89 45 f8 mov %rax,-0x8(%rbp)
40058e: 31 c0 xor %eax,%eax
char arr[] = {'a', 'b', 'c', 'd'};
400590: c6 45 f4 61 movb $0x61,-0xc(%rbp)
400594: c6 45 f5 62 movb $0x62,-0xb(%rbp)
400598: c6 45 f6 63 movb $0x63,-0xa(%rbp)
40059c: c6 45 f7 64 movb $0x64,-0x9(%rbp)
int len = sizeof(arr);
4005a0: c7 45 f0 04 00 00 00 movl $0x4,-0x10(%rbp)
myfunc(arr, len + 1);
4005a7: 8b 45 f0 mov -0x10(%rbp),%eax
4005aa: 8d 50 01 lea 0x1(%rax),%edx
4005ad: 48 8d 45 f4 lea -0xc(%rbp),%rax
4005b1: 89 d6 mov %edx,%esi
4005b3: 48 89 c7 mov %rax,%rdi
4005b6: e8 8b ff ff ff callq 400546 <myfunc>
return 0;
4005bb: b8 00 00 00 00 mov $0x0,%eax
}
# Check that the canary at -0x8(%rbp) hasn't changed after calling myfunc.
# If it has, jump to the failure point __stack_chk_fail.
4005c0: 48 8b 4d f8 mov -0x8(%rbp),%rcx
4005c4: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx
4005cb: 00 00
4005cd: 74 05 je 4005d4 <main+0x5b>
4005cf: e8 4c fe ff ff callq 400420 <__stack_chk_fail@plt>
# Otherwise, exit normally.
4005d4: c9 leaveq
4005d5: c3 retq
4005d6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
4005dd: 00 00 00
objdump
এর কৃত্রিম বুদ্ধিমত্তা মডিউল দ্বারা স্বয়ংক্রিয়ভাবে যুক্ত মন্তব্যগুলি লক্ষ্য করুন ।
আপনি যদি জিডিবির মাধ্যমে একাধিকবার এই প্রোগ্রামটি চালান, আপনি দেখতে পাবেন:
- ক্যানারি প্রতিবার একটি ভিন্ন এলোমেলো মান পায়
- শেষ লুপটি
myfunc
হ'ল ক্যানারিটির ঠিকানা পরিবর্তন করে
ক্যানারিটি সেট করে এটি এলোমেলো করে রেখেছে, এতে বর্ণিত %fs:0x28
একটি এলোমেলো মান রয়েছে:
ডিবাগ প্রচেষ্টা
এখন থেকে, আমরা কোডটি পরিবর্তন করেছি:
myfunc(arr, len + 1);
পরিবর্তে হতে:
myfunc(arr, len);
myfunc(arr, len + 1); /* line 12 */
myfunc(arr, len);
আরও আকর্ষণীয় হতে।
এরপরে আমরা চেষ্টা করব যে আমরা + 1
পুরো সোর্স কোডটি পড়ার এবং বোঝার চেয়ে কোনও স্বয়ংক্রিয় পদ্ধতিতে অপরাধীর কলটিকে চিহ্নিত করতে পারি কিনা ।
gcc -fsanitize=address
গুগলের ঠিকানা স্যানিটাইজার (আসান) সক্ষম করতে
আপনি যদি এই পতাকাটির সাথে পুনরায় সংকলন করে এবং প্রোগ্রামটি চালনা করেন তবে এটি আউটপুট দেয়:
#0 0x4008bf in myfunc /home/ciro/test/main.c:4
#1 0x40099b in main /home/ciro/test/main.c:12
#2 0x7fcd2e13d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400798 in _start (/home/ciro/test/a.out+0x40079
এর পরে আরও কিছু রঙিন আউটপুট আসে।
এটি স্পষ্টভাবে সমস্যাযুক্ত লাইনটি 12 চিহ্নিত করে।
এর উত্স কোডটি এখানে রয়েছে: https://github.com/google/sanitizers তবে উদাহরণ থেকে আমরা দেখেছি এটি ইতিমধ্যে জিসিসিতে উত্থিত হয়েছে।
আসান মেমরি ফাঁসের মতো অন্যান্য মেমরি সমস্যাও সনাক্ত করতে পারে: একটি সি ++ কোড / প্রকল্পে মেমরি ফাঁস কীভাবে পাওয়া যায়?
ভালগ্রাইন্ড এসজিচেক
অন্যদের দ্বারা যেমন উল্লেখ করা হয়েছে , ভালগ্র্যান্ড এই ধরণের সমস্যা সমাধানে ভাল নয়।
এটিতে এসজিচেক নামে একটি পরীক্ষামূলক সরঞ্জাম রয়েছে :
এসজিচেক স্ট্যাক এবং গ্লোবাল অ্যারেগুলির ওভাররানগুলি অনুসন্ধান করার জন্য একটি সরঞ্জাম। এটি স্ট্যাক এবং বিশ্বব্যাপী অ্যারে অ্যাক্সেসের সম্ভাব্য ফর্মগুলি সম্পর্কে একটি পর্যবেক্ষণ থেকে প্রাপ্ত হিউরিস্টিক পদ্ধতির ব্যবহার করে কাজ করে।
সুতরাং ত্রুটিটি না পেয়ে আমি খুব অবাক হইনি:
valgrind --tool=exp-sgcheck ./a.out
ত্রুটি বার্তাটি আপাতদৃষ্টিতে দেখতে হবে: ভালগ্র্যান্ড রেকর্ডিং ত্রুটি
, GDB
একটি গুরুত্বপূর্ণ পর্যবেক্ষণটি হ'ল আপনি যদি প্রোগ্রামটি জিডিবির মাধ্যমে পরিচালনা করেন বা core
ফাইলটি পরীক্ষা করে দেখেন:
gdb -nh -q a.out core
তারপরে, আমরা সমাবেশে দেখেছি, জিডিবি আপনাকে ক্যানারি চেক করে ফাংশনটির শেষের দিকে নির্দেশ করবে:
(gdb) bt
#0 0x00007f0f66e20428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007f0f66e2202a in __GI_abort () at abort.c:89
#2 0x00007f0f66e627ea in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7f0f66f7a49f "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f0f66f0415c in __GI___fortify_fail (msg=<optimized out>, msg@entry=0x7f0f66f7a481 "stack smashing detected") at fortify_fail.c:37
#4 0x00007f0f66f04100 in __stack_chk_fail () at stack_chk_fail.c:28
#5 0x00000000004005f6 in main () at main.c:15
(gdb) f 5
#5 0x00000000004005f6 in main () at main.c:15
15 }
(gdb)
এবং তাই এই ফাংশনটি করেছে যে কলগুলির মধ্যে একটিতে সম্ভবত সমস্যা।
এরপরে আমরা ক্যানারি স্থাপনের ঠিক পরে প্রথম একক পদক্ষেপের মাধ্যমে সঠিক ব্যর্থ কলটি চিহ্নিত করার চেষ্টা করব:
400581: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400588: 00 00
40058a: 48 89 45 f8 mov %rax,-0x8(%rbp)
এবং ঠিকানাটি দেখছেন:
(gdb) p $rbp - 0x8
$1 = (void *) 0x7fffffffcf18
(gdb) watch 0x7fffffffcf18
Hardware watchpoint 2: *0x7fffffffcf18
(gdb) c
Continuing.
Hardware watchpoint 2: *0x7fffffffcf18
Old value = 1800814336
New value = 1800814378
myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
3 for (i = 0; i < len; ++i) {
(gdb) p len
$2 = 5
(gdb) p i
$3 = 4
(gdb) bt
#0 myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
#1 0x00000000004005cc in main () at main.c:12
এখন, এটি আমাদের আপত্তিজনক সঠিক নির্দেশে ফেলেছে: len = 5
এবং i = 4
, এবং এই বিশেষ ক্ষেত্রে, আমাদের দোষী রেখার দিকে 12 নির্দেশ করেছে।
তবে, ব্যাকট্রিসটি দূষিত এবং এতে কিছু আবর্জনা রয়েছে। একটি সঠিক ব্যাকট্রেস দেখতে পাবেন:
#0 myfunc (src=0x7fffffffcf14 "abcd", len=4) at main.c:3
#1 0x00000000004005b8 in main () at main.c:11
সুতরাং এটি স্ট্যাকটিকে দূষিত করতে পারে এবং ট্রেসটি দেখতে বাধা দিতে পারে।
এছাড়াও, এই পদ্ধতিটি ক্যানারি চেকিং ফাংশনটির শেষ কলটি কী তা জেনে রাখা দরকার অন্যথায় আপনার কাছে মিথ্যা ধনাত্মকতা থাকবে, যা আপনি সর্বদা সম্ভব হবে না, যদি না আপনি বিপরীত ডিবাগিং ব্যবহার করেন ।