Understanding Native Crashes in Android Development

Author: Chen Guanyu – Xiaomi

This article is published with the author’s permission.

Native Crash has always been a pain point for various apps. This article is very in-depth and is an internal sharing by Chen Guanyu from Xiaomi, generously shared with everyone. There is very little relevant information available online, and it is indeed rare for someone proficient in Native Crash analysis to share their knowledge. Due to the depth of knowledge, it may be very difficult to understand, and I can only grasp a little bit, but this article is definitely worth collecting and reviewing regularly.

Now let’s start with the main content.

1

Common Types of Native Crashes

SIGSEGV

SEGV_MAPERR

Address not in /proc/self/maps mapping

SEGV_ACCERR

No access permission

SEGV_MTESERR

MTE specific type

SIGABRT

Program aborts actively, commonly called functions like abort(), raise(), etc.

SIGILL

ILL_ILLOPC

Illegal opcode (opcode)

ILL_ILLOPN

Illegal operand (operand)

ILL_ILLADR

Illegal addressing

ILL_ILLTRP

Illegal trap, such as _builtintrap() actively crashing

ILL_PRVOPC

Illegal privileged opcode (privileged opcode)

ILL_PRVREG

Illegal privileged register (privileged register)

ILL_COPROC

Co-processor error

ILL_BADSTK

Internal stack error

SIGBUS

BUS_ADRALN

Access address misalignment

BUS_ADRERR

Access nonexistent physical address

BUS_OBJERR

Specific object hardware error

SIGFPE

FPE_INTDIV

Integer division by 0

FPE_INTOVF

Integer overflow

FPE_FLTDIV

Floating-point division by 0

FPE_FLTOVF

Floating-point overflow

FPE_FLTUND

Floating-point underflow

FPE_FLTRES

Floating-point result not precise

FPE_FLTINV

Invalid floating-point operation

FPE_FLTSUB

Out of bounds

2

Android Logs

When a program encounters a Native Crash error, Android’s logs will output to the log crash buffer. Therefore, we can use

adb logcat -b crash to capture the corresponding error report. However, the information that the log itself can provide is limited, merely the error stack and the register information of the current thread.

——— beginning of crash

06-07 01:53:32.465 12027 12027 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

06-07 01:53:32.465 12027 12027 F DEBUG : Revision: ‘0’

06-07 01:53:32.466 12027 12027 F DEBUG : ABI: ‘arm64’

06-07 01:53:32.466 12027 12027 F DEBUG : Timestamp: 2022-06-07 01:53:32.033409857+0800

06-07 01:53:32.466 12027 12027 F DEBUG : Process uptime: 0s

06-07 01:53:32.466 12027 12027 F DEBUG : Cmdline: mediaserver64

06-07 01:53:32.466 12027 12027 F DEBUG : pid: 1139, tid: 11981, name: NPDecoder >>> mediaserver64 <<<

06-07 01:53:32.466 12027 12027 F DEBUG : uid: 1013

06-07 01:53:32.466 12027 12027 F DEBUG : tagged_addr_ctrl: 0000000000000001

06-07 01:53:32.466 12027 12027 F DEBUG : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

06-07 01:53:32.466 12027 12027 F DEBUG : x0 79748c5e568e2ddc x1 0000007ca13c3618 x2 0000000000000000 x3 0000007ca1291000

06-07 01:53:32.466 12027 12027 F DEBUG : x4 0000000001909705 x5 0000000000000000 x6 0000007c02d88808 x7 b60625655bf0252f

06-07 01:53:32.467 12027 12027 F DEBUG : x8 0000000000000080 x9 0000007ca126fed7 x10 0000000000000006 x11 0000007bfd0a81fc

06-07 01:53:32.467 12027 12027 F DEBUG : x12 9ef8a95ca9649dbe x13 e44782d5ac38720e x14 0000007bfd0a8030 x15 0000001e56307b5c

06-07 01:53:32.467 12027 12027 F DEBUG : x16 0000007c95dfdb70 x17 0000007c9844f118 x18 0000007bfaa28000 x19 b400007c13c246d0

06-07 01:53:32.467 12027 12027 F DEBUG : x20 0000007c02d88730 x21 b400007c13c67c00 x22 0000000000000415 x23 0000007c02d89000

06-07 01:53:32.467 12027 12027 F DEBUG : x24 0000000000000002 x25 b400007c13c246d0 x26 b400007c13c67c00 x27 0000007c02d89000

06-07 01:53:32.467 12027 12027 F DEBUG : x28 0000007ca13c2c28 x29 0000007c02d886f0

06-07 01:53:32.467 12027 12027 F DEBUG : lr 0000007c02d886f0 sp 0000007c02d886d0 pc 0000007c02d886f0 pst 0000000080001000

06-07 01:53:32.467 12027 12027 F DEBUG : backtrace:

06-07 01:53:32.467 12027 12027 F DEBUG : #00 pc 00000000000f86f0 [anon:stack_and_tls:11981]

When only the log stack cannot provide a more detailed analysis, we also need part of the memory information of the program and the register information. Android’s error mechanism will correspondingly generate a tombstone file saved to /data/tombstones/tombstone_xx. For machines without root permissions, the tombstone file can be captured through adb bugreport.

Understanding Native Crashes in Android Development

3

Tombstone

The tombstone file saves information about the error program’s architecture, commonly arm, arm64, the time of occurrence, program name, error type, the process ID and thread ID of the erroneous program, the register information at the error site, the stack and part of the memory information near the register addresses, the program memory mapping table /proc/self/maps, FD information, and the logs output by the program at the time of the error.

ABI: ‘arm64’ 【 arm64 program 】

Timestamp: 2022-06-07 01:53:32.033409857+0800 【 timestamp of error occurrence 】

Process uptime: 0s

Cmdline: mediaserver64 【 program name 】

pid: 1139, tid: 11981, name: NPDecoder >>> mediaserver64 <<< 【 process ID, thread ID 】

uid: 1013

Error Type

signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

【 Error type is SIGSEGV, subclass is SEGV_ACCERR, error address 0x7c02d886f0 】

SIGSEGV is also the most common type of Native Crash, most of the time we refer to it as a segmentation fault, which means that a segment fault occurred at PC=0x7c02d886f0 due to access denial.

Register Information

x0 79748c5e568e2ddc x1 0000007ca13c3618 x2 0000000000000000 x3 0000007ca1291000

x4 0000000001909705 x5 0000000000000000 x6 0000007c02d88808 x7 b60625655bf0252f

x8 0000000000000080 x9 0000007ca126fed7 x10 0000000000000006 x11 0000007bfd0a81fc

x12 9ef8a95ca9649dbe x13 e44782d5ac38720e x14 0000007bfd0a8030 x15 0000001e56307b5c

x16 0000007c95dfdb70 x17 0000007c9844f118 x18 0000007bfaa28000 x19 b400007c13c246d0

x20 0000007c02d88730 x21 b400007c13c67c00 x22 0000000000000415 x23 0000007c02d89000

x24 0000000000000002 x25 b400007c13c246d0 x26 b400007c13c67c00 x27 0000007c02d89000

x28 0000007ca13c2c28 x29 0000007c02d886f0

lr 0000007c02d886f0 sp 0000007c02d886d0 pc 0000007c02d886f0 pst 0000000080001000

Stack Information

backtrace:

#00 pc 00000000000f86f0 [anon:stack_and_tls:11981]

Memory Information

The tombstone will record memory information near the current valid address of the register, with a size of 0x100. This can be modified in the macro definition MEMORY_BYTES_TO_DUMP in

system/core/debuggerd/libdebuggerd/utility.cpp

In cases like this, the memory information of the stack can help us recover the call stack.

memory near x1 (/system/lib64/libstagefright.so):

0000007ca13c35f0 0000000000000000 0000000000000000 …………….

0000007ca13c3600 0000000000000000 0000000000000000 …………….

0000007ca13c3610 0000000000000000 0000007ca132326c ……..l22.|…

0000007ca13c3620 0000007ca1324008 0000007ca10552e4 .@2.|….R..|…

0000007ca13c3630 0000007ca10552e8 0000007ca10552ec .R..|….R..|…

0000007ca13c3640 0000007ca10552f4 0000007ca132402c .R..|…,@2.|…

0000007ca13c3650 0000000000000000 0000000000000000 …………….

0000007ca13c3660 0000000000000000 0000000000000000 …………….

0000007ca13c3670 0000000000000000 0000007ca134ea84 ……….4.|…

0000007ca13c3680 0000007ca134ecec 0000007ca10552e4 ..4.|….R..|…

0000007ca13c3690 0000007ca10552e8 0000007ca10552ec .R..|….R..|…

0000007ca13c36a0 0000007ca10552f4 0000007ca134ed10 .R..|…..4.|…

0000007ca13c36b0 0000000000000000 0000000000000000 …………….

0000007ca13c36c0 0000000000000000 0000000000000000 …………….

0000007ca13c36d0 0000000000000000 0000007ca135d02c ……..,.5.|…

0000007ca13c36e0 0000007ca135d4b8 0000007ca10552e4 ..5.|….R..|…

memory near x29 ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|……….. 【SP = 0x0000007c02d886d0】

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|… 【x29 = 0x0000007c02d886f0】

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

memory near sp ([anon:stack_and_tls:11981]):

0000007c02d886b0 0000000000000018 0000007caae98c58 ……..X…|…

0000007c02d886c0 0000007c02d886f0 0000007c95de6754 ….|…Tg..|…

0000007c02d886c0: 0x0000007c02d886f0 0x0000007c95de6754

0000007c02d886d0: 0xb400007c13c246d0 0x0000000001909705 /SP

0000007c02d886e0: 0x0000007c02d88700 0x6f2ab3b40fa2f8ef

0x7c02d886f0: 0x0000007c02d88750 0x0000007ca133f8e0

0x7c02d88700: 0x0000000000000002 0x0000000000000000

0x7c02d88710: 0x0000000000000415 0x0000000001909705

0x7c02d88720: 0x0000000000000000 0x0000007c02d88808

0x7c02d88730: 0xb400007c13c67c00 0x0000000000000000

0x7c02d88740: 0x0000007c02d89000 0x6f2ab3b40fa2f8ef

0x7c02d88750: 0x0000007c02d88830 0x0000007ca796ee7c

0x7c02d88760: 0x0000007ca79f3dd8 0x0000007ca79edb80

0x7c02d88770: 0x0000007c02d89000 0xb400007c13c04680

0x7c02d88780: 0x0000000000000000 0x0000000000000002

0x7c02d88790: 0xb400007c13c67c00 0x0000000000000000

0x7c02d887a0: 0x0000000000000000 0xb400007c13c7c100

memory near pc ([anon:stack_and_tls:11981]):

0x0000007c02d886d0 0x0000000000000018 0x0000007caae98c58 ……..X…|…

0x0000007c02d886e0 0x0000007c02d886f0 0x0000007c95de6754 ….|…Tg..|…

0x0000007c02d886f0 0x0000007c02d88750 0x0000007ca133f8e0 P…|…..3.|…

0x0000007c02d88700 0x0000000000000002 0x0000000000000000 …………….

0x0000007c02d88710 0x0000000000000415 0x0000000001909705 …………….

0x0000007c02d88720 0x0000000000000000 0x0000007c02d88808 …………|…

0x0000007c02d88730 0xb400007c13c67c00 0x0000000000000000 .|..|………..

0x0000007c02d88740 0x0000007c02d89000 0x6f2ab3b40fa2f8ef ….|………o

0x0000007c02d88750 0x0000007c02d88830 0x0000007ca796ee7c 0…|…|…|…

0x0000007c02d88760 0x0000007ca79f3dd8 0x0000007ca79edb80 .=..|…….|…

0x0000007c02d88770 0x0000007c02d89000 0xb400007c13c04680 ….|….F..|…

0x0000007c02d88780 0x0000000000000000 0x0000000000000002 …………….

0x0000007c02d88790 0xb400007c13c67c00 0x0000000000000000 .|..|………..

0x0000007c02d887a0 0x0000000000000000 0xb400007c13c7c100 …………|…

Memory Mapping Table

memory map (1146 entries):

0000005f’fabc7000-0000005f’fabc7fff r– 0 1000 /system/bin/mediaserver64

0000005f’fabc8000-0000005f’fabc9fff r-x 1000 2000 /system/bin/mediaserver64

0000005f’fabca000-0000005f’fabcafff r– 3000 1000 /system/bin/mediaserver64

0000007b’e79a3000-0000007b’e7d93fff — 0 3f1000

0000007c’a120e000-0000007c’a128efff r– 0 81000 /system/lib64/libstagefright.so

0000007c’a128f000-0000007c’a13c0fff r-x 81000 132000 /system/lib64/libstagefright.so

0000007c’a13c1000-0000007c’a13cffff r– 1b3000 f000 /system/lib64/libstagefright.so

0000007c’a13d0000-0000007c’a13d1fff rw- 1c1000 2000 /system/lib64/libstagefright.so

0000007c’a787c000-0000007c’a78f4fff r– 0 79000 /system/lib64/libmediaplayerservice.so

0000007c’a78f5000-0000007c’a79ecfff r-x 79000 f8000 /system/lib64/libmediaplayerservice.so

0000007c’a79ed000-0000007c’a79f8fff r– 171000 c000 /system/lib64/libmediaplayerservice.so

0000007c’a79f9000-0000007c’a79f9fff rw- 17c000 1000 /system/lib64/libmediaplayerservice.so

FD Information

open files:

fd 0: /dev/null (unowned)

fd 1: /dev/null (unowned)

fd 2: /dev/null (unowned)

fd 3: socket:[62562] (unowned)

fd 4: /dev/binderfs/binder (unowned)

fd 5: /dev/binderfs/hwbinder (unowned)

fd 6: /sys/kernel/tracing/trace_marker (unowned)

fd 7: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

fd 8: /dmabuf: (unowned)

fd 9: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

fd 10: /storage/emulated/0/zapya/folder/华语音乐/IN-K&王忻辰&苏星婕 – 落日与晚风.mp3 (owned by unique_fd 0x7c13c7a498)

fd 11: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

4

Coredump

The previous tombstone file content shows that its information is very limited. When we need more memory information, coredump becomes particularly important. It can capture the corresponding memory information as configured. For more information about core, see:

https://man7.org/linux/man-pages/man5/core.5.html

AOSP Method

# build/envsetup.sh# coredump_setup - enable core dumps globally for any process that has the core-file-size limit set correctly# NOTE: You must call also coredump_enable for a specific process if its core-file-size limit is not set already.# NOTE: Core dumps are written to ramdisk; they will not survive a reboot!function coredump_setup(){echo "Getting root...";adb root;adb wait-for-device;echo "Remounting root partition read-write...";adb shell mount -w -o remount -t rootfs rootfs;sleep 1;adb wait-for-device;adb shell mkdir -p /cores;adb shell mount -t tmpfs tmpfs /cores;adb shell chmod 0777 /cores;echo "Granting SELinux permission to dump in /cores...";adb shell restorecon -R /cores;echo "Set core pattern.";adb shell 'echo /cores/core.%p &gt; /proc/sys/kernel/core_pattern';echo "Done."}# coredump_enable - enable core dumps for the specified process# $1 = PID of process (e.g., $(pid mediaserver))# NOTE: coredump_setup must have been called as well for a core#       dump to actually be generated.function coredump_enable(){local PID=$1;if [ -z "$PID" ]; thenprintf "Expecting a PID!\n";return;fi;echo "Setting core limit for $PID to infinite...";adb shell /system/bin/ulimit -P $PID -c unlimited}

Common Methods

Configure coredump parameters for system_server. Since the directory where the target process’s coredump is generated is restricted by SELinux permissions, this method of configuring coredump needs to pay attention to which directories the target process has read and write SELinux permissions, and then configure the corresponding directories.

adb wait-for-deviceadb rootadb shell mkdir /data/coresadb shell chmod 777 /data/cores#adb shell setenforce 0adb shell restorecon -R /data/coresadb shell 'echo /data/cores/core.%e.%p &gt; /proc/sys/kernel/core_pattern'adb shell 'system/bin/ulimit -P `pidof system_server` -c unlimited'#adb shell 'echo 2 &gt; /proc/sys/fs/suid_dumpable'

Note: Ensure that the issue is unrelated to SELinux permissions. You can disable SELinux permissions by using adb shell setenforce 0.

Configure parameters to capture coredump for com.android.settings. Since the previous configuration restores SELinux permissions for the /data/cores directory as follows:

drwxrwxrwx 2 root root u:object_r:system_data_file:s0 3452 2022-07-04 15:08 cores

We know that the app must have permission to read and write files in its own /data/data/$PACKAGE/ directory, so we can configure it as follows:

adb wait-for-deviceadb rootadb shell mkdir /data/data/com.android.settings/coresadb shell chmod 777 /data/data/com.android.settings/coresadb shell restorecon -R /data/data/com.android.settings/coresadb shell 'echo /data/data/com.android.settings/cores/core.%e.%p &gt; /proc/sys/kernel/core_pattern'adb shell 'system/bin/ulimit -P `pidof com.android.settings` -c unlimited'#adb shell 'echo 2 &gt; /proc/sys/fs/suid_dumpable'

When we verify this app on the machine by simulating kill -11

$ kill -11 `pidof com.android.settings`

$ ls /data/data/com.android.settings/cores/core.ndroid.settings.27946

Parameter Description

coredump_filter process default value is 0x23, only captures: private anonymous/shared anonymous/private large pages. To capture all memory information, set adb shell ‘echo 0x27 > /proc/$PID/coredump_filter’.

/proc/$PID/coredump_filter

bit0: private anonymous

bit1: shared anonymous

bit2: private mapping with underlying file

bit3: shared mapping with underlying file

bit4: ELF header

bit5: private large pages

bit6: shared large pages

core_pattern controls the filename of the generated core and the output location of the core. For example:

adb shell ‘echo /data/cores/core.%p > /proc/sys/kernel/core_pattern’

/proc/sys/kernel/core_pattern

%p: add pid

%u: add current uid

%g: add current gid

%s: add the signal that caused the core

%t: add the unix time when the core file was generated

%h: add hostname

%e: add command name

%E: executable file path name, replaces slashes (’/’) with exclamation marks (’!’).

When the program calls seteuid()/setegid() to change the effective user or group of the process, the system will not generate a core for these processes by default. At this time, you may need to adjust the suid_dumpable parameter to enter debug mode or safe mode.

/proc/sys/fs/suid_dumpable

0: default mode

1: debug mode

2: safe mode

File Format

Core files are also a type of ELF file, so its main format components are the same as those of ELF files.

Taking the core file explained in the case as an example, its main components are the VMA in /proc/self/maps and the registers of each thread. The register information is stored in PT_NOTE, and each VMA is stored in PT_LOAD. When the VMA is filtered out, it only has Program Header descriptions and no corresponding segment.

Understanding Native Crashes in Android Development

Understanding Native Crashes in Android Development

Offline Debugging

Note: The MINIDUMP of the MTK platform is also a type of coredump, and it saves limited memory information. Core analysis can use debugging tools like GDB, lldb, etc. How to use these debugging tools will not be introduced here one by one.

$ ~/work/debug/gdb_arm64/gdb-12.1/output/bin/aarch64-linux-gdb
When we do not have a symbol table, loading the core-file is also possible.
(gdb) core-file PROCESS_MINIDUMP
When we have the corresponding symbol table, we can load the symbol table directory
(gdb) set solib-search-path symbols/
(gdb) set sysroot symbols/
(gdb) info sharedlibrary Displays the address range of all shared libraries
(gdb) info registers Displays the current frame register information of the current thread
(gdb) info locals Displays the local variables of the current frame
(gdb) info thread Displays which threads exist
(gdb) thread 2 Switch to thread 2
(gdb) bt Displays the stack of the current thread
(gdb) thread apply all [command] For example, print the stack of all threads
(gdb) thread apply all bt Let all threads execute the same command
(gdb) frame Displays the current frame information
(gdb) frame 3 Switch to frame #3
(gdb) print or (gdb) p Prints the variable
(gdb) ptype ‘android::AHandler’ Views the data structure of a certain class or struct
(gdb) ptype /o ‘android::AHandler’ Views how many bytes the data type occupies
(gdb) set print pretty on Formats output
(gdb) set log on Saves the results of gdb output
(gdb) x /gx 0x7c02d886f0 Reads the memory content at address 0x7c02d886f0, where the output format is as follows:
o(octal), x(hex), d(decimal),
u(unsigned decimal), t(binary),
f(float), a(address), i(instruction),
c(char), s(string),
z(hex, zero padded on the left).
(gdb) disassemble 0x0000007c95de6708 or (gdb) disassemble ‘android::AMessage::setTarget’
Displays the function assembly information
...

Memory Detection Mechanism

ASAN

After Android 11, AOSP master has abandoned the platform development ASAN on arm64 and switched to HWASAN. AddressSanitizer (ASAN) is a compiler-based fast detection tool used to detect memory errors.

Stack and heap buffer overflow/underflow

Stack and heap buffer overflow/underflow

Heap use after free

Using freed memory

Stack use outside scope

Out of stack range

Double free/wild free

Freeing memory multiple times/wrongly freeing

HWASAN

HWASan is only applicable to Android 10 and higher versions and can only be used on AArch64 hardware, with the same detection capability as ASAN. Support for tagged-pointers is required in Linux-4.14 and above.

When compiling the Android version, include the following environment variable:

$ export SANITIZE_TARGET=hwaddress

To skip a certain module, add the following content in the Android.bp file under the corresponding module:

sanitize: {

hwaddress: false,

address: false,

},

In Android.mk, add the following content:

LOCAL_NOSANITIZE := hwaddress

APP build supports HWASAN, then add the following content in Application.mk:

APP_STL := c++_shared

APP_CFLAGS := -fsanitize=hwaddress -fno-omit-frame-pointer

APP_LDFLAGS := -fsanitize=hwaddress

MTE

The ARM Memory Tagging Extension (MTE) introduced in the latest Android S operates similarly to HWASAN. The biggest difference is that HWASAN requires recompilation and instrumentation of the corresponding detection functions before all memory accesses, while MTE performs detection entirely supported by hardware.

For more content, refer to the Juejin expert – Lu Banshan:

https://juejin.cn/post/6844904111570157575

https://juejin.cn/post/7013595058125406238

6

The Dangers of Wild Pointers

When the object pointed to is released or reclaimed, but the pointer is not modified, causing the pointer to still point to the already reclaimed memory address, this pointer is called a Wild pointer. If the memory pointed to by this wild pointer is allocated to other pointers, and this wild pointer is still in use, the program will be difficult to predict.

#include &lt;stdio.h&gt;class A {public:virtual ~A() = default;virtual void foo() {printf("A:%ld\n", a);}long a;};class B {public:virtual ~B() = default;virtual void foo() {printf("B:%ld\n", b);}long b;};int main(int /*argc*/, char** /*argv[]*/) {A *a = new A();A *a_bak = a;a-&gt;a = 1000L;printf("A ptr = %p\n", a);delete a;   //  At this point, the pointer a has been freed, so the pointer a_bak is a wild pointerB *b = new B();printf("B ptr = %p\n", b);b-&gt;b = 2000L;b-&gt;foo();   //  What will happen here?delete b;return 0;}

What will the above program output? Since B and A have the same data structure size and run in the same thread, it is highly likely that the same pointer address will be allocated just released. Therefore, this program will output that pointer b and pointer a_bak are the same.

# ./data/Tester64

A ptr = 0xb400007690205010

B ptr = 0xb400007690205010

B:2000

B:2000

Like the above result, if the program is rewritten below, what will happen?

#include &lt;stdio.h&gt;class A {public:long bad;long a;};class B {public:virtual ~B() = default;virtual void foo() {b = 2000L;printf("B:%ld\n", b);}long b;};int main(int /*argc*/, char** /*argv[]*/) {A *a = new A();A *a_bak = a;printf("A ptr = %p\n", a);delete a;B *b = new B();printf("B ptr = %p\n", b);a_bak-&gt;bad = 0x20L;b-&gt;foo();   //  This will cause a segmentation fault when the program runs. delete b;return 0;}

The above program will cause a segmentation fault at line 27, as bad will corrupt the virtual function table of B, causing lines 28 and 29 to search for the addresses of the foo function and destructor function in the virtual function table, resulting in a segmentation fault.

Timestamp: 2022-07-06 14:47:50.925654058+0800

Process uptime: 0s

Cmdline: ./data/Tester64

pid: 12652, tid: 12652, name: Tester64 >>> ./data/Tester64 <<<

uid: 0

tagged_addr_ctrl: 0000000000000001

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x28

Cause: null pointer dereference

x0 b40000762f005010 x1 b40000762f01b000 x2 0000000000000007 x3 ffffffffffffffff

x4 ffffffffffffffff x5 0000000040100401 x6 b40000762f01b006 x7 3637303030303462

x8 0000000000000020 x9 a454ef76eb4317d3 x10 0000000000004001 x11 0000000000000000

x12 0000000000000000 x13 0000000000000002 x14 0000000000000010 x15 0000000000000010

x16 000000762f5a0c58 x17 000000762f5910d4 x18 000000763745c000 x19 b40000762f005010

x20 b40000762f005010 x21 0000007fe173d378 x22 0000000000000001 x23 0000000000000000

x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000

x28 0000000000000000 x29 0000007fe173d2e0

lr 0000005ac14600c8 sp 0000007fe173d2e0 pc 0000005ac14600d0 pst 0000000060001000

backtrace:

#00 pc 00000000000010d0 /data/Tester64 (main+124)

#01 pc 000000000008436c /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+100)

The program will error, which is good. If the program does not error and continues to run, then this program will become very scary because you do not know how the program will run. Like the program below, rewriting control will lead the program to run in other directions.

#include &lt;stdio.h&gt;class A {public:void *bad;long a;};class B {public:virtual ~B() {printf("delete B\n");};virtual void foo() {b = 2000L;printf("B:%ld\n", b);}long b;};void func1() {printf("Hello !!\n");}void func2() {printf("GoGoGo !!\n");}int main(int /*argc*/, char** /*argv[]*/) {A *a = new A();A *a_bak = a;printf("A ptr = %p\n", a);delete a;B *b = new B();printf("B ptr = %p\n", b);long *data = new long[4] {0x0L, (long)func2, (long)func1, 0x0L};a_bak-&gt;bad = data;printf("Test .. \n");b-&gt;foo();delete b;printf("Done.\n");return 0;}

# ./data/Tester64

A ptr = 0xb400007ce9605010

B ptr = 0xb400007ce9605010

Test ..

Hello !!

GoGoGo !!

Done.

7

The Dangers of Array Out of Bounds

Compared to the previous wild pointers, array out of bounds can often be detected by memory detectors such as HWASAN. Of course, wild pointer situations can also be detected. Array out of bounds often leads to a situation where the memory of a certain object’s front half is polluted while the back half of the data remains normal.

#include &lt;stdio.h&gt;class A {public:long a = 0x55AA;long b = 0xDEAD;};int main(int /*argc*/, char** /*argv[]*/) {long *b = new long[2] {0x0L, 0x1L};A *a = new A();printf("A:%p\n", a);printf("B:%p\n", b);b[2] = 0xDEAD;printf("B2:%p\n", &amp;b[2]);printf("0x%lx-0x%lx\n", a-&gt;a, a-&gt;b);return 0;}

Because the data size of b is the same as that of object a, when the program starts and allocates pointer addresses, they are likely to be linked together. Therefore, the out-of-bounds behavior of b[2] will corrupt the contents of object a, often after the program runs for a long time, increasing memory fragmentation, making it unknown which object’s memory will be corrupted by the out-of-bounds operation of b[2].

# ./data/Tester64

A:0xb400007fa7c05020

B:0xb400007fa7c05010

B2:0xb400007fa7c05020

0xdead-0xdead

8

Machine Code Translation

The tombstone file in this article’s case shows that the PC ran to a location not in the text segment address. Here, let’s change to a tombstone file to facilitate explanation. We can generate the corresponding ELF file by compiling it, and then use objdump to obtain the corresponding assembly.

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x40

Cause: null pointer dereference

x0 0000000000000020 x1 b400007bf95f0e00 x2 63692f6863726165 x3 0000000000000008

x4 b400007bf95f0e74 x5 b400007bf8ea36f4 x6 656863732f676e69 x7 732f7269645f616d

x8 db4cf552ad46f717 x9 0000000000000001 x10 0000000000004001 x11 0000000000000000

x12 0000000000000000 x13 0000000000000002 x14 0000000000000010 x15 0000000000000010

x16 000000762f5a0c58 x17 000000762f5910d4 x18 000000763745c000 x19 0000000000000020

x20 0000000000000000 x21 0000007b548e9000 x22 0000007b548e9000 x23 b400007bf946ffd0

x24 0000007b548e76c0 x25 0000007b548e9000 x26 0000007b548e7b30 x27 0000007b548e7b18

x28 0000007b548e7a10 x29 0000007b548e7410

lr 0000007a8e4e7980 sp 0000007b548e73e0 pc 0000007a8e4d0e00 pst 0000000060001000

memory near pc (/apex/com.android.appsearch/lib64/libicing.so):

0000007a8e4d0de0 a90557f6f90023f7 9100c3fda9064ff4 .#…W…O……

0000007a8e4d0df0 f94016c8d53bd056 f81f83a8aa0003f3 V.;…@………

0000007a8e4d0e00 350001c839408008 91181c42b0fffde2 ..@9…5….B…

0000007a8e4d0e10 2a1f03e1910023e0 910023f452808ee3 .#…..…R.#..

0000007a8e4d0e20 910022809400d695 912b742190fffde1 …..

Leave a Comment