assembly 'sprintf' libc调用在Ubuntu上的程序集中给出SegFault(有时)[重复]

xurqigkl  于 2022-12-29  发布在  其他
关注(0)|答案(1)|浏览(101)
    • 此问题在此处已有答案**:

glibc scanf Segmentation faults when called from a function that doesn't align RSP(1个答案)
昨天关门了。
我试图通过调用sprintf将汇编(NASM)中的整数字符串化。
我有下面的汇编代码可以做到这一点:

sprintf_Int_str db "%lld",
                   dq 0
...

Int.str:
    push rbp
    mov rbp, rsp  ; creates stack frame

    ; allocate string
    mov rdi, 64   ; much larger than needed
    call malloc

    push rax ; save char*

    ; write string to allocated memory using `sprintf(buf, "%lld", n)`
    mov rdi, rax
    mov rsi, sprintf_Int_str
    mov rdx, qword [rbp + 16]     ; the location of the number on the stack 
                                  ; (trust me - it's pushed before Int.str is called,
                                  ; meaning it's 2 (64-bit) places before the start 
                                  ; of the stack frame)
    mov rcx, rsi

    mov r8, 0
    mov r9, 0    ; clearing other registers for sake of it
    mov rax, 0   ; (doesn't make a difference if I do this or not)

    call sprintf

    pop rax      ; pop return value which is saved after call to malloc

    mov rsp, rbp
    pop rbp
    ret

这段代码在我的机器(Ubuntu 22.10)上运行良好,但我注意到我的自动测试在GitHub Actions(Ubuntu VM)上失败了。我在Docker中设置了一些测试在我的机器上运行,它们在Alpine、Debian和Arch容器中运行良好,但在Ubuntu中再次失败。我使用GDB分析了失败代码的核心转储,它给了我以下信息:

这不是很有帮助。我已经尝试了各种方法传递参数到sprintf,因为我看到了冲突的信息在线,但GCC给出了以下输出从以下C++代码:
x一个一个一个一个x一个一个二个x
它运行良好,并且似乎与我的代码相同,就调用sprintf时哪些寄存器包含哪些内容而言。
尤其令我不解的是,它很少失败:我唯一的想法是,我正在做一些未定义行为的事情,这些行为碰巧总是在我的机器上工作,因为我的机器有更大的RAM或更有弹性的 * 东西 *,但是,在更轻量级的VM上,这是不允许的(尽管添加--max-ram="1g"没有帮助)。
我的集合命令是

nasm -f elf64 <file.asm> -o out.o

我的连接命令是

gcc -Wall -no-pie out.o -e main -o a.out

我的Ubuntu停靠文件是:

FROM ubuntu:latest

WORKDIR /app

COPY . .

SHELL ["/bin/bash", "-c"]

RUN apt-get update && apt-get install -y nasm curl build-essential bc

RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

CMD ["/bin/bash", "bin/test"]

它运行一个执行程序集的自定义bash脚本(./bin/test)。
在我的机器上运行ldd --version(运行良好),得到:
ldd (Ubuntu GLIBC 2.36-0ubuntu4) 2.36
在本地Docker容器上运行它(它所在的地方),得到:
ldd (Ubuntu GLIBC 2.35-0ubuntu3.1) 2.35
但是我不能想象一个小的版本更新会碰巧解决这个问题?无论如何,运行apt install libc-bin表明软件包在这两个版本上都是最新的。
我的项目的repo在这里是https://github.com/revers3ntropy/oxynium,尽管它有很多围绕该程序集的 Package 器。
如果你能帮我的话,非常感谢!

gc0ot86w

gc0ot86w1#

我唯一的想法是我在做一些行为未定义的事情
是的。特别是,x86_64 ABI * 要求 * 在16字节边界上保持堆栈对齐,而您 * 没有 * 这样做。
在使用-msse2构建GLIBC的计算机上,调用堆栈未对齐的libc例程 * 可能 * 崩溃。
检查崩溃的指令(使用x/i $pc GDB命令)--可能是MOVDQA或类似指令,源地址或目的地址未对齐。

相关问题