使用一个非常简单的示例,该示例使用2个指针。 指针1(s1_buffer)是malloc,然后是memset。 指针2(s2_buffer)是malloc,然后是memset,因此该位置有望接近指针1(即指针)。
当然,这不是使用指针和结构的首选方法,但是它是在模仿其他代码的。目的是了解幕后发生的情况以及gdb所显示的内容。
这是代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
struct
{
int a;
int b;
int c;
int d;
} n_s;
int e;
} struct1;
typedef struct
{
char aa[5];
int bb;
} struct2;
int main () {
struct1 s1;
struct2 s2;
int * s1_buffer = (int*) 0;
int * s2_buffer = (int*) 0;
printf("Size...\n");
printf(" s1 : %d\n", sizeof (struct1));
printf(" s2 : %d\n", sizeof (struct2));
int numOfElements = 1;
s1_buffer = ( int*) malloc((numOfElements * sizeof (struct1)));
memset((int *)s1_buffer, 0, (numOfElements * sizeof (struct1)));
s2_buffer = ( int*) malloc((numOfElements * sizeof (struct2)));
memset((int *)s2_buffer, 0, (numOfElements * sizeof (struct2)));
//The following shows that the memory locations are close to each other.
printf("\nMemory Location... \n");
printf(" s1_buffer : %p\n", s1_buffer);
printf(" s2_buffer : %p\n", s2_buffer);
numOfElements = 2;
//Here a memset is done WITHOUT doing a malloc and since the size is
//now twice as large Pointer 1's area should overwrite Pointer 2's memory.
memset((int *)s1_buffer, 0, (numOfElements * sizeof (struct1)));
printf("\nFreeing Memory... \n");
printf(" s1_buffer\n");
free(s1_buffer);
printf(" s2_buffer\n");
free(s2_buffer);
return(0);
}
运行程序将产生以下输出。
> ./memFreeTest
Size...
s1 : 20
s2 : 12
Memory Location...
s1_buffer : 0x16a6010
s2_buffer : 0x16a6030
Freeing Memory...
s1_buffer
*** Error in `./memFreeTest': free(): invalid next size (fast): 0x00000000016a6010 ***
======= Backtrace: =========
/usr/lib64/libc.so.6(+0x7c503)[0x2af569904503]
./memFreeTest[0x4007b1]
/usr/lib64/libc.so.6(__libc_start_main+0xf5)[0x2af5698a9b35]
./memFreeTest[0x400589]
======= Memory map: ========
基于s1_buffer和s2_buffer的地址,它们彼此之间在32个字节之内。完成s1_buffer的第二个内存集后,它应该踩到s2_buffer区域。
通过gdb分析核心文件,在“反汇编主程序”调用并查看寄存器后,可以看到以下内容。
0x0000000000400779 <+300>: shl $0x2,%rax
0x000000000040077d <+304>: mov %rax,%rdx
0x0000000000400780 <+307>: mov -0x8(%rbp),%rax
0x0000000000400784 <+311>: mov $0x0,%esi
0x0000000000400789 <+316>: mov %rax,%rdi
以下是第二个内存集。
0x000000000040078c <+319>: callq 0x400520 <memset@plt>
为什么将$ 0x4008c9的值移动到edi?
0x0000000000400791 <+324>: mov $0x4008c9,%edi
0x0000000000400796 <+329>: callq 0x400500 <puts@plt>
0x000000000040079b <+334>: mov $0x4008dd,%edi
0x00000000004007a0 <+339>: callq 0x400500 <puts@plt>
0x00000000004007a5 <+344>: mov -0x8(%rbp),%rax
0x00000000004007a9 <+348>: mov %rax,%rdi
0x00000000004007ac <+351>: callq 0x4004f0 <free@plt>
下一行是失败的地方。 为什么将$ 0x4008ea的值移动到edi?
我指的是edi,因为它似乎是发生故障的地方。
=> 0x00000000004007b1 <+356>: mov $0x4008ea,%edi
正在检查edi,它似乎是有效的:
(gdb) info reg edi
edi 0x19569 103785
dgb输出在这里继续:
0x00000000004007b6 <+361>: callq 0x400500 <puts@plt>
0x00000000004007bb <+366>: mov -0x10(%rbp),%rax
0x00000000004007bf <+370>: mov %rax,%rdi
0x00000000004007c2 <+373>: callq 0x4004f0 <free@plt>
0x00000000004007c7 <+378>: mov $0x0,%eax
0x00000000004007cc <+383>: leaveq
0x00000000004007cd <+384>: retq
End of assembler dump.
(gdb) info reg
rax 0x0 0
rbx 0x0 0
rcx 0xffffffffffffffff -1
rdx 0x6 6
rsi 0x19569 103785
rdi 0x19569 103785
rbp 0x7ffdc0b23440 0x7ffdc0b23440
rsp 0x7ffdc0b23400 0x7ffdc0b23400
r8 0x2af5699fe840 47233527441472
r9 0x2af569886000 47233525899264
r10 0x8 8
r11 0x246 582
r12 0x400560 4195680
r13 0x7ffdc0b23520 140727836357920
r14 0x0 0
r15 0x0 0
rip 0x4007b1 0x4007b1 <main+356>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) info reg edi
edi 0x19569 103785
核心文件第一次出现。如何知道此内存不属于s1_buffer?
如果知道它不“拥有”它以释放它,为什么它不知道它不拥有该内存集呢?似乎应该在此时出现错误或警告。
答案 0 :(得分:2)
首先,根据C标准,一旦通过memset
指针s1_buffer
花了太多时间,就调用了Undefined Behavior。但是无视...
s1_buffer
长20个字节。 s2_buffer
开始后0x16a6030 - 0x16a6010 = 0x20 = 32
个字节开始s1_buffer
个字节,这之间留有12个字节的间隔。您的“坏”内存集正在写入40个字节,它是s1_buffer
的全部,12个字节的间隙,然后是s2_buffer
的前8个字节。当您尝试free
时,是中止12字节的间隙(您的libc可能将其用于自己的记帐目的)导致中止。由于C通常不会尝试发现这种问题,因此不会更早发现该错误。幸运的是,free
能够发现它的内部数据不一致时能够找到它。