为啥数据和堆栈段是可执行的?

Posted

技术标签:

【中文标题】为啥数据和堆栈段是可执行的?【英文标题】:Why data and stack segments are executable?为什么数据和堆栈段是可执行的? 【发布时间】:2011-12-13 08:58:48 【问题描述】:

我刚刚注意到我的简单程序的数据和堆栈段是可执行的。 我在/proc/[pid]/maps中看到了,简单的代码就确认了。

例如:

; prog.asm
section .data
    code:   db 0xCC    ;int3

section .text
global _start
_start:
    jmp    code

    mov    rax, 60    ; sys_exit
    mov    rdi, 0
    syscall

然后

nasm -f elf64 prog.asm
ld -o prog prog.o
./prog

使 prog 执行 int3 指令。

用 C 编写并用 gcc 构建的程序的数据、堆栈和堆是不可执行的,那么为什么用汇编编写的程序的行为方式不同呢?

【问题讨论】:

【参考方案1】:

在现代 Linux 系统上,链接器会将堆栈/数据标记为不可执行IFF所有参与链接的对象都有一个特殊的“标记”部分.note.GNU-stack

如果你编译例如int foo() return 1; 进入汇编(与gcc -S foo.c),你会看到这个:

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

对于nasm,语法见section 8.9.2 of the manual;你想要这样的东西:

 section .note.GNU-stack noalloc noexec nowrite progbits

注意

这必须为进入可执行文件的每个 .o 文件完成。如果任何目标文件需要可执行堆栈或数据,则为整个段设置。

【讨论】:

它有效,谢谢。顺便说一句,我发现了这个:link,并且在第 7.9.2 节“对 SECTION 指令的 elf 扩展”中声明了节 .data 默认情况下具有 noexec 标志。但是,即使在带有 .data 部分的代码中显式编写 noexec 也是行不通的。奇怪。 您能否提供任何记录GNU_STACK 行为的来源?我已经看到您可以将-z noexecstack 传递给ld 以使其具有相同的行为...但这不是加载程序问题吗?当 .data 部分被明确标记为 noexec 时,加载器将其映射为可执行文件。 @Marco:Unexpected exec permission from mmap when assembly files included in the project 的更多信息,包括 Linux 内核源代码的链接。 谢谢。这提供了一些清晰度。我想我理解它的基本原理:旧精灵(在现代拱门中的 NX 位之前)可能取决于拥有可执行数据,因为没有办法禁用它。因此,为了检测“较新”的精灵,加载程序使用可执行堆栈功能。很遗憾nasm 没有自动添加的标志。 注意 linux 内核的行为 recently changed。对于较新的内核,使用nasm 编译的部分将不再默认在 x86_64 中可执行。 See commit

以上是关于为啥数据和堆栈段是可执行的?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 c++ 线程是可移动的但不可复制的?

Linux进程的五个段

为啥 PostgreSQL 会中止这个可序列化的计划

C程序的存储空间布局

iOS开发中的内存分配(堆和栈)

fork()函数