3

I am currently taking a course which requires me to write assembly code, to be precise x86-64 AT&T syntax assembly code. Below is a c file which contains the function definition of a function "bubble" whose assembly code I have to write.

#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);
}

And below is the assembly code for the function 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

I simply compile with the command,

gcc bubble.c func.s

Now on just compiling as above and running, there are no errors and the program runs as expected (note - I am compiling and running on Ubuntu).

However, on compiling using

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

and running, I get the following error,

=================================================================
==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

On putting it into gcc, it runs fine and exits with

[Inferior 1 (process 685) exited normally]

and on using valgrind with command,

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

it runs fine and exits with

==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)

So then why does address sanitizer give that segfault? Apologies if there are any issues with my question or formatting, this is my first question.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Anili
  • 88
  • 6
  • 3
    Works here. What input did you try? What is the instruction at the printed address? `bubble.c:6` looks very suspicious as that is the `int main(){` so probably function prologue that certainly should not crash. PS: +1 good first question :) – Jester May 03 '23 at 12:27
  • 2
    Your `bubble` function has a bug where it will misbehave if the `len` argument is 1 or 0. Is that what you used as input? – Nate Eldredge May 03 '23 at 14:31
  • 3
    Another bug is that your `bubble` function clobbers `%rbx` which is supposed to be call-preserved. You need to either push/pop it, or choose another register that is designated as call-clobbered. `%rdx` is available and would do fine. – Nate Eldredge May 03 '23 at 15:19
  • 1
    @NateEldredge : I happened to try this code out and it seems that clobbering RBX causes this issue. Of course your other observations for 0 and 1 elements is also correct. – Michael Petch May 03 '23 at 22:54
  • @NateEldredge No, I had actually used an input of `len` greater than 2 when this error had occured, though I now see that no matter what `len` I used, I would have had an issue. Thank you! – Anili May 04 '23 at 05:10
  • @Jester The error occurred for any input I gave it actually. I wasn't really sure how to get to the printed address, I tried using `objdump` on both `main` and `bubble` but both didn't seem to contain the printed address, so I wasn't sure how to proceed. PS: Thank you! – Anili May 04 '23 at 05:13

1 Answers1

5

Your bubble function clobbers the %rbx register (modifies it without restoring its previous value before returning). This violates the Linux/SysV x86-64 calling conventions, which specify that %rbx is to be preserved by fuction calls. See What registers are preserved through a linux x86-64 function call, x86 Assembly - Why is [e]bx preserved in calling conventions?.

The simplest fix is just to use a different register that is designated as call-clobbered, such as %rdx. If you really want to use %rbx, then push %rbx at the top of your function, and pop %rbx just before returning.

As to why it crashes only when using AddressSanitizer: it appears that without ASAN, the compiled main code doesn't actually store any important value in %rbx across the function call; indeed it doesn't use %rbx at all. (Not too surprising for unoptimized compilation.) And I guess that the caller of main doesn't store a value there either. So no harm happens to result in that case. But when ASAN is switched on, %rbx is used by the instrumentation code that it adds. So we can't actually credit ASAN's rigorous checking for finding your bug; it's simply that enabling it changes the generated code in a way that happens to trigger it.

(Side bug: your bubble function will crash if passed an array of length 0 or 1.)

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 1
    I had just read about callee and caller saved and other such conventions a few hours ago, but it didn't click to me that what I was doing was illegal. Also, thank you for clarifying why ASAN was the only one to detect this error, I was losing my mind over what I thought was an 'invisible' segfault! PS: I did what you said and pushed `%rbx` on to the stack and then later restored its value and now the segfault is gone. Thank you! – Anili May 04 '23 at 05:18
  • 1
    Also thanks for notifying me about the side bug, I totally forgot to add checks for those 2 conditions! – Anili May 04 '23 at 05:19