【问题标题】:c : gdb : Overwriting and freeing too much memoryc : gdb : 覆盖和释放太多内存
【发布时间】:2018-10-18 16:49:15
【问题描述】:

使用一个非常简单的示例,该示例使用 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 的第二个 memset 完成后,它应该踩到 s2_buffer 区域。

通过gdb分析核心文件,在“反汇编main”调用和查看寄存器后看到以下内容。

   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

以下是第二个 memset。

   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

核心文件发生在 1 号免费。它怎么知道这个内存不是由 s1_buffer “拥有”的?

如果它知道它不“拥有”它来释放它,为什么它不知道它不拥有被 memset 的区域?似乎当时应该出现错误或警告。

【问题讨论】:

  • 回复:“为什么要将价值 $0x4008c9 转移到 edi?” x/s *0x4008c9 显示什么?
  • (gdb) x/s *0x4008c9 0x6572460a:

标签: c memory gdb free


【解决方案1】:

首先,根据 C 标准,一旦您通过 s1_buffer 指针过多地 memsetted,您就会调用未定义行为。但忽略这一点......

s1_buffer 的长度为 20 个字节。 s2_buffers1_buffer 开始之后开始 0x16a6030 - 0x16a6010 = 0x20 = 32 字节,这之间会留下 12 个字节的间隙。您的“坏”memset 正在写入 40 个字节,即 s1_buffer 的全部,12 字节间隙,然后是 s2_buffer 的前 8 个字节。当您尝试free 时,导致中止的是 12 字节间隙(您的 libc 可能将其用于自己的会计目的)的破坏。错误没有被更早发现,因为 C 通常不会尝试找到此类问题。当free 发现其内部数据不一致时,它真的只能靠运气找到它。

【讨论】:

  • 我想知道 s2_buffer 的位置不直接位于 s1_buffer 末尾的一个原因是否是由于“一般信息”存储在那里。是否有任何参考资料讨论了存储哪些信息?
猜你喜欢
  • 2019-03-06
  • 1970-01-01
  • 2018-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-06
  • 2018-09-18
  • 1970-01-01
相关资源
最近更新 更多