AddressSanitizer报段错误,Valgrind/GDB未检测?汇编冒泡函数异常
汇编实现冒泡排序函数的AddressSanitizer段错误问题
背景
课程作业要求用x86-64 AT&T语法汇编实现bubble冒泡排序函数,配套的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); }
对应的汇编实现代码:
.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编译运行,程序正常执行; - 添加
-g -fsanitize=address编译后,AddressSanitizer触发段错误,错误日志如下:
================================================================= ==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
- 使用GDB调试、
valgrind --leak-check=full ./a.out检测均未发现异常。
原因分析
问题核心是汇编代码违反了x86-64 System V调用约定:
%rbx属于被调用者保存寄存器,即函数作为被调用者,若要修改该寄存器,必须先将其原值压栈保存,函数退出前恢复;- 当前汇编代码直接修改了
%ebx(%rbx的低32位),未做保存和恢复操作,导致调用者main函数的%rbx寄存器状态被破坏; - 普通编译时,程序未依赖
%rbx保存关键数据,因此未触发异常;但AddressSanitizer插入的 instrumentation 代码依赖寄存器的正确调用约定,寄存器被破坏后直接引发内存访问错误; - Valgrind主要检测内存泄漏、越界访问等内存问题,不会校验寄存器调用约定;GDB若未主动检查寄存器状态,也难以定位该类问题。
修复方案
在bubble函数开头保存%rbx到栈,结尾恢复,修改后的汇编代码:
.global bubble .text bubble: pushq %rbx # 保存被调用者保存寄存器rbx 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 popq %rbx # 恢复rbx原值 ret
内容的提问来源于stack exchange,提问作者Anili
相关产品推荐
相关产品推荐

