What possible errors will we encounter if we compile and execute this code? If you execute it, you will be surprised to see the output:
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001a5a010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7f9a8fa6f725]
/lib/x86_64-linux-gnu/libc.so.6(+0x7ff4a)[0x7f9a8fa77f4a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f9a8fa7babc]
./a.out[0x400771]
./a.out[0x400717]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f9a8fa18830]
./a.out[0x4005e9]
It can be more clear by looking at the disassembly code from gdb.
Dump of assembler code for function main(int, char**):
0x00000000004006b6 <+0>: push rbp
0x00000000004006b7 <+1>: mov rbp,rsp
0x00000000004006ba <+4>: push rbx
0x00000000004006bb <+5>: sub rsp,0x38
0x00000000004006bf <+9>: mov DWORD PTR [rbp-0x34],edi
0x00000000004006c2 <+12>: mov QWORD PTR [rbp-0x40],rsi
0x00000000004006c6 <+16>: mov rax,QWORD PTR fs:0x28
0x00000000004006cf <+25>: mov QWORD PTR [rbp-0x18],rax
0x00000000004006d3 <+29>: xor eax,eax
0x00000000004006d5 <+31>: lea rax,[rbp-0x30]
0x00000000004006d9 <+35>: mov rdi,rax
0x00000000004006dc <+38>: call 0x400734 <Word::Word()>
0x00000000004006e1 <+43>: lea rax,[rbp-0x30]
0x00000000004006e5 <+47>: mov esi,0x400884
0x00000000004006ea <+52>: mov rdi,rax
0x00000000004006ed <+55>: call 0x400774 <Word::setWord(char*)>
0x00000000004006f2 <+60>: mov rax,QWORD PTR [rbp-0x30]
0x00000000004006f6 <+64>: mov QWORD PTR [rbp-0x20],rax
0x00000000004006fa <+68>: mov ebx,0x0
0x00000000004006ff <+73>: lea rax,[rbp-0x20]
0x0000000000400703 <+77>: mov rdi,rax
0x0000000000400706 <+80>: call 0x40074a <Word::~Word()>
0x000000000040070b <+85>: lea rax,[rbp-0x30]
0x000000000040070f <+89>: mov rdi,rax
0x0000000000400712 <+92>: call 0x40074a <Word::~Word()>
0x0000000000400717 <+97>: mov eax,ebx
0x0000000000400719 <+99>: mov rdx,QWORD PTR [rbp-0x18]
0x000000000040071d <+103>: xor rdx,QWORD PTR fs:0x28
0x0000000000400726 <+112>: je 0x40072d <main(int, char**)+119>
0x0000000000400728 <+114>: call 0x400570 <__stack_chk_fail@plt>
0x000000000040072d <+119>: add rsp,0x38
0x0000000000400731 <+123>: pop rbx
0x0000000000400732 <+124>: pop rbp
0x0000000000400733 <+125>: ret
In both <+80> and <+92>, destructor Word::~Word is called. However, they are exact copies, so we are trying to free char* word twice. Note that Word hello is constructed in <+38>, whose stack address is $rbp-0x30, and its entire content is copied over to $rbp-0x20 in <+60> and <+64>, which is Word hi.
So, how should we fix this? Well, we simply need to explicitly declare copy constructor for class Word, in which we create a copy of the string pointed by word and have the copy object's word point to it. In fact, we also need to explicitly declare copy operator in a similar manner, because implicit copy operator will, again, make the exact copy, having two different objects point to the same string.
Below is the code that correct these.
If one has to explicitly declare any of class destructor, copy constructor, or copy assignment operator, then perhaps one needs to explicitly declare all of them.
Initially, we only declared explicit destructor, which caused a serious problem. Now that we have declared all three of them, we are in good shape.
Some other minor fixes to the above code is declaring the argument as const char* in setWord, and returning const char* for getWord method.
No comments:
Post a Comment