A List of Common Reasons for Segmentation Faults

15 minute read

Segment fault are common issues for native developers, it is caused by accessing memory location that is not allowed to, I’m gonna list the possible reasons of segfault, and show how to analyze these kind of issues, a tombstone was generated when segment fault occurs.

Possible Reasons that get segment fault

Google has developed crasher which shows the reasons that lead to the native crash, and also I find some in this answer, in this section those cases will be illustrated, and other cases that I found will be also be listed here.

Abort by calling abort/assert/raise/LOG(FATAL)

The first type of crash was made by calling abort, assert, raise or LOG(FATAL) and the friends on purpose, the exception logs are much similar, the fault addresses are N/A, the following is created with crasher abort:

F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
F DEBUG   : LineageOS Version: '16.0-20200803-UNOFFICIAL-rpi3'
F DEBUG   : Build fingerprint: 'Raspberry/lineage_rpi3/rpi3:9/PQ3A.190801.002/fdbai08031438:userdebug/test-keys'
F DEBUG   : Revision: '0'
F DEBUG   : ABI: 'arm'
F DEBUG   : pid: 2384, tid: 2384, name: crasher  >>> crasher <<<
F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG   :     r0  00000000  r1  00000950  r2  00000006  r3  00000008
F DEBUG   :     r4  00000950  r5  00000950  r6  fff99e0c  r7  0000010c
F DEBUG   :     r8  00000000  r9  00000000  r10 00000000  r11 00000000
F DEBUG   :     ip  fff9a010  sp  fff99df8  lr  ea9a5ed9  pc  ea99dd2a
F DEBUG   :
F DEBUG   : backtrace:
F DEBUG   :     #00 pc 0001cd2a  /system/lib/libc.so (abort+58)
F DEBUG   :     #01 pc 000010f3  /system/bin/crasher (maybe_abort+26)
F DEBUG   :     #02 pc 000014e3  /system/bin/crasher (do_action+686)
F DEBUG   :     #03 pc 00002341  /system/bin/crasher (main+68)
F DEBUG   :     #04 pc 00088bc1  /system/lib/libc.so (__libc_init+48)
F DEBUG   :     #05 pc 00001097  /system/bin/crasher (_start_main+38)
F DEBUG   :     #06 pc 00000306  <anonymous:eadee000>

The key messages used to identify these kind of issues are listed below, and the tombstone files are also included:

Type Key Messages Log
Abort F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : #00 pc 0001cd2a /system/lib/libc.so (abort+58)
abort
Assert F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : Abort message: ‘some_file.c:123: assertion “false” failed’
assert
assert2
LOG(FATAL) F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : #01 pc 000068fb /system/lib/libbase.so (android::base::DefaultAborter(char const*)+6)
LOG(FATAL)
Raise F DEBUG : signal 8 (SIGFPE), code -6 (SI_TKILL), fault addr -------- raise

Null pointer dereference

If fault addr is 0 or lower address, this indicates a null pointer dereference, this could be caused by use after free, uninitialized pointer, wild pointer or missing argument in printf/sprintf, also malloc is not guaranteed to be successful, missing the return value check can also lead to undefined behavior.

This is an example crash log of use after free case:

F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
F DEBUG   : LineageOS Version: '16.0-20200803-UNOFFICIAL-rpi3'
F DEBUG   : Build fingerprint: 'Raspberry/lineage_rpi3/rpi3:9/PQ3A.190801.002/fdbai08031438:userdebug/test-keys'
F DEBUG   : Revision: '0'
F DEBUG   : ABI: 'arm'
F DEBUG   : pid: 2936, tid: 2936, name: crasher  >>> crasher <<<
F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
F DEBUG   : Cause: null pointer dereference
F DEBUG   :     r0  00000005  r1  00000068  r2  00000006  r3  ffffffff
F DEBUG   :     r4  00000000  r5  aac429bf  r6  00000006  r7  00000000
F DEBUG   :     r8  00000000  r9  00000000  r10 00000000  r11 00000000
F DEBUG   :     ip  aac44fc8  sp  ffebfc38  lr  aac3fe8f  pc  ec9c98ce
F DEBUG   :
F DEBUG   : backtrace:
F DEBUG   :     #00 pc 000398ce  /system/lib/libc.so (__strncpy_chk2+26)
F DEBUG   :     #01 pc 00001e8b  /system/bin/crasher (uaf+106)
F DEBUG   :     #02 pc 0000158f  /system/bin/crasher (do_action+710)
F DEBUG   :     #03 pc 000024b5  /system/bin/crasher (main+68)
F DEBUG   :     #04 pc 00088bc1  /system/lib/libc.so (__libc_init+48)
F DEBUG   :     #05 pc 0000112b  /system/bin/crasher (_start_main+38)
F DEBUG   :     #06 pc 00000306  <anonymous:ecdab000>

Other examples in crasher regarding null pointer dereferences are as follows:

Type Key Messages Log
strlen-NULL F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
F DEBUG : Cause: null pointer dereference
strlen null
call-null F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
F DEBUG : Cause: null pointer dereference
call null
myprintf F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x5
F DEBUG : Cause: null pointer dereference
missing argument

The last one will report a warning if -Wall or -Wformat option is on, or an error if -Werror was also enabled:

system/core/debuggerd/crasher/crasher.cpp:199:17: error: more '%' conversions than data arguments [-Werror,-Wformat]
    printf("%s %d\n", i);

FORTIFY failure

bionic c library keep involving along with android, in Android P, formerly null pointer dereference cases such as fprintf-NULL and readdir-NULL are now fall into FORTIFY failure case, a sanity check on FILE* for nullptr is added, this is done by the following macro:

#define CHECK_FP(fp) \
  if (fp == nullptr) __fortify_fatal("%s: null FILE*", __FUNCTION__)

which is defined in libc/stdio/local.h.

In bionic, buffer size is also checked before the real syscall, this is a FORTIFY failure case try to read 32-byte data into a 10-byte array:

F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
F DEBUG   : LineageOS Version: '16.0-20200803-UNOFFICIAL-rpi3'
F DEBUG   : Build fingerprint: 'Raspberry/lineage_rpi3/rpi3:9/PQ3A.190801.002/fdbai08031438:userdebug/test-keys'
F DEBUG   : Revision: '0'
F DEBUG   : ABI: 'arm'
F DEBUG   : pid: 3118, tid: 3118, name: crasher  >>> crasher <<<
F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG   : Abort message: 'FORTIFY: read: prevented 32-byte write into 10-byte buffer'
F DEBUG   :     r0  00000000  r1  00000c2e  r2  00000006  r3  00000008
F DEBUG   :     r4  00000c2e  r5  00000c2e  r6  ffcfe9e4  r7  0000010c
F DEBUG   :     r8  00000000  r9  00000000  r10 00000000  r11 00000000
F DEBUG   :     ip  ffcfec00  sp  ffcfe9d0  lr  e9374ed9  pc  e936cd2a
F DEBUG   :
F DEBUG   : backtrace:
F DEBUG   :     #00 pc 0001cd2a  /system/lib/libc.so (abort+58)
F DEBUG   :     #01 pc 00039113  /system/lib/libc.so (__fortify_fatal(char const*, ...)+26)
F DEBUG   :     #02 pc 0003952b  /system/lib/libc.so (__read_chk+62)
F DEBUG   :     #03 pc 00001613  /system/bin/crasher (do_action+842)
F DEBUG   :     #04 pc 000024b5  /system/bin/crasher (main+68)
F DEBUG   :     #05 pc 00088bc1  /system/lib/libc.so (__libc_init+48)
F DEBUG   :     #06 pc 0000112b  /system/bin/crasher (_start_main+38)
F DEBUG   :     #07 pc 00000306  <anonymous:e96c5000>
Type Key Messages Log
fortify F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : Abort message: ‘FORTIFY: read: prevented 32-byte write into 10-byte buffer’
fortify
fprintf-NULL F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : Abort message: ‘FORTIFY: fprintf: null FILE*’
fprintf null
readdir-NULL F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : Abort message: ‘FORTIFY: readdir: null DIR*’
readdir null

Check the Abort message and __fortify_fatal in the backtrace to see if it is FORTIFY related.

Array index past the end of the array

void oob()
{
	int a[10];

	a[10] = 100;
}

The above code creates an out of bound issue, it is undefined behaviour and can be detected by Bound Sanitizer as we shown in the previous post Android native memory debugging, the compiler option -fstack-protector-all is also served for this purpose.

Type Key Messages Log
Out of Bounds F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : Abort message: ‘stack corruption detected (-fstack-protector)’
smash stack

Seccomp SIGSYS from a disallowed system call

Seccomp (SECure COMPuting with filters) is a kernel feature that used for filtering unwanted syscalls by applying BPF, android take this advantage to harden android system from Android O, the filter is applied to zygote, thus all syscall from app will be checked, if there is any syscall that is forbidden it will crash, seccomp filter must set explicitly for native applications with set_system_seccomp_filter(), for example, swapon/wapoff are blocked as they are considered to be unsecure to the system, if you apps make calls to them, the below exception will be reported in logcat:

F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
F DEBUG   : LineageOS Version: '16.0-20200803-UNOFFICIAL-rpi3'
F DEBUG   : Build fingerprint: 'Raspberry/lineage_rpi3/rpi3:9/PQ3A.190801.002/fdbai08031438:userdebug/test-keys'
F DEBUG   : Revision: '0'
F DEBUG   : ABI: 'arm'
F DEBUG   : pid: 2521, tid: 2521, name: crasher  >>> crasher <<<
F DEBUG   : signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr --------
F DEBUG   : Cause: seccomp prevented call to disallowed arm system call 115
F DEBUG   :     r0  b1d93b3d  r1  a1b570dc  r2  00000001  r3  00000000
F DEBUG   :     r4  b1d915b9  r5  ff9ed544  r6  00000002  r7  00000073
F DEBUG   :     r8  00000000  r9  00000000  r10 00000000  r11 00000000
F DEBUG   :     ip  ff9ed550  sp  ff9ed378  lr  b1d908bd  pc  edd1fac8
F DEBUG   :
F DEBUG   : backtrace:
F DEBUG   :     #00 pc 00055ac8  /system/lib/libc.so (swapoff+12)
F DEBUG   :     #01 pc 000018b9  /system/bin/crasher (do_action+1592)
F DEBUG   :     #02 pc 000025fd  /system/bin/crasher (main+68)
F DEBUG   :     #03 pc 00088bc1  /system/lib/libc.so (__libc_init+48)
F DEBUG   :     #04 pc 0000112b  /system/bin/crasher (_start_main+38)
F DEBUG   :     #05 pc 00000306  <anonymous:edf3b000>
Type Key Messages Log
swapoff F DEBUG : signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr --------
F DEBUG : Cause: seccomp prevented call to disallowed arm system call 115
seccomp

Write access to read-only memory address

Trying to write to read-only memory location will cause app crash with error code SEGV_ACCERR, for example:

void accerr()
{
	char *str;

	str = "hello";
	str[1] = 'w';
}

The code will lead to below exception:

F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
F DEBUG   : LineageOS Version: '16.0-20200803-UNOFFICIAL-rpi3'
F DEBUG   : Build fingerprint: 'Raspberry/lineage_rpi3/rpi3:9/PQ3A.190801.002/fdbai08031438:userdebug/test-keys'
F DEBUG   : Revision: '0'
F DEBUG   : ABI: 'arm'
F DEBUG   : pid: 1392, tid: 1392, name: crasher  >>> crasher <<<
F DEBUG   : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0xb3ad2a8c
F DEBUG   :     r0  f1afbe0c  r1  b3ad2a8b  r2  00000077  r3  00000000
F DEBUG   :     r4  b3ad0535  r5  ff8ab164  r6  00000002  r7  ff8ab170
F DEBUG   :     r8  00000000  r9  00000000  r10 00000000  r11 00000000
F DEBUG   :     ip  00000000  sp  ff8aaf88  lr  b3acf563  pc  b3acff40
F DEBUG   :
F DEBUG   : backtrace:
F DEBUG   :     #00 pc 00001f40  /system/bin/crasher (accerr+24)
F DEBUG   :     #01 pc 0000155f  /system/bin/crasher (do_action+734)
F DEBUG   :     #02 pc 00002579  /system/bin/crasher (main+68)
F DEBUG   :     #03 pc 00088bc1  /system/lib/libc.so (__libc_init+48)
F DEBUG   :     #04 pc 0000112b  /system/bin/crasher (_start_main+38)
F DEBUG   :     #05 pc 00000306  <anonymous:f1cc5000>
Type Key Messages Log
write to RO F DEBUG : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0xb3ad2a8c write to read only

Invalid use of free

I think this is not a common issue, passing address of variable in stack to free will cause app to crash:

F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
F DEBUG   : LineageOS Version: '16.0-20200803-UNOFFICIAL-rpi3'
F DEBUG   : Build fingerprint: 'Raspberry/lineage_rpi3/rpi3:9/PQ3A.190801.002/fdbai08031438:userdebug/test-keys'
F DEBUG   : Revision: '0'
F DEBUG   : ABI: 'arm'
F DEBUG   : pid: 1407, tid: 1407, name: crasher  >>> crasher <<<
F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG   : Abort message: 'Invalid address 0xfff18d0c passed to free: value not allocated'
F DEBUG   :     r0  00000000  r1  0000057f  r2  00000006  r3  00000008
F DEBUG   :     r4  0000057f  r5  0000057f  r6  fff18ca4  r7  0000010c
F DEBUG   :     r8  f5b0b008  r9  00000000  r10 00000000  r11 00000000
F DEBUG   :     ip  00000000  sp  fff18c90  lr  f5ca8ed9  pc  f5ca0d2a
F DEBUG   :
F DEBUG   : backtrace:
F DEBUG   :     #00 pc 0001cd2a  /system/lib/libc.so (abort+58)
F DEBUG   :     #01 pc 0007c3a5  /system/lib/libc.so (ifree+880)
F DEBUG   :     #02 pc 0007c4c1  /system/lib/libc.so (je_free+68)
F DEBUG   :     #03 pc 00001cb9  /system/bin/crasher (abuse_heap+20)
F DEBUG   :     #04 pc 00001827  /system/bin/crasher (do_action+1446)
F DEBUG   :     #05 pc 00002579  /system/bin/crasher (main+68)
F DEBUG   :     #06 pc 00088bc1  /system/lib/libc.so (__libc_init+48)
F DEBUG   :     #07 pc 0000112b  /system/bin/crasher (_start_main+38)
F DEBUG   :     #08 pc 00000306  <anonymous:f60c7000>
Type Key Messages Log
F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
F DEBUG : Abort message: ‘Invalid address 0xfff18d0c passed to free: value not allocated’
heap  

Returning value of local variable

This is similar to OOB, which may or may not cause a crash, but as it is undefined behavior, the results depending on the compiler implementation, there is a compiler option -Wreturn-stack-address to give it a warning if it is enabled:

system/core/debuggerd/crasher/crasher.cpp:204:9: error: address of stack memory associated with local variable 's' returned [-Werror,-Wreturn-stack-address]
        return s;

This is an example code of accessing address of s after local_var returned:

char *local_var() {
	char s[] = "hello";
	return s;
}

void test_local_var() {
	printf("The string is:%s!\n", local_var());
}

List all the reasons which may cause a segment fault is virtually impossible, do our best to avoid them by at least NEVER IGNORE ANY COMPILER WARNING, and keep below options always on:

-Werror
-Wall

Analyzing native crash log

The very first thing I do when I found a native crash is to decode the backtrace with development/scripts/stack, and review the line of code and the context, save the crash log to a file and feed to the script like this:

$ source build/envsetup.sh
$ lunch lineage_rpi3-userdebug
$ stack --arch=arm accerr
build/make/core/combo/TARGET_linux-arm.mk:43: warning: cortex-a53 is armv8-a.
build/make/core/combo/TARGET_linux-arm.mk:45: warning: TARGET_ARCH_VARIANT, armv7-a-neon, ignored! Use armv8-a instead.
Reading native crash info from stdin
Reading symbols from /opt/workdir/lineageos/lineageos-16/out/target/product/rpi3/symbols
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0xb2f8ac28 in tid 2686 (crasher), pid 2686 (crasher)
Revision: '0'
pid: 2686, tid: 2686, name: crasher  >>> crasher <<<
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0xb2f8ac28
     r0  f1aeee0c  r1  b2f8ac27  r2  00000077  r3  00000000
     r4  b2f886a9  r5  ff9939d4  r6  00000002  r7  ff9939e0
     r8  00000000  r9  00000000
     ip  00000000  sp  ff9937f8  lr  b2f87597  pc  b2f87fb4
Using arm toolchain from: /opt/workdir/lineageos/lineageos-16/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/

Stack Trace:
  RELADDR   FUNCTION        FILE:LINE
  00001fb4  accerr+24       system/core/debuggerd/crasher/crasher.cpp:193
  00001593  do_action+734   system/core/debuggerd/crasher/crasher.cpp:309
  000026ed  main+68         system/core/debuggerd/crasher/crasher.cpp:406
  00088bc1  __libc_init+48  bionic/libc/bionic/libc_init_dynamic.cpp:129
  0000115f  _start_main+38  bionic/libc/arch-common/bionic/crtbegin.c:45
  00000306  <unknown>       <anonymous:f1d34000>

One thing needs to be mentioned here is the source code must be kept unchanged, or the backtrace and the code maybe unmatched, if you find the line of code is definitely not the suspected one, then check the BuildId in tombstone file and the output of file command to see if they are the same one:

$ file out/target/product/rpi3/system/bin/crasher
out/target/product/rpi3/system/bin/crasher: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /system/bin/linker, BuildID[md5/uuid]=99f763bbd73b1764ad54af6c2bbdb94f, stripped

$ rg crasher tombstone_09 | rg BuildId
--->b2f86000-b2f8bfff r-x         0      6000  /system/bin/crasher (BuildId: 99f763bbd73b1764ad54af6c2bbdb94f)

Another less likely to happen is the executable binary is put to the target file system in different directory as android build output directory, this might be a problem when you debugging native applications, this will cause the backtrace cannot be parsed correctly by the script, unless you fix the path manually before passing it to the stack script.

The key information in tombstone

Summarize the information in crash log and tombstone we might be interested:

  • The signal and code
  • The fault address
  • The registers
  • Frame #00 is 00000000 (call-null case)
  • The backtrace
  • Memory map (permission)
  • Open files info in tombstone (fd leak)
  • BuildID

Online debugging with gdb

If the crash can be reproduced, then the recommended way to do is using gdb, run gdb server in android:

# gdbserver :8989 stack
[ 8214.476024] init: Untracked pid 1624 received signal 9
Process stack created; pid = 1625
Listening on port 8989

Then connect it from host:

$ adb forward tcp:8989 tcp:8989
$ arm-linux-gnueabi-gdb -q -nh										\
	-ex 'set solib-search-path out/target/product/rpi3/symbols'		\
	-ex 'set solib-absolute-prefix out/target/product/rpi3/symbols'	\
	-ex 'target extended-remote ip.add.re.ss:8989'					\
	out/target/product/rpi3/symbols/system/bin/crasher

(gdb) monitor exit /* quit gdbserver */

You can find the prebuilt gdbserver in prebuilts/misc/android-arm/gdbserver/ in case it is not packaged into android system image.

Cordeump

Sometimes the tombstone file along with the crash information is not sufficient, core dump can provide more information than that, by default, it is disabled in Android, and it need kernel support.

# zcat /proc/config.gz | grep -E 'ELF_CORE|COREDUMP'
CONFIG_ELF_CORE=y
CONFIG_COREDUMP=y

Make sure the above options are enabled in your kernel image.

Use ulimit to enable/disable cordeump:

# Enable coredump
ulimit -S -c unlimited

# Disable coredump
ulimit -S -c 0

By default coredump file will be created in the current working directory named core, unless the core_pattern was set, this can be either done in command line:

sysctl kernel.core_pattern="/data/coredump/core.%e.%p"

or put the following in init.rc:

write  /proc/sys/kernel/core_pattern /data/coredump/core.%e.%p

See man core for more information.

Analyze core dump with gdb

The executable binary used in gdb needs to be the one with debug information.

$ adb pull /data/coredump/
$ arm-linux-gnueabi-gdb -q -nh out/target/product/rpi3/symbols/system/bin/crasher
Reading symbols from out/target/product/rpi3/symbols/system/bin/crasher...
(gdb) set solib-search-path out/target/product/rpi3/symbols/
(gdb) set solib-absolute-prefix out/target/product/rpi3/symbols
(gdb) core coredump/core.crasher.3007
[New LWP 3007]
Core was generated by `crasher seccomp'.
Program terminated with signal SIGSYS, Bad system call.
#0  swapoff () at bionic/libc/arch-arm/syscalls/swapoff.S:10
10	    mov     r7, ip
(gdb) where
#0  swapoff () at bionic/libc/arch-arm/syscalls/swapoff.S:10
#1  0xafccc90a in do_action (arg=0xffcbdb3b "seccomp") at system/core/debuggerd/crasher/crasher.cpp:360
#2  0xafccd6f0 in main (argc=2, argv=0xffcbd6e4) at system/core/debuggerd/crasher/crasher.cpp:406
(gdb) frame 1 /* switch to frame 1 */
#1  0xafccc90a in do_action (arg=0xffcbdb3b "seccomp") at system/core/debuggerd/crasher/crasher.cpp:360
360	        swapoff("/dev/null");
(gdb) l
355	        munmap(map, sizeof(int));
356	        map[0] = '8';
357	    } else if (!strcasecmp(arg, "seccomp")) {
358	        set_system_seccomp_filter();
359	        //syscall(99999);
360	        swapoff("/dev/null");
[...]
(gdb) disassemble
(gdb) info files
(gdb) info threads
(gdb) maintenance info sections
(gdb) i[nfo] proc m[appings]
Mapped address spaces:

	Start Addr   End Addr       Size     Offset objfile
	0xab6fc000 0xab702000     0x6000        0x0 /system/bin/crasher
	0xab702000 0xab703000     0x1000     0x5000 /system/bin/crasher
	0xeb66f000 0xeb673000     0x4000        0x0 /system/lib/libnetd_client.so
	[...]
	0xebb04000 0xebb24000    0x20000        0x0 /dev/__properties__/properties_serial
	0xebb25000 0xebb28000     0x3000        0x0 /dev/__properties__/property_info
	0xebb39000 0xebbf7000    0xbe000        0x0 /system/bin/linker
	0xebbf7000 0xebbfd000     0x6000    0xbd000 /system/bin/linker

Any access out of the range shown above is illegal.

Use below command to save some time:

$ arm-linux-gnueabi-gdb -q -nh										\
	-ex 'set solib-search-path out/target/product/rpi3/symbols'		\
	-ex 'set solib-absolute-prefix out/target/product/rpi3/symbols'	\
	-ex 'core coredump/core.crasher.3007'							\
	out/target/product/rpi3/symbols/system/bin/crasher

If the gdb complain about exec file is newer, please be caution, the source code may changed in anyway, this may lead to parsing issue:

warning: exec file is newer than core file.

Core dump file is actually a ELF file, which means you can inspect it with readelf or objdump.

$ file coredump/core.crasher.3007
coredump/core.crasher.3282: ELF 32-bit LSB core file ARM, version 1 (SYSV), SVR4-style, from 'crasher seccomp', real uid: 0, effective uid: 0, real gid: 0, effective gid: 0, execfn: '/system/bin/crasher', platform: 'v8l'