Additional checks in GetDirectBuffer{Address,Capacity}

Add checks that the buffer argument is non-null and is a j.n.Buffer
instance.

This matches the behaviour described in the JNI spec for both methods
based on RETURNS section commentary. The JNI spec comments for
parameters seem at odds since they say the buffer must not be null,
(but not MUST not be null :-).

Bug: 122025675
Test: art/tools/buildbot-build.sh --host && jni_internal_test
Change-Id: Ie74ad2160a7b265e12fe490aa96fccda52379949
Merged-In: Ie74ad2160a7b265e12fe490aa96fccda52379949
(cherry picked from commit bda163d9c8313f0b92046abda5ffb1216af1e808)
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 67073a8e..b228d99 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -2519,13 +2519,29 @@
   }
 
   static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) {
+    // Return null if |java_buffer| is not defined.
+    if (java_buffer == nullptr) {
+      return nullptr;
+    }
+
+    // Return null if |java_buffer| is not a java.nio.Buffer instance.
+    if (!IsInstanceOf(env, java_buffer, WellKnownClasses::java_nio_Buffer)) {
+      return nullptr;
+    }
+
+    // Buffer.address is non-null when the |java_buffer| is direct.
     return reinterpret_cast<void*>(env->GetLongField(
-        java_buffer, WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress));
+        java_buffer, WellKnownClasses::java_nio_Buffer_address));
   }
 
   static jlong GetDirectBufferCapacity(JNIEnv* env, jobject java_buffer) {
+    // Check if |java_buffer| is a direct buffer, bail if not.
+    if (GetDirectBufferAddress(env, java_buffer) == nullptr) {
+      return -1;
+    }
+
     return static_cast<jlong>(env->GetIntField(
-        java_buffer, WellKnownClasses::java_nio_DirectByteBuffer_capacity));
+        java_buffer, WellKnownClasses::java_nio_Buffer_capacity));
   }
 
   static jobjectRefType GetObjectRefType(JNIEnv* env ATTRIBUTE_UNUSED, jobject java_object) {
diff --git a/runtime/jni/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc
index a4b3801..02e1ad4 100644
--- a/runtime/jni/jni_internal_test.cc
+++ b/runtime/jni/jni_internal_test.cc
@@ -2281,11 +2281,30 @@
   ASSERT_NE(buffer_class, nullptr);
 
   char bytes[1024];
-  jobject buffer = env_->NewDirectByteBuffer(bytes, sizeof(bytes));
-  ASSERT_NE(buffer, nullptr);
-  ASSERT_TRUE(env_->IsInstanceOf(buffer, buffer_class));
-  ASSERT_EQ(env_->GetDirectBufferAddress(buffer), bytes);
-  ASSERT_EQ(env_->GetDirectBufferCapacity(buffer), static_cast<jlong>(sizeof(bytes)));
+  jobject direct_buffer = env_->NewDirectByteBuffer(bytes, sizeof(bytes));
+  ASSERT_NE(direct_buffer, nullptr);
+  ASSERT_TRUE(env_->IsInstanceOf(direct_buffer, buffer_class));
+  ASSERT_EQ(env_->GetDirectBufferAddress(direct_buffer), bytes);
+  ASSERT_EQ(env_->GetDirectBufferCapacity(direct_buffer), static_cast<jlong>(sizeof(bytes)));
+
+  // Check we don't crash if a nullptr is passed to field accessors.
+  ASSERT_EQ(env_->GetDirectBufferAddress(nullptr), nullptr);
+  ASSERT_EQ(env_->GetDirectBufferCapacity(nullptr), -1L);
+
+  // Check if j.n.Buffer types backed by heap memory return the invalid values described in the
+  // RETURNS clauses of JNI spec for GetDirectBufferAddress() and GetDirectBufferCapacity().
+  ScopedLocalRef<jclass> bb(env_, env_->FindClass("java/nio/ByteBuffer"));
+  jmethodID bb_allocate = env_->GetStaticMethodID(bb.get(), "allocate", "(I)Ljava/nio/ByteBuffer;");
+  jobject heap_buffer = env_->CallStaticObjectMethod(bb.get(), bb_allocate, 128);
+  ASSERT_NE(heap_buffer, nullptr);
+  ASSERT_EQ(env_->GetDirectBufferAddress(heap_buffer), nullptr);
+  ASSERT_EQ(env_->GetDirectBufferCapacity(heap_buffer), -1L);
+
+  // Check invalid values are returned if the buffer argument has an object type is not a sub-type
+  // of j.n.Buffer.
+  jobject not_buffer = env_->NewStringUTF("A String");
+  ASSERT_EQ(env_->GetDirectBufferAddress(not_buffer), nullptr);
+  ASSERT_EQ(env_->GetDirectBufferCapacity(not_buffer), -1L);
 
   {
     CheckJniAbortCatcher check_jni_abort_catcher;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index fabd4db..27eb28a 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -75,6 +75,7 @@
 jclass WellKnownClasses::java_lang_Thread;
 jclass WellKnownClasses::java_lang_ThreadGroup;
 jclass WellKnownClasses::java_lang_Throwable;
+jclass WellKnownClasses::java_nio_Buffer;
 jclass WellKnownClasses::java_nio_ByteBuffer;
 jclass WellKnownClasses::java_nio_DirectByteBuffer;
 jclass WellKnownClasses::java_util_Collections;
@@ -153,6 +154,7 @@
 jfieldID WellKnownClasses::java_lang_Throwable_stackState;
 jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions;
 jfieldID WellKnownClasses::java_nio_Buffer_address;
+jfieldID WellKnownClasses::java_nio_Buffer_capacity;
 jfieldID WellKnownClasses::java_nio_Buffer_elementSizeShift;
 jfieldID WellKnownClasses::java_nio_Buffer_limit;
 jfieldID WellKnownClasses::java_nio_Buffer_position;
@@ -161,8 +163,6 @@
 jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly;
 jfieldID WellKnownClasses::java_nio_ByteBuffer_limit;
 jfieldID WellKnownClasses::java_nio_ByteBuffer_offset;
-jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity;
-jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress;
 jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST;
 jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
 jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data;
@@ -364,6 +364,7 @@
   java_lang_Thread = CacheClass(env, "java/lang/Thread");
   java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
   java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
+  java_nio_Buffer = CacheClass(env, "java/nio/Buffer");
   java_nio_ByteBuffer = CacheClass(env, "java/nio/ByteBuffer");
   java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
   java_util_Collections = CacheClass(env, "java/util/Collections");
@@ -445,19 +446,17 @@
   java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
   java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
 
-  ScopedLocalRef<jclass> java_nio_Buffer(env, env->FindClass("java/nio/Buffer"));
-  java_nio_Buffer_address = CacheField(env, java_nio_Buffer.get(), false, "address", "J");
-  java_nio_Buffer_elementSizeShift = CacheField(env, java_nio_Buffer.get(), false, "_elementSizeShift", "I");
-  java_nio_Buffer_limit = CacheField(env, java_nio_Buffer.get(), false, "limit", "I");
-  java_nio_Buffer_position = CacheField(env, java_nio_Buffer.get(), false, "position", "I");
+  java_nio_Buffer_address = CacheField(env, java_nio_Buffer, false, "address", "J");
+  java_nio_Buffer_capacity = CacheField(env, java_nio_Buffer, false, "capacity", "I");
+  java_nio_Buffer_elementSizeShift = CacheField(env, java_nio_Buffer, false, "_elementSizeShift", "I");
+  java_nio_Buffer_limit = CacheField(env, java_nio_Buffer, false, "limit", "I");
+  java_nio_Buffer_position = CacheField(env, java_nio_Buffer, false, "position", "I");
 
   java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J");
   java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B");
   java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z");
   java_nio_ByteBuffer_limit = CacheField(env, java_nio_ByteBuffer, false, "limit", "I");
   java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I");
-  java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
-  java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J");
   java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
   libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
   org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
@@ -538,6 +537,7 @@
   java_lang_ThreadGroup = nullptr;
   java_lang_Throwable = nullptr;
   java_util_Collections = nullptr;
+  java_nio_Buffer = nullptr;
   java_nio_ByteBuffer = nullptr;
   java_nio_DirectByteBuffer = nullptr;
   libcore_reflect_AnnotationFactory = nullptr;
@@ -617,8 +617,6 @@
   java_nio_ByteBuffer_isReadOnly = nullptr;
   java_nio_ByteBuffer_limit = nullptr;
   java_nio_ByteBuffer_offset = nullptr;
-  java_nio_DirectByteBuffer_capacity = nullptr;
-  java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr;
   java_util_Collections_EMPTY_LIST = nullptr;
   libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr;
   org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 6f67fde6..fe17849 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -88,6 +88,7 @@
   static jclass java_lang_Thread;
   static jclass java_lang_ThreadGroup;
   static jclass java_lang_Throwable;
+  static jclass java_nio_Buffer;
   static jclass java_nio_ByteBuffer;
   static jclass java_nio_DirectByteBuffer;
   static jclass java_util_Collections;
@@ -166,6 +167,7 @@
   static jfieldID java_lang_Throwable_stackState;
   static jfieldID java_lang_Throwable_suppressedExceptions;
   static jfieldID java_nio_Buffer_address;
+  static jfieldID java_nio_Buffer_capacity;
   static jfieldID java_nio_Buffer_elementSizeShift;
   static jfieldID java_nio_Buffer_limit;
   static jfieldID java_nio_Buffer_position;
@@ -174,8 +176,6 @@
   static jfieldID java_nio_ByteBuffer_isReadOnly;
   static jfieldID java_nio_ByteBuffer_limit;
   static jfieldID java_nio_ByteBuffer_offset;
-  static jfieldID java_nio_DirectByteBuffer_capacity;
-  static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress;
 
   static jfieldID java_util_Collections_EMPTY_LIST;
   static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT;