Revert^2 "Add AddToDexClassloader JVMTI extension functions"

This reverts commit 799e536da9733ab638946f56e1ceb62d62cd3c81.

It seems that on some of our test devices the kernel does not have an
implementation for memfd_create. To work around this I added a basic
wrapper that will simulate memfd_create using temp files. This should
be sufficient for testing. All actual devices are expected to support
the memfd_create syscall natively.

Reason for revert: Implemented fallback for memfd_create
Bug: 132699522
Bug: 132914283
Test: ./test.py --host

Change-Id: I63b36464df24193fff27624c1e2350d65545ad1d
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index f12cb0a..a21a97f 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -42,6 +42,7 @@
 #include "ti_heap.h"
 #include "ti_logging.h"
 #include "ti_monitor.h"
+#include "ti_search.h"
 
 #include "thread-inl.h"
 
@@ -327,6 +328,48 @@
     return error;
   }
 
+  // AddToDexClassLoader
+  error = add_extension(
+      reinterpret_cast<jvmtiExtensionFunction>(SearchUtil::AddToDexClassLoader),
+      "com.android.art.classloader.add_to_dex_class_loader",
+      "Adds a dexfile to a given dalvik.system.BaseDexClassLoader in a manner similar to"
+      " AddToSystemClassLoader.",
+      {
+        { "classloader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, false },
+        { "segment", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CCHAR, false },
+      },
+      {
+         ERR(NULL_POINTER),
+         ERR(CLASS_LOADER_UNSUPPORTED),
+         ERR(ILLEGAL_ARGUMENT),
+         ERR(WRONG_PHASE),
+      });
+  if (error != ERR(NONE)) {
+    return error;
+  }
+
+  // AddToDexClassLoaderInMemory
+  error = add_extension(
+      reinterpret_cast<jvmtiExtensionFunction>(SearchUtil::AddToDexClassLoaderInMemory),
+      "com.android.art.classloader.add_to_dex_class_loader_in_memory",
+      "Adds a dexfile buffer to a given dalvik.system.BaseDexClassLoader in a manner similar to"
+      " AddToSystemClassLoader. This may only be done during the LIVE phase. The buffer is copied"
+      " and the caller is responsible for deallocating it after this call.",
+      {
+        { "classloader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, false },
+        { "dex_bytes", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CCHAR, false },
+        { "dex_bytes_len", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
+      },
+      {
+         ERR(NULL_POINTER),
+         ERR(CLASS_LOADER_UNSUPPORTED),
+         ERR(ILLEGAL_ARGUMENT),
+         ERR(WRONG_PHASE),
+      });
+  if (error != ERR(NONE)) {
+    return error;
+  }
+
   // Copy into output buffer.
 
   *extension_count_ptr = ext_vector.size();
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index 2187825..5654c22 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -29,6 +29,9 @@
  * questions.
  */
 
+#include <sstream>
+#include <unistd.h>
+
 #include "ti_search.h"
 
 #include "jni.h"
@@ -37,6 +40,9 @@
 #include "art_jvmti.h"
 #include "base/enums.h"
 #include "base/macros.h"
+#include "base/memfd.h"
+#include "base/os.h"
+#include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file.h"
@@ -249,8 +255,121 @@
   return ERR(NONE);
 }
 
-jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
-                                                    const char* segment) {
+jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env,
+                                                   jobject classloader,
+                                                   const char* dex_bytes,
+                                                   jint dex_bytes_length) {
+  if (jvmti_env == nullptr) {
+    return ERR(INVALID_ENVIRONMENT);
+  } else if (art::Thread::Current() == nullptr) {
+    return ERR(UNATTACHED_THREAD);
+  } else if (classloader == nullptr) {
+    return ERR(NULL_POINTER);
+  } else if (dex_bytes == nullptr) {
+    return ERR(NULL_POINTER);
+  } else if (dex_bytes_length <= 0) {
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+
+  jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
+
+  // TODO We really should try to support doing this during the ON_LOAD phase.
+  if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
+    JVMTI_LOG(INFO, jvmti_env) << "Cannot add buffers to classpath during ON_LOAD phase to "
+                               << "prevent file-descriptor leaking.";
+    return ERR(WRONG_PHASE);
+  }
+
+  // We have java APIs for adding files to the classpath, we might as well use them. It simplifies a
+  // lot of code as well.
+
+  // Create a memfd
+  art::File file(art::memfd_create_compat("JVMTI InMemory Added dex file", 0), /*check-usage*/true);
+  if (file.Fd() < 0) {
+    char* reason = strerror(errno);
+    JVMTI_LOG(ERROR, jvmti_env) << "Unable to create memfd due to " << reason;
+    return ERR(INTERNAL);
+  }
+  // Fill it with the buffer.
+  if (!file.WriteFully(dex_bytes, dex_bytes_length) || file.Flush() != 0) {
+    JVMTI_LOG(ERROR, jvmti_env) << "Failed to write to memfd!";
+    return ERR(INTERNAL);
+  }
+  // Get the filename in procfs.
+  std::ostringstream oss;
+  oss << "/proc/self/fd/" << file.Fd();
+  std::string seg(oss.str());
+  // Use common code.
+
+  jvmtiError result = AddToDexClassLoader(jvmti_env, classloader, seg.c_str());
+  // We have either loaded the dex file and have a new MemMap pointing to the same pages or loading
+  // has failed and the memory isn't needed anymore. Either way we can close the memfd we created
+  // and return.
+  if (file.Close() != 0) {
+    JVMTI_LOG(WARNING, jvmti_env) << "Failed to close memfd!";
+  }
+  return result;
+}
+
+jvmtiError SearchUtil::AddToDexClassLoader(jvmtiEnv* jvmti_env,
+                                           jobject classloader,
+                                           const char* segment) {
+  if (jvmti_env == nullptr) {
+    return ERR(INVALID_ENVIRONMENT);
+  } else if (art::Thread::Current() == nullptr) {
+    return ERR(UNATTACHED_THREAD);
+  } else if (classloader == nullptr) {
+    return ERR(NULL_POINTER);
+  } else if (segment == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
+
+  // TODO We really should try to support doing this during the ON_LOAD phase.
+  if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
+    JVMTI_LOG(INFO, jvmti_env) << "Cannot add to classpath of arbitrary classloaders during "
+                               << "ON_LOAD phase.";
+    return ERR(WRONG_PHASE);
+  }
+
+  // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
+  // exceptions are swallowed.
+
+  art::Thread* self = art::Thread::Current();
+  JNIEnv* env = self->GetJniEnv();
+  if (!env->IsInstanceOf(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+    JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!";
+    return ERR(CLASS_LOADER_UNSUPPORTED);
+  }
+
+  jmethodID add_dex_path_id = env->GetMethodID(
+      art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
+      "addDexPath",
+      "(Ljava/lang/String;)V");
+  if (add_dex_path_id == nullptr) {
+    return ERR(INTERNAL);
+  }
+
+  ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
+  if (dex_path.get() == nullptr) {
+    return ERR(INTERNAL);
+  }
+  env->CallVoidMethod(classloader, add_dex_path_id, dex_path.get());
+
+  if (env->ExceptionCheck()) {
+    {
+      art::ScopedObjectAccess soa(self);
+      JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
+                                  << self->GetException()->Dump();
+    }
+    env->ExceptionClear();
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+  return OK;
+}
+
+jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env, const char* segment) {
   if (segment == nullptr) {
     return ERR(NULL_POINTER);
   }
@@ -266,41 +385,18 @@
     return ERR(WRONG_PHASE);
   }
 
-  jobject sys_class_loader = art::Runtime::Current()->GetSystemClassLoader();
-  if (sys_class_loader == nullptr) {
-    // This is unexpected.
+  jobject loader = art::Runtime::Current()->GetSystemClassLoader();
+  if (loader == nullptr) {
     return ERR(INTERNAL);
   }
 
-  // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
-  // exceptions are swallowed.
-
   art::Thread* self = art::Thread::Current();
   JNIEnv* env = self->GetJniEnv();
-  if (!env->IsInstanceOf(sys_class_loader,
-                         art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+  if (!env->IsInstanceOf(loader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
     return ERR(INTERNAL);
   }
 
-  jmethodID add_dex_path_id = env->GetMethodID(
-      art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
-      "addDexPath",
-      "(Ljava/lang/String;)V");
-  if (add_dex_path_id == nullptr) {
-    return ERR(INTERNAL);
-  }
-
-  ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
-  if (dex_path.get() == nullptr) {
-    return ERR(INTERNAL);
-  }
-  env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get());
-
-  if (env->ExceptionCheck()) {
-    env->ExceptionClear();
-    return ERR(ILLEGAL_ARGUMENT);
-  }
-  return ERR(NONE);
+  return AddToDexClassLoader(jvmti_env, loader, segment);
 }
 
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_search.h b/openjdkjvmti/ti_search.h
index 81a28cc..b8d08bf 100644
--- a/openjdkjvmti/ti_search.h
+++ b/openjdkjvmti/ti_search.h
@@ -46,6 +46,12 @@
   static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
 
   static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
+
+  static jvmtiError AddToDexClassLoader(jvmtiEnv* env, jobject classloader, const char* segment);
+  static jvmtiError AddToDexClassLoaderInMemory(jvmtiEnv* env,
+                                                jobject classloader,
+                                                const char* dex_bytes,
+                                                jint dex_bytes_length);
 };
 
 }  // namespace openjdkjvmti