John Costella
John Costella

Reputation: 11

Why does AddressSanitizer on Mac arm64 only show the part of the stack trace from the same file?

I've recently moved from an Intel Mac to an M1 (arm64). AddressSanitizer seems to work the same, with Homebrew clang, except that the stack traces only show the functions in the stack that are in the same source file as the call that threw the signal. This wasn't the case on Intel Macs or on Linux. I feel like I'm missing something obvious, but can't find it.

Example 1: All functions in the same source file

a.c:

void bar(void) {
  *(volatile char *)0 = 0;
}

void foo(void) {
  bar();
}

int main(void) {
  foo();
}

Compile, link, and symbolize:

$ /opt/homebrew/opt/llvm/bin/clang -c -fsanitize=address -O1 -g -fno-omit-frame-pointer a.c -o a.o
$ /opt/homebrew/opt/llvm/bin/clang -fsanitize=address a.o -o a.out
$ dsymutil a.out

Run:

$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==31187==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x00010050bf5c bp 0x00010054d08c sp 0x00016f8f7660 T0)
==31187==The signal is caused by a UNKNOWN memory access.
==31187==Hint: address points to the zero page.
    #0 0x10050bf5c in bar /Users/jpc/src/asantest/a.c:2:23
    #1 0x10050bf5c in foo /Users/jpc/src/asantest/a.c:6:3
    #2 0x10050bf5c in main /Users/jpc/src/asantest/a.c:10:3

==31187==Register values:
 x[0] = 0x0000000000000001   x[1] = 0x000000016f8f77d0   x[2] = 0x000000016f8f77e0   x[3] = 0x000000016f8f78f8  
 x[4] = 0x0000000000000000   x[5] = 0x0000000000000000   x[6] = 0x0000000000000000   x[7] = 0x0000000000000000  
 x[8] = 0x0000000000000000   x[9] = 0x0000000000000002  x[10] = 0x0000000000000000  x[11] = 0x0000000000000002  
x[12] = 0x0000000000000002  x[13] = 0x0000000000000000  x[14] = 0x0000000000000020  x[15] = 0x0000000000000000  
x[16] = 0x0000000300fd7088  x[17] = 0x6ae100016f8f6a70  x[18] = 0x0000000000000000  x[19] = 0x00000001005fc060  
x[20] = 0x000000010050bf34  x[21] = 0x00000001005a8070  x[22] = 0x0000000000000000  x[23] = 0x0000000000000000  
x[24] = 0x0000000000000000  x[25] = 0x0000000000000000  x[26] = 0x0000000000000000  x[27] = 0x0000000000000000  
x[28] = 0x0000000000000000     fp = 0x000000016f8f7660     lr = 0x000000010054d08c     sp = 0x000000016f8f7660  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /Users/jpc/src/asantest/a.c:2:23 in bar
==31187==ABORTING
zsh: abort      ./a.out

All three functions in the stack are shown in the trace, as expected.

Example 2: Violating function in a different source file

b1.c:

void bar(void);

void foo(void) {
  bar();
}

int main(void) {
  foo();
}

b2.c:

void bar(void) {
  *(volatile char *)0 = 0;
}

Compile, link, and symbolize:

$ /opt/homebrew/opt/llvm/bin/clang -c -fsanitize=address -O1 -g -fno-omit-frame-pointer b1.c -o b1.o
$ /opt/homebrew/opt/llvm/bin/clang -c -fsanitize=address -O1 -g -fno-omit-frame-pointer b2.c -o b2.o
$ /opt/homebrew/opt/llvm/bin/clang -fsanitize=address b1.o b2.o -o b.out
$ dsymutil b.out

Run:

$ ./b.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==31297==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000102afff5c bp 0x000102afff10 sp 0x00016d303650 T0)
==31297==The signal is caused by a UNKNOWN memory access.
==31297==Hint: address points to the zero page.
    #0 0x102afff5c in bar /Users/jpc/src/asantest/b2.c:2:23

==31297==Register values:
 x[0] = 0x0000000000000001   x[1] = 0x000000016d3037d0   x[2] = 0x000000016d3037e0   x[3] = 0x000000016d3038f8  
 x[4] = 0x0000000000000000   x[5] = 0x0000000000000000   x[6] = 0x0000000000000000   x[7] = 0x0000000000000000  
 x[8] = 0x0000000000000000   x[9] = 0x0000000000000002  x[10] = 0x0000000000000000  x[11] = 0x0000000000000002  
x[12] = 0x0000000000000002  x[13] = 0x0000000000000000  x[14] = 0x0000000000000020  x[15] = 0x0000000000000000  
x[16] = 0x00000003074e7088  x[17] = 0x6ae100016d302a70  x[18] = 0x0000000000000000  x[19] = 0x0000000102b08060  
x[20] = 0x0000000102afff04  x[21] = 0x0000000102cb8070  x[22] = 0x0000000000000000  x[23] = 0x0000000000000000  
x[24] = 0x0000000000000000  x[25] = 0x0000000000000000  x[26] = 0x0000000000000000  x[27] = 0x0000000000000000  
x[28] = 0x0000000000000000     fp = 0x000000016d303650     lr = 0x0000000102afff10     sp = 0x000000016d303650  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /Users/jpc/src/asantest/b2.c:2:23 in bar
==31297==ABORTING
zsh: abort      ./b.out

The violating function bar in b2.c is shown in the stack trace, but the two calling functions in the stack, main and foo in b1.c, are not. On Intel Macs or Linux the whole stack trace would be shown, from whichever source files they came from.

Upvotes: 0

Views: 1582

Answers (1)

John Costella
John Costella

Reputation: 11

It looks like this is simply a bug in the current arm64 implementation; I've filed this bug report. Running the same commands as above on the same machine but under Rosetta, under which Brew installs in /usr/local/bin/:

Example 1

$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3697==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000102e0ff43 bp 0x000309fa86c0 sp 0x000309fa86c0 T0)
==3697==The signal is caused by a WRITE memory access.
==3697==Hint: address points to the zero page.
    #0 0x102e0ff43 in bar /Users/jpc/src/asantest/a.c:2:23
    #1 0x102e0ff43 in foo /Users/jpc/src/asantest/a.c:6:3
    #2 0x102e0ff43 in main /Users/jpc/src/asantest/a.c:10:3
    #3 0x2030ea51d in start (/usr/lib/dyld:x86_64+0x551d)
    #4 0x2030e4fff  (<unknown module>)

==3697==Register values:
rax = 0x0000100000000000  rbx = 0x000000010b343060  rcx = 0x0000000309fa8938  rdx = 0x0000000309fa8808  
rdi = 0x0000000000000001  rsi = 0x0000000309fa87f8  rbp = 0x0000000309fa86c0  rsp = 0x0000000309fa86c0  
 r8 = 0x0000000000145e7b   r9 = 0xffffffff00000000  r10 = 0x0000000000000000  r11 = 0x000000020312001a  
r12 = 0x00000002031653a0  r13 = 0x0000000309fa8778  r14 = 0x0000000102e0ff30  r15 = 0x0000000203151010  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /Users/jpc/src/asantest/a.c:2:23 in bar
==3697==ABORTING
zsh: abort      ./a.out

Example 2

$ ./b.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3847==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000100329f43 bp 0x0003047ad6b0 sp 0x0003047ad6b0 T0)
==3847==The signal is caused by a WRITE memory access.
==3847==Hint: address points to the zero page.
    #0 0x100329f43 in bar /Users/jpc/src/asantest/b2.c:2:23
    #1 0x100329f18 in foo /Users/jpc/src/asantest/b1.c:4:3
    #2 0x100329f18 in main /Users/jpc/src/asantest/b1.c:8:3
    #3 0x2003d551d in start (/usr/lib/dyld:x86_64+0x551d)
    #4 0x2003cffff  (<unknown module>)

==3847==Register values:
rax = 0x0000100000000000  rbx = 0x000000010885d060  rcx = 0x00000003047ad938  rdx = 0x00000003047ad808  
rdi = 0x0000000000000001  rsi = 0x00000003047ad7f8  rbp = 0x00000003047ad6b0  rsp = 0x00000003047ad6b0  
 r8 = 0x00000000001460cd   r9 = 0xffffffff00000000  r10 = 0x0000000000000000  r11 = 0x000000020040b01a  
r12 = 0x00000002004503a0  r13 = 0x00000003047ad778  r14 = 0x0000000100329f10  r15 = 0x000000020043c010  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /Users/jpc/src/asantest/b2.c:2:23 in bar
==3847==ABORTING
zsh: abort      ./b.out

Upvotes: 1

Related Questions