diff options
author | 2020-07-14 10:38:06 -0700 | |
---|---|---|
committer | 2020-07-16 08:10:58 +0000 | |
commit | 32846611b22619e8823e69daa91ac078dafce62a (patch) | |
tree | 370e76400b197546a6d6a16bf989c31f7db76b2b /test/1940-ddms-ext/ddm_ext.cc | |
parent | 7400a5466a04f9a274d262c5cb1fd35ff496839a (diff) |
Fix ddm.publish_chunk extension to prevent deadlock
The ddm.publish_chunk jvmti extension event could could cause
deadlocks with the GC. This is not really fixable so instead remove
the JNIenv from the event function to prevent the dangerous actions
from being performed. Since this is an internal API that isn't really
used this should be safe enough. Rename the extension to
publish_chunk_safe so users can be notified of new behavior. Also fix
the test to stop relying on the buggy behavior.
Test: ./test.py
Bug: 161207646
Change-Id: Ib67d6607a8f359167069bc65cf5e3adaa70f0fc5
Diffstat (limited to 'test/1940-ddms-ext/ddm_ext.cc')
-rw-r--r-- | test/1940-ddms-ext/ddm_ext.cc | 77 |
1 files changed, 58 insertions, 19 deletions
diff --git a/test/1940-ddms-ext/ddm_ext.cc b/test/1940-ddms-ext/ddm_ext.cc index 452187bdcb..110ad64ac6 100644 --- a/test/1940-ddms-ext/ddm_ext.cc +++ b/test/1940-ddms-ext/ddm_ext.cc @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <queue> +#include <vector> + #include "jvmti.h" // Test infrastructure @@ -33,10 +36,16 @@ using DdmHandleChunk = jvmtiError(*)(jvmtiEnv* env, jint* len_data_out, jbyte** data_out); +struct DdmCallbackData { + public: + DdmCallbackData(jint type, jint size, jbyte* data) : type_(type), data_(data, data + size) {} + jint type_; + std::vector<jbyte> data_; +}; struct DdmsTrackingData { DdmHandleChunk send_ddm_chunk; - jclass test_klass; - jmethodID publish_method; + jrawMonitorID callback_mon; + std::queue<DdmCallbackData> callbacks_received; }; template <typename T> @@ -111,21 +120,52 @@ static void DeallocParams(jvmtiParamInfo* params, jint n_params) { } } -static void JNICALL PublishCB(jvmtiEnv* jvmti, JNIEnv* jnienv, jint type, jint size, jbyte* bytes) { +extern "C" JNIEXPORT void JNICALL Java_art_Test1940_publishListen(JNIEnv* env, + jclass test_klass, + jobject publish) { + jmethodID publish_method = env->FromReflectedMethod(publish); DdmsTrackingData* data = nullptr; - if (JvmtiErrorToException(jnienv, jvmti, - jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } - ScopedLocalRef<jbyteArray> res(jnienv, jnienv->NewByteArray(size)); - jnienv->SetByteArrayRegion(res.get(), 0, size, bytes); - jnienv->CallStaticVoidMethod(data->test_klass, data->publish_method, type, res.get()); + std::vector<DdmCallbackData> callbacks; + while (true) { + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(data->callback_mon))) { + return; + } + while (data->callbacks_received.empty()) { + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorWait(data->callback_mon, 0))) { + CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->RawMonitorExit(data->callback_mon)); + return; + } + } + while (!data->callbacks_received.empty()) { + callbacks.emplace_back(std::move(data->callbacks_received.front())); + data->callbacks_received.pop(); + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(data->callback_mon))) { + return; + } + for (auto cb : callbacks) { + ScopedLocalRef<jbyteArray> res(env, env->NewByteArray(cb.data_.size())); + env->SetByteArrayRegion(res.get(), 0, cb.data_.size(), cb.data_.data()); + env->CallStaticVoidMethod(test_klass, publish_method, cb.type_, res.get()); + } + callbacks.clear(); + } +} + +static void JNICALL PublishCB(jvmtiEnv* jvmti, jint type, jint size, jbyte* bytes) { + DdmsTrackingData* data = nullptr; + CHECK_EQ(JVMTI_ERROR_NONE, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data))); + CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorEnter(data->callback_mon)); + data->callbacks_received.emplace(type, size, bytes); + CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorNotifyAll(data->callback_mon)); + CHECK_EQ(JVMTI_ERROR_NONE, jvmti->RawMonitorExit(data->callback_mon)); } -extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, - jclass, - jclass method_klass, - jobject publish_method) { +extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, jclass) { void* old_data = nullptr; if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) { return; @@ -134,17 +174,16 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); return; } - DdmsTrackingData* data = nullptr; + void* mem = nullptr; if (JvmtiErrorToException(env, jvmti_env, jvmti_env->Allocate(sizeof(DdmsTrackingData), - reinterpret_cast<unsigned char**>(&data)))) { + reinterpret_cast<unsigned char**>(&mem)))) { return; } - memset(data, 0, sizeof(DdmsTrackingData)); - data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass)); - data->publish_method = env->FromReflectedMethod(publish_method); - if (env->ExceptionCheck()) { + DdmsTrackingData* data = new (mem) DdmsTrackingData{}; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->CreateRawMonitor("callback-mon", &data->callback_mon))) { return; } // Get the extensions. @@ -181,7 +220,7 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, } for (jint i = 0; i < n_ext; i++) { jvmtiExtensionEventInfo* cur_info = &events[i]; - if (strcmp("com.android.art.internal.ddm.publish_chunk", cur_info->id) == 0) { + if (strcmp("com.android.art.internal.ddm.publish_chunk_safe", cur_info->id) == 0) { found_event = true; event_index = cur_info->extension_event_index; } |