Created
April 4, 2020 07:11
-
-
Save rprichard/04c522bc811956d05b4cf458243fb3d9 to your computer and use it in GitHub Desktop.
Expose disjoint mapping unwinder bug with older Android devices
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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