Commit d31afe7d authored by husky@chromium.org's avatar husky@chromium.org

Fix segfault in JNIEnv function hooks for test

On newer JB builds, the JNINativeInterface struct appears to be held
in read-only memory so attempting to repoint individual function pointers
in it causes segfault. So instead we repoint the top-level struct pointer
to refer to our own copy held in read/write memory.

BUG=137348
TEST=JNIAndroidTest


Review URL: https://chromiumcodereview.appspot.com/10828050

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148943 0039d316-1c4b-4281-b951-d872f2087c98
parent cd117799
// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/at_exit.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace base { namespace base {
...@@ -21,14 +22,14 @@ const char* g_last_method; ...@@ -21,14 +22,14 @@ const char* g_last_method;
const char* g_last_jni_signature; const char* g_last_jni_signature;
jmethodID g_last_method_id; jmethodID g_last_method_id;
JNINativeInterface g_previous_functions = {0}; const JNINativeInterface* g_previous_functions;
jmethodID GetMethodIDWrapper(JNIEnv* env, jclass clazz, const char* method, jmethodID GetMethodIDWrapper(JNIEnv* env, jclass clazz, const char* method,
const char* jni_signature) { const char* jni_signature) {
g_last_method = method; g_last_method = method;
g_last_jni_signature = jni_signature; g_last_jni_signature = jni_signature;
g_last_method_id = g_previous_functions.GetMethodID(env, clazz, method, g_last_method_id = g_previous_functions->GetMethodID(env, clazz, method,
jni_signature); jni_signature);
return g_last_method_id; return g_last_method_id;
} }
...@@ -38,15 +39,16 @@ class JNIAndroidTest : public testing::Test { ...@@ -38,15 +39,16 @@ class JNIAndroidTest : public testing::Test {
protected: protected:
virtual void SetUp() { virtual void SetUp() {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
g_previous_functions = *env->functions; g_previous_functions = env->functions;
JNINativeInterface* native_interface = hooked_functions = *g_previous_functions;
const_cast<JNINativeInterface*>(env->functions); env->functions = &hooked_functions;
native_interface->GetMethodID = &GetMethodIDWrapper; hooked_functions.GetMethodID = &GetMethodIDWrapper;
} }
virtual void TearDown() { virtual void TearDown() {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
*(const_cast<JNINativeInterface*>(env->functions)) = g_previous_functions; env->functions = g_previous_functions;
Reset();
} }
void Reset() { void Reset() {
...@@ -54,6 +56,12 @@ class JNIAndroidTest : public testing::Test { ...@@ -54,6 +56,12 @@ class JNIAndroidTest : public testing::Test {
g_last_jni_signature = 0; g_last_jni_signature = 0;
g_last_method_id = NULL; g_last_method_id = NULL;
} }
// Needed to cleanup the cached method map in the implementation between
// runs (e.g. if using --gtest_repeat)
base::ShadowingAtExitManager exit_manager;
// From JellyBean release, the instance of this struct provided in JNIEnv is
// read-only, so we deep copy it to allow individual functions to be hooked.
JNINativeInterface hooked_functions;
}; };
TEST_F(JNIAndroidTest, GetMethodIDFromClassNameCaching) { TEST_F(JNIAndroidTest, GetMethodIDFromClassNameCaching) {
......
...@@ -15,26 +15,26 @@ namespace { ...@@ -15,26 +15,26 @@ namespace {
int g_local_refs = 0; int g_local_refs = 0;
int g_global_refs = 0; int g_global_refs = 0;
JNINativeInterface g_previous_functions = {0}; const JNINativeInterface* g_previous_functions;
jobject NewGlobalRef(JNIEnv* env, jobject obj) { jobject NewGlobalRef(JNIEnv* env, jobject obj) {
++g_global_refs; ++g_global_refs;
return g_previous_functions.NewGlobalRef(env, obj); return g_previous_functions->NewGlobalRef(env, obj);
} }
void DeleteGlobalRef(JNIEnv* env, jobject obj) { void DeleteGlobalRef(JNIEnv* env, jobject obj) {
--g_global_refs; --g_global_refs;
return g_previous_functions.DeleteGlobalRef(env, obj); return g_previous_functions->DeleteGlobalRef(env, obj);
} }
jobject NewLocalRef(JNIEnv* env, jobject obj) { jobject NewLocalRef(JNIEnv* env, jobject obj) {
++g_local_refs; ++g_local_refs;
return g_previous_functions.NewLocalRef(env, obj); return g_previous_functions->NewLocalRef(env, obj);
} }
void DeleteLocalRef(JNIEnv* env, jobject obj) { void DeleteLocalRef(JNIEnv* env, jobject obj) {
--g_local_refs; --g_local_refs;
return g_previous_functions.DeleteLocalRef(env, obj); return g_previous_functions->DeleteLocalRef(env, obj);
} }
} // namespace } // namespace
...@@ -44,21 +44,24 @@ class ScopedJavaRefTest : public testing::Test { ...@@ -44,21 +44,24 @@ class ScopedJavaRefTest : public testing::Test {
g_local_refs = 0; g_local_refs = 0;
g_global_refs = 0; g_global_refs = 0;
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
g_previous_functions = *env->functions; g_previous_functions = env->functions;
hooked_functions = *g_previous_functions;
env->functions = &hooked_functions;
// We inject our own functions in JNINativeInterface so we can keep track // We inject our own functions in JNINativeInterface so we can keep track
// of the reference counting ourselves. // of the reference counting ourselves.
JNINativeInterface* native_interface = hooked_functions.NewGlobalRef = &NewGlobalRef;
const_cast<JNINativeInterface*>(env->functions); hooked_functions.DeleteGlobalRef = &DeleteGlobalRef;
native_interface->NewGlobalRef = &NewGlobalRef; hooked_functions.NewLocalRef = &NewLocalRef;
native_interface->DeleteGlobalRef = &DeleteGlobalRef; hooked_functions.DeleteLocalRef = &DeleteLocalRef;
native_interface->NewLocalRef = &NewLocalRef;
native_interface->DeleteLocalRef = &DeleteLocalRef;
} }
virtual void TearDown() { virtual void TearDown() {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
*(const_cast<JNINativeInterface*>(env->functions)) = g_previous_functions; env->functions = g_previous_functions;
} }
// From JellyBean release, the instance of this struct provided in JNIEnv is
// read-only, so we deep copy it to allow individual functions to be hooked.
JNINativeInterface hooked_functions;
}; };
// The main purpose of this is testing the various conversions compile. // The main purpose of this is testing the various conversions compile.
......
...@@ -24,9 +24,6 @@ FileUtilProxyTest.Touch ...@@ -24,9 +24,6 @@ FileUtilProxyTest.Touch
ProcessUtilTest.FDRemapping ProcessUtilTest.FDRemapping
# Flaky? # Flaky?
ScopedJavaRefTest.RefCounts ScopedJavaRefTest.RefCounts
# http://crbug.com/137348
JNIAndroidTest.GetMethodIDFromClassNameCaching
ScopedJavaRefTest.Conversions
# Death tests are not supported with apks. # Death tests are not supported with apks.
*DeathTest* *DeathTest*
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment