gpt4 book ai didi

linux - 为什么这段代码会随着地址随机化而崩溃?

转载 作者:IT王子 更新时间:2023-10-29 00:26:03 26 4
gpt4 key购买 nike

我正在学习 amd64 汇编程序,并尝试实现一个简单的 Unix 过滤器。由于未知原因,即使简化到最低限度版本(下面的代码),它也会随机崩溃。

我试图在 GNU 调试器 (gdb) 中调试这个程序。在 gdb 的默认配置中,程序运行良好,但如果我启用地址随机化 (set disable-randomization off),程序开始崩溃 (SIGSEGV)。 list 中标记了有问题的指令:

format ELF64 executable

sys_read = 0
sys_write = 1
sys_exit = 60

entry $
foo:
label .inbuf at rbp - 65536
label .outbuf at .inbuf - 65536
label .endvars at .outbuf
mov rbp, rsp

mov rax, sys_read
mov rdi, 0
lea rsi, [.inbuf]
mov rdx, 65536
syscall

xor ebx, ebx
cmp eax, ebx
jl .read_error
jz .exit

mov r8, rax ; r8 - count of valid bytes in input buffer
xor r9, r9 ; r9 - index of byte in input buffer, that is being processed.
xor r10, r10 ; r10 - index of next free position in output buffer.

.next_byte:
cmp r9, r8
jg .exit
mov al, [.inbuf + r9]
mov [.outbuf + r10], al ;; SIGSEGV here in GDB
inc r10
inc r9
jmp .next_byte

.read_error:
mov rax, sys_exit
mov rdi, 1
syscall
.exit:
mov rax, sys_write
mov rdi, 1
lea rsi, [.outbuf]
mov rdx, r10
syscall

mov rax, sys_exit
xor rdi, rdi
syscall


这个程序的目的是从stdin读取最多64kB,存入栈上的缓冲区,将读取的数据逐字节复制到输出缓冲区,并将输出缓冲区的内容写入标准输出流.本质上,它应该作为 cat 的有限版本。

在我的电脑上,它要么按预期工作,要么因 SIGSEGV 而崩溃,大约有 1 次成功运行到 4 次崩溃的比率。

最佳答案

sub rsp, <size>如果您在 RSP 以下使用超过 128 个字节,请在接触它之前保留堆栈空间。


当它崩溃时,查看你的进程内存映射。您可能正在使用远低于 RSP 的内存,以至于内核不会增加堆栈映射,因此它只是对未映射页面的普通访问 = 无效页面错误 => 内核提供 SIGSEGV。

(ABI 只定义了一个 128 字节的红色区域,但实际上唯一可以破坏内存的是信号处理程序(你没有安装)或运行 print some_func() 的 GDB 使用你的程序堆栈来在你的程序中调用一个函数。)

通常 Linux 非常愿意在不触及中间页面的情况下增加堆栈映射,但显然会检查 RSP 的值。通常你移动 RSP 而不是仅仅使用远低于堆栈指针的内存(因为不能保证它是安全的)。参见 How is Stack memory allocated when using 'push' or 'sub' x86 instructions?

另一个副本:Which exception can be generated when subtracting ESP or RSP register? (stack growing)在哪里使用sub rsp, 5555555在接触新的堆栈之前,内存就足够了。

Stack ASLR 可能会在相对于页面边界的不同位置启动 RSP,因此有时您可能勉强能摆脱它。 Linux 最初映射 132kiB 的堆栈空间,其中包括环境空间和进入 _start 时堆栈上的参数。 .您的 128kiB 非常接近该值,因此它有时会随机工作是完全合理的。


顺便说一句,在用户空间中实际复制内存的理由为零,尤其是一次不是 1 个字节。只需将相同的地址传递给 write .

或者至少在可能的情况下就地过滤,这样您的缓存占用空间会更小。

此外,加载一个字节的正常方式是movzx eax, byte [mem] .仅使用 mov al, [mem]如果您特别想与 RAX 的旧值合并。在某些 CPU 上,moval对旧值有错误的依赖性,您可以通过写入完整的寄存器来打破它。


顺便说一句,如果您的程序总是 使用这个空间,您不妨在 BSS 中静态分配它。如果您选择组装一个位置相关(非 PIE)可执行文件,这将使更有效的索引寻址成为可能。

关于linux - 为什么这段代码会随着地址随机化而崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56718127/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com