linux - 使用 r8 寄存器作为循环计数器会导致无限循环 - 为什么?

标签 linux assembly x86-64 nasm system-calls

以下代码通过使用 rsi 寄存器作为循环计数器打印 hello world 10 次。

section .data
    hello:     db 'Hello world!',10   
    helloLen:  equ $-hello             

section .text
    global _start

_start:
    mov rsi, 0                 ;<--- use r8 here

do_loop:
    inc rsi                    ;<--- use r8 here

    ;print hello world
    mov eax,4             
    mov ebx,1            
    mov ecx,hello       
    mov edx,helloLen     

    int 80h              

    cmp rsi, 10                ;<--- use r8 here
    jnz do_loop

    ;system exit
    mov eax,1            ; The system call for exit (sys_exit)
    mov ebx,0            ; Exit with return code of 0 (no error)
    int 80h;

如果我尝试使用 r8 寄存器而不是 rsi 作为循环计数器,它会导致无限循环。这里的 r8 寄存器只是一个例子。它也发生在寄存器 r9、r10 上。

谁能解释一下,因为我认为这些都是通用寄存器,你应该被允许使用它们?

最佳答案

TL;DR:int 0x80 隐式清零 R8,<在返回用户态代码之前,64 位 Linux 系统上的 em>R9、R10R11。此行为发生在 2.6.32-rc1 之后的内核上。对于首选的 64 位 SYSCALL 调用约定,情况并非如此。


在 2.6.32-rc1 版本之后,您遇到了 Linux 内核的一个特性。对于 <= 2.6.32-rc1 的 Linux 内核版本,您可能会得到预期的行为。由于信息泄漏错误(和漏洞利用),寄存器 R8R9R10R11 现在是当内核从 int 0x80 返回时清零。

您可能认为这些寄存器在兼容模式(32 位代码)下应该无关紧要,因为这些较新的寄存器不可用。这是一个错误的假设,因为 32 位应用程序可以切换到 64 位长模式并访问这些寄存器。 Linux Kernel Mailing List post确定这个问题的人是这样说的:

x86: Don't leak 64-bit kernel register values to 32-bit processes

While 32-bit processes can't directly access R8...R15, they can gain access to these registers by temporarily switching themselves into 64-bit mode.

演示早期内核上寄存器泄漏的代码由 Jon Oberheide 提供。 .它创建一个 32 位应用程序以在启用 IA32 兼容性的 x86-64 系统上运行。程序切换到 64 位长模式,然后将寄存器 R8-R11 存储到兼容模式(32 位模式)下可用的通用寄存器中。 John 在 article 中讨论了细节.他在这段摘录中很好地总结了漏洞和内核修复:

The Vulnerability

The underlying issue that causes this vulnerability is a lack of zeroing out several of the x86-64 registers upon returning from a syscall. A 32-bit application may be able to switch to 64-bit mode and access the r8, r9, r10, and r11 registers to leak their previous values. This issue was discovered by Jan Beulich and patched on October 1st. The fix is obviously to zero out these registers to avoid leaking any information to userspace.


如果您要在像 GDB 这样的调试器中单步执行代码,您应该会发现 R8 实际上在 int 0x80 之后设置为零>。因为它是您的循环计数器,所以您的程序最终会无限循环打印 Hello world!

关于linux - 使用 r8 寄存器作为循环计数器会导致无限循环 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38505049/

相关文章:

assembly - ljmp(64 位模式)被#GP(0) 拒绝。为什么?

linux - Crontab 作业输出到文件

c - 如何在 gcc asm 中添加一个计数器?

c - 将 asm 嵌入到 C 编程中

c - 堆栈缓冲区溢出文章中的奇怪地址

assembly - ASM x86_64 AVX : xmm and ymm registers differences

linux - 避免碰撞,如果复制文件

带参数调用Linux服务

检查文件是否存在,包括在 PATH 中

c++ - 不同的Ubuntu版本二进制执行差异