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