gcc 地址消毒剂说有一个segfault,但valgrind和gdb说没有?

cdmah0mi  于 2023-05-07  发布在  其他
关注(0)|答案(1)|浏览(190)

我目前正在上一门课程,要求我写汇编代码,准确地说是x86-64 AT&T语法汇编代码。下面是一个c文件,其中包含函数“气泡”的函数定义,我必须编写其汇编代码。

#include<stdio.h>
#include<stdlib.h>

void bubble(int* arr, int len);

int main(){
    int n;
    scanf("%d", &n);
    int* arr = malloc(sizeof(int)*n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &arr[i]);
    }
    bubble(arr, n);
    for (int i = 0; i < n; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
    free(arr);
}

下面是函数bubble的汇编代码

.global bubble
.text

bubble:
    movq $-1, %rcx
.L1:
    incq %rcx
    movl 4(%rdi,%rcx,4), %eax
    cmpl (%rdi,%rcx,4), %eax
    jge .T1
    movl (%rdi,%rcx,4), %eax
    movl 4(%rdi,%rcx,4), %ebx
    movl %eax, 4(%rdi,%rcx,4)
    movl %ebx, (%rdi,%rcx,4)
.T1:
    movq %rsi, %rax
    subq %rcx, %rax
    cmpq $0x2, %rax
    jne .L1
.T2:
    decq %rsi
    cmpq $0x1, %rsi
    jne bubble
    ret

我只是用命令编译

gcc bubble.c func.s

现在,只要像上面那样编译并运行,就没有错误,程序按预期运行(注意-我正在Ubuntu上编译和运行)。
但是,在使用

gcc bubble.c func.s -g -fsanitize=address

运行时,我得到以下错误,

=================================================================
==654==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000001 (pc 0x55d713a2944e bp 0x7ffed345b730 sp 0x7ffed345b6a0 T0)
==654==The signal is caused by a WRITE memory access.
==654==Hint: address points to the zero page.
    #0 0x55d713a2944e in main /mnt/c/Users/rudy/Desktop/CSO/test/bubble.c:6
    #1 0x7fe6ec4b0d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #2 0x7fe6ec4b0e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #3 0x55d713a29124 in _start (/mnt/c/Users/rudy/Desktop/CSO/test/a.out+0x1124)        

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /mnt/c/Users/rudy/Desktop/CSO/test/bubble.c:6 in main    
==654==ABORTING

在将其放入gcc时,它运行良好,并以

[Inferior 1 (process 685) exited normally]

并且在使用valgrind命令时,

valgrind --leak-check=full ./a.out

它运行良好,并退出

==694== 
==694== HEAP SUMMARY:
==694==     in use at exit: 0 bytes in 0 blocks
==694==   total heap usage: 3 allocs, 3 frees, 2,068 bytes allocated
==694==
==694== All heap blocks were freed -- no leaks are possible
==694==
==694== For lists of detected and suppressed errors, rerun with: -s
==694== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

那为什么地址消毒器会给予段错误呢?如果我的问题或格式有任何问题,我道歉,这是我的第一个问题。

zz2j4svz

zz2j4svz1#

您的bubble函数会清除%rbx寄存器(在返回之前修改它而不恢复其先前的值)。这违反了Linux/SysV x86-64调用约定,该约定指定%rbx由函数调用保留。参见What registers are preserved through a linux x86-64 function callx86 Assembly - Why is [e]bx preserved in calling conventions?
最简单的修复方法是使用一个不同的寄存器,指定为call-clobbered,例如%rdx。如果你真的想使用%rbx,那么在函数的顶部使用push %rbx,在返回之前使用pop %rbx
至于为什么它只在使用AddressSanitizer时崩溃:如果没有ASAN,编译后的main代码实际上不会在函数调用中存储任何重要值;实际上,它根本不使用%rbx。(对于未优化的编译来说,这并不奇怪。)我猜main的调用者也没有在那里存储值。所以在这种情况下不会造成伤害。但是当ASAN打开时,%rbx被它添加的检测代码使用。所以我们不能相信ASAN的严格检查找到了你的bug;只是启用它会以触发它的方式更改生成的代码。
(Side bug:如果传入一个长度为0或1的数组,你的bubble函数将崩溃。

相关问题