在linux上访问堆栈下面的内存

时间:2014-03-15 14:26:19

标签: c linux callstack

该程序访问堆栈下方的内存。

我会假设在超出堆栈范围时得到段错误或只是nul但我看到实际数据。 (这假设100kb以下的堆栈指针超出了堆栈范围)

或者系统实际上让我看到堆栈下方的内存? Weren应该对此进行内核级保护,还是仅适用于已分配的内存?

编辑:使用1024*127下面的char指针,它会随机段错或运行,所以堆栈似乎不是固定的8MB,而且似乎有点也是随机的。

#include <stdio.h>

int main(){
  char * x;
  int a;
  for( x = (char *)&x-1024*127; x<(char *)(&x+1); x++){
    a = *x & 0xFF;
    printf("%p = 0x%02x\n",x,a);
  }
}

编辑:另一个奇怪的事情。第一个程序只在1024*127进行段错误,但是如果我向下打印远离堆栈我没有得到段错误并且所有内存似乎都是空的(全部0x00):

#include <stdio.h>

int main(){
  char * x;
  int a;
  for( x = (char *)(&x); x>(char *)&x-1024*1024; x--){
    a = *x & 0xFF;
    printf("%p = 0x%02x\n",x,a);
  }
}

4 个答案:

答案 0 :(得分:3)

当您访问内存时,您正在访问进程地址空间。

进程地址空间分为页面(x86上通常为4 KB)。这些是虚拟页面:它们的内容保存在其他地方。内核管理从虚拟页面到其内容的映射。内容可以通过以下方式提供:

  • 物理页面,用于当前由物理RAM支持的页面。直接访问(通过内存管理硬件)。

  • 已换出磁盘的页面。访问它将导致内核处理的页面错误。它需要使用磁盘内容填充物理页面,因此它找到一个空闲的物理页面(可能将该页面的内容交换到磁盘),从磁盘读取内容,并更新映射以声明该状态&#34;虚拟页面X在物理页面Y&#34;。

  • 文件(即内存映射文件)。

  • 硬件设备(即硬件设备寄存器)。这些通常不会引起我们用户空间的关注。

假设我们有一个4 GB的虚拟地址空间,分成4 KB页面,给我们1048576个虚拟页面。其中一些将由内核映射;别人不会。当进程开始时(即调用main()时),虚拟地址空间将包含以下内容:

  • 程序代码。这些页面通常是可读和可执行的。

  • 程序数据(即初始化变量)。这通常有一些只读页面和一些读写页面。

  • 程序所依赖的库中的代码和数据。

  • 堆栈的某些页面。

这些内容都映射为4 GB地址空间中的页面。您可以通过查看/ proc /(pid)/ maps查看映射的内容,正如其中一条评论所指出的那样。这些页面的精确内容和位置取决于(a)所讨论的程序,以及(b)地址空间布局随机化(ASLR),这使得事物的位置更难以猜测,从而使某些安全开发技术更加困难。

您可以通过定义指针并解除引用来访问内存中的任何特定位置:

*(unsigned char *)0x12345678

如果这恰好指向一个映射页面,并且该页面是可读的,那么访问将成功并产生在该地址映射的任何内容。如果没有,那么您将从内核收到SIGSEGV。您可以处理(在某些情况下这很有用,例如JIT编译器),但通常您不会,并且该过程将被终止。如上所述,由于ASLR,如果您在程序中执行此操作并多次运行程序,那么您将获得某些地址的非确定性结果。

答案 1 :(得分:1)

堆栈指针下面通常有相当多的可访问内存,因为在正常增长堆栈时会使用该内存。堆栈本身仅由堆栈指针的值控制 - 它是一个软件实体,而不是硬件实体。

但是,系统代码可能会假设典型的堆栈使用情况。例如,在某些系统上,堆栈用于存储上下文切换的状态,同时运行信号处理程序等。这还取决于硬件在离开用户模式时是否自动切换堆栈指针。如果系统确实使用了你的堆栈,那么它将破坏你存储在那里的数据,这可能真的发生在程序的每一点上。

因此,在堆栈指针下操作堆栈内存是不安全的。假设已成功写入的值在下一行代码中仍然相同,这甚至都不安全。只保证堆栈指针上方的部分不被运行时/内核触及。


不言而喻,此代码会调用未定义的行为。指针算法无效,因为地址&x-1024*127未分配给变量x,因此取消引用此指针会调用未定义的行为。

答案 2 :(得分:1)

  

这是C中的未定义行为。您正在访问随机存储器地址,该地址取决于平台,可能在堆栈上,也可能不在堆栈中。它可能存在也可能不存在于该用户可以访问的内存中;如果不是,你会得到一个段落或类似的。无论如何都没有任何承诺。

实际上,它并非未定义的行为,它的定义非常明确。通过指针访问存储器位置始终是定义的,因为C尽可能接近硬件。 然而,我同意当你不确切地知道你正在做什么时,通过指针访问硬件是一件危险的事情。

  不要这样做。 (如果你是有正当理由这样做的五六个人中的一个,你已经知道了,并且不需要我们的建议。)

这将是一个贫穷的世界,只有五六个人合法编程操作系统,嵌入式设备和驱动程序(虽然有时看起来好像后者就是这种情况......)。

答案 3 :(得分:0)

这是C中的未定义行为。您正在访问随机存储器地址,该地址取决于平台,可能在堆栈上,也可能不在堆栈中。它可能存在也可能不存在于该用户可以访问的内存中;如果不是,你会得到一个段落或类似的。无论如何都没有任何承诺。

不要这样做。 (如果你是有正当理由这样做的五六个人中的一个,你已经知道了,并且不需要我们的建议。)