【问题标题】:Address Space Layout Randomization( ALSR ) and mmap地址空间布局随机化 ( ALSR ) 和 mmap
【发布时间】:2012-02-28 15:56:31
【问题描述】:

我预计由于地址空间布局随机化 (ALSR),从另一个进程派生的进程在调用 mmap 时将返回不同的地址。但我发现,事实并非如此。为此,我制作了以下测试程序。 malloc 返回的所有地址对于父母和孩子来说都是完全相同的。 请注意,cl1、cl2、pl1、pl2 的 malloc 在内部使用 mmap,因为它们是大块。

所以,我的问题是,为什么 mmap 即使在 ALSR 存在的情况下也不会返回不同的地址。也许是因为这里的随机化种子对于原始和分叉的过程是相同的。还是有其他原因?

int main()
{
  pid = fork();

  if (pid == 0)                // child
  {
    void * c1 = malloc( 4096 );
    void * c2 = malloc( 4096 );

    void * cl1 = malloc( (long)512e3 ); // internally uses mmap
    void * cl2 = malloc( (long)512e3 ); // internally uses mmap

    printf( "c1 = %p, c2 = %p, cl1 = %p, cl2 = %p!\n", c1, c2, cl1, cl2 );
  }
  else
  {
    void * p1 = malloc( 4096 );
    void * p2 = malloc( 4096 );

    void * pl1 = malloc( (long)512e3 ); // internally uses mmap
    void * pl2 = malloc( (long)512e3 ); // internally uses mmap

    printf( "p1 = %p, p2 = %p, pl1 = %p, pl2 = %p!\n", p1, p2, pl1, pl2 );
  }

  return 0;
}

【问题讨论】:

  • 我不确定 ASLR 是否要求 mmap 返回不同的地址;它只是意味着它可能返回不同的。也许(只是猜测!)execve 比fork 触发的更多。当然,如果我连续两次启动你的程序,我会得到不同的地址。这可能会随着未来的内核或启用 SELinux 的内核而改变......
  • @Basile:当然,每次运行都会获得不同的地址,但是对于一次运行,两个进程(父进程和子进程)的地址是否不同?
  • 我只试了3次,但是父母和孩子的地址是一样的。

标签: c linux gcc x86-64 aslr


【解决方案1】:

ASLR主要随机化用户空间地址空间顶部下到栈的距离,栈保留空间底部到第一个mmap的距离(大概是动态链接器的映射) .任何进一步的随机化都会对虚拟内存空间产生严重的碎片化影响,因此会破坏需要制作大型 mmaps 的程序(例如,在 32 位机器上映射 1-2 GB)。

我看到一些 Linux 发行版发布了补丁内核,这些内核对mmap 返回的地址执行更多随机化。其中一些甚至为您提供与为堆栈扩展保留的空间重叠的映射,然后当堆栈增长时,它会破坏您的映射(导致巨大的安全漏洞,比任何非随机地址分配可能造成的漏洞都要大) .远离这些黑客行为。

【讨论】:

  • 所以我们可以假设分叉进程中的 mmap 不会总是返回与父进程相同的地址?对吗?
  • 今天(通常)是这样,但我不会假设未来的内核也是如此。没有具体说明,它只是当前的实现。内核人员可能想要改进它。
  • 我会避免以一种或另一种方式做出假设。例如,在非常大的映射之前添加少量随机填充(未映射的页面)可能是安全/合理的(因为按百分比计算的开销/碎片必然非常小)。
【解决方案2】:

您不能重新随机化子地址空间 - 所有指针都必须被修改,这在技术上是不可能的(运行时环境甚至不知道您的数据的哪一部分是指针)。

所以您看到的结果是预期的,来自 fork 的子节点在分叉时拥有其父地址空间的精确副本,包括其虚拟地址布局。

您需要调用exec* 来获得新的地址空间布局。

$ cat t.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    printf("%p\n", malloc((long)512e3));
    if ((argc > 1) && fork()) {
        execl("./a.out", "./a.out", NULL);
    }
    return 0;
}
$ gcc -Wall t.c
$ ./a.out 1
0x7f5bf6962010
0x7f3483044010
$ ./a.out 1
0x7f1ce7462010
0x7feb2adc2010

(并确保/proc/sys/kernel/randomize_va_space 也不为零。)

【讨论】:

  • OP 似乎不希望在分叉之前创建映射来更改地址,只是为了在分叉之后的新映射获得独立的随机地址。这在技术上并非不可能,但正如我的回答中所述,这是一个坏主意。
  • 那么,你的意思是每个未来的 mmap、sbrk、malloc 等都会为父进程和子进程返回相同的地址?
  • @MetallicPriest:如果您以完全相同的顺序、相同的数量进行操作,并且没有外部因素(资源限制、普通 OOM 等)起作用,那么对于普通 Linux 来说可能是的核心。可能存在一些补丁来随机化动态分配,但不是基本内核。有关更多信息,请参阅 R.. 的答案。
猜你喜欢
  • 2020-04-18
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
  • 2014-02-06
相关资源
最近更新 更多