当项目中包含程序集文件时,mmap产生了意外的执行权限


94

我用这个把我的头撞到墙上。

在我的项目中,当我使用mmap映射(/proc/self/maps)分配内存时,尽管我只请求了可读内存,但它仍是一个可读且可执行的区域。

在研究了strace(看起来不错)和其他调试之后,我能够确定似乎唯一可以避免这个奇怪问题的东西:从项目中删除程序集文件,只保留纯C。(什么?!)

因此,这是我一个奇怪的示例,我正在使用Ubunbtu 19.04和默认gcc。

如果使用ASM文件(为空)编译目标可执行文件,则将mmap返回一个可读和可执行区域,如果不进行编译则其行为正确。请参阅/proc/self/maps示例中已嵌入的输出。

example.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

example.s:是一个空文件!

产出

附带ASM版本

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

没有ASM随附的版本

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

5
这是很奇怪的。
fuz

6
我只用GCC(没有CMake)来重现了这一点,所以我编辑了问题以使示例更小。
约瑟夫·西布尔-恢复莫妮卡·


您可能是对的,他回答的一部分必须围绕READ_IMPLIES_EXEC角色
Ben Hirschberg,

用汇编源文件-Wa,--noexecstack
jww

Answers:


90

Linux有一个称为的执行域READ_IMPLIES_EXEC,这将使所有分配给它的页面PROT_READ也被给出PROT_EXEC。该程序将向您显示是否已启用该功能:

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

如果将其与一个空.s文件一起编译,则会看到该文件已启用,但如果没有文件,则将其禁用。此值的初始值来自您Binary中的ELF元信息。做readelf -Wl example。在没有空.s文件的情况下进行编译时,您将看到以下行:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

但是,当您使用它进行编译时:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

请注意,RWE而不仅仅是RW。这样做的原因是,链接器假定您的程序集文件需要read-implies-exec,除非明确告诉您不需要,并且如果程序的任何部分都需要read-implies-exec,那么它将为您的整个程序启用。GCC编译的汇编文件通过以下代码告诉它不需要此文件(如果使用编译,您将看到此文件-S):

        .section        .note.GNU-stack,"",@progbits

将这一行放入example.s,它会告诉链接器它也不需要它,然后您的程序将按预期运行。


13
废话,这是一个奇怪的默认值。我猜该工具链在noexec之前就已经存在,并且将noexec设置为默认值可能会造成问题。现在,我很好奇像NASM / YASM这样的其他汇编器如何创建.o文件!但是无论如何,我想这是使用的机制gcc -zexecstack,以及为什么它不仅使堆栈而且使所有文件都可执行。
彼得·科德斯

23
@Peter-这就是为什么使用汇编器的Botan,Crypto ++和OpenSSL之类的项目add的原因-Wa,--noexecstack。我认为这是一个非常讨厌的尖锐边缘。静默丢失nx堆栈应该是一个安全漏洞。Binutil的人应该修复它。
jww

14
@jww这确实是一个安全问题,奇怪的是,没有人报告过它
本·海森堡

4
+1,但如果.note.GNU-stack,"",@progbits解释了该行的含义/逻辑,则此答案会更好-现在它是不透明的,等效于“此神奇的字符串导致此效果”,但该字符串显然看起来像具有某种语义。
mtraceur

33

除了使用特定于GNU的section指令变体来修改程序集文件之外,您还可以添加-Wa,--noexecstack到命令行中以构建程序集文件。例如,看看我如何在musl的configure

https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a

我相信至少有一些带有集成汇编程序的clang版本可能要求将其作为--noexecstack(不带-Wa)进行传递,因此您的configure脚本可能应该同时检查两者并查看哪个被接受。

您也可以-Wl,-z,noexecstack在链接时间(中使用LDFLAGS)获得相同的结果。这样做的缺点是,如果您的项目生成.a供其他软件使用的静态()库文件,则无济于事,因为当其他程序使用链接时选项时,您将无法对其进行控制。


1
嗯...在阅读本文之前,我不知道您是Rich Felker。为什么您的显示名称不是dalias?
SS安妮
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.