Search code examples
linuxassemblyx86-64nasmbus-error

Retrieving memory data with non-canonical-address causes SIGSEGV rather than SIGBUS


I can not produce a "Bus error" with the following assembly code. Here the memory address I use is not a legal "canonical-address". So, how can I trigger that error?

I was running this snippet of code under Ubuntu 20.04 LTS with NASM 2.14.02, but it results in a SIGSEGV segmentation fault on the load, not SIGBUS.

global _start
section .text
_start:
    mov rax, [qword 0x11223344557788]
    mov rax, 60
    xor rdi, rdi
    syscall

Corresponding X86-64 assembly code after compiling:

Disassembly of section .text:

0000000000401000 <_start>:
  401000:   48 a1 88 77 55 44 33    movabs 0x11223344557788,%rax
  401007:   22 11 00 
  40100a:   b8 3c 00 00 00          mov    $0x3c,%eax
  40100f:   48 31 ff                xor    %rdi,%rdi
  401012:   0f 05                   syscall

Solution

  • If you review the Instruction Set Architecture manual for the MOV instruction you would find that accessing a non-canonical address yields a #GP(0) General Protection Fault:

    enter image description here

    Linux maps all #GP exceptions to SIGSEGV signal (Segmentation Fault). However, in Linux there is a way for a non-canonical address to cause a Bus Error and that is by getting the processor to raise an #SS (Stack Segment) exception. Linux maps #SS exceptions to the SIGBUS signal. Setting the stack pointer to a non-canonical address and then performing a stack related operation will produce such an exception.

    This code should produce a Bus Error:

    global _start
    section .text
    _start:
        mov rsp, 0x8000000000000000 ; Set RSP to a non-canonical address
        push rax                    ; Pushing value on stack should produce BUS ERROR
    

    One other way of producing a Bus Error on Linux is to raise an #AC (Alignment Check) exception. If you write ring 3 (user) code that enables the Alignment Check bit (bit 18) in RFLAGS and do an unaligned memory access you should also receive a SIGBUS signal. This code should produce a Bus Error:

    global _start
    section .text
    _start:
        pushf                      ; Put current RFLAGS on the stack
        or dword [rsp], 1<<18      ; Enable bit 18 (Alignment Check) of the
                                   ;     RFLAGS value saved on stack
        popf                       ; Pop new RFLAGS flags value into the RFLAGS register
        mov eax, [rsp + 1]         ; Move a DWORD value from unaligned address
                                   ;     Should produce a BUS ERROR