Skip to content

Instantly share code, notes, and snippets.

@rprichard
Created April 4, 2020 07:11
Show Gist options
  • Save rprichard/04c522bc811956d05b4cf458243fb3d9 to your computer and use it in GitHub Desktop.
Save rprichard/04c522bc811956d05b4cf458243fb3d9 to your computer and use it in GitHub Desktop.
Expose disjoint mapping unwinder bug with older Android devices
#!/bin/bash -e
#
# On an older device (Galaxy Nexus S running Android 4.1.2), an executable built
# with -z,max-page-size=0x10000 has a gap of size 0x1e000 between its R+E and
# R+W segments. If a DSO is loaded into the gap, then that DSO may hit a bug in
# old versions of Android where dl_unwind_find_exidx searches the executable
# rather than the DSO.
#
# This test program runs 10 times and typically fails a few times, depending on
# whether ASLR puts a DSO into the gap. Failure looks like so:
#
# terminating with uncaught exception of type int
# assertion "terminating with uncaught exception of type int" failed: file "/usr/local/google/buildbot/src/android/ndk-release-r21/external/libcxx/../../external/libcxxabi/src/abort_message.cpp", line 72, function "abort_message"
# Segmentation fault
#
# If both the executable *and* DSO are built with a 64KB max-page-size, then the
# DSO probably won't fit into the gap. Newer devices seem(?) to lay out memory
# differently, so that a DSO won't accidentally land in the gap.
#
# The Bionic bug was fixed in Android Q and up:
# https://android-review.googlesource.com/c/platform/bionic/+/916470/
NDK=/x/android-ndk-r21
MAX_PAGE_SIZE=-Wl,-z,max-page-size=0x10000
CXX=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi16-clang++
LIBCXX=$NDK/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so
cat >main.cpp <<EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/mman.h>
int somevar = 42;
void func1(); void func2(); void func3();
void func4(); void func5(); void func6();
int main() {
func1(); func2(); func3();
func4(); func5(); func6();
if (false) {
char cmd[256];
snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", (int)getpid());
system(cmd);
}
return 0;
}
EOF
cat >mylib.cpp <<EOF
#include <stdio.h>
void FUNC() {
try {
throw 42;
} catch (int i) {
printf("%s: caught %d\n", __FUNCTION__, i);
}
}
EOF
$CXX mylib.cpp -fpic -shared -o libmylib1.so -DFUNC=func1
$CXX mylib.cpp -fpic -shared -o libmylib2.so -DFUNC=func2
$CXX mylib.cpp -fpic -shared -o libmylib3.so -DFUNC=func3
$CXX mylib.cpp -fpic -shared -o libmylib4.so -DFUNC=func4
$CXX mylib.cpp -fpic -shared -o libmylib5.so -DFUNC=func5
$CXX mylib.cpp -fpic -shared -o libmylib6.so -DFUNC=func6
$CXX main.cpp $MAX_PAGE_SIZE -o main \
libmylib1.so \
libmylib2.so \
libmylib3.so \
libmylib4.so \
libmylib5.so \
libmylib6.so
REMOTE_DIR=/data/local/tmp/gaptest
adb shell rm -fr $REMOTE_DIR
adb shell mkdir -p $REMOTE_DIR
adb push \
main \
libmylib1.so \
libmylib2.so \
libmylib3.so \
libmylib4.so \
libmylib5.so \
libmylib6.so \
$LIBCXX \
$REMOTE_DIR
adb shell "for i in 1 2 3 4 5 6 7 8 9 10; do LD_LIBRARY_PATH=$REMOTE_DIR $REMOTE_DIR/main; done"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment