Ensure GetStringUTFRegion is null-terminated

Null terminate GetStringUTFRegion results. This should help prevent
some types of bugs in JNI.

This changes behavior to match what is observed on the RI.

Test: ./test.py
Bug: 166834353
Change-Id: Ifee6ea2d10ffb7501fc7f141f6da7dc08214fd23
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index bf66d4f..a2e4f22 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -1857,14 +1857,20 @@
       ThrowSIOOBE(soa, start, length, s->GetLength());
     } else {
       CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf);
+      if (length == 0 && buf == nullptr) {
+        // Don't touch anything when length is 0 and null buffer.
+        return;
+      }
       if (s->IsCompressed()) {
         for (int i = 0; i < length; ++i) {
           buf[i] = s->CharAt(start+i);
         }
+        buf[length] = '\0';
       } else {
         const jchar* chars = s->GetValue();
         size_t bytes = CountUtf8Bytes(chars + start, length);
         ConvertUtf16ToModifiedUtf8(buf, bytes, chars + start, length);
+        buf[bytes] = '\0';
       }
     }
   }
diff --git a/runtime/jni/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc
index 0968b6b..a4cfd3a 100644
--- a/runtime/jni/jni_internal_test.cc
+++ b/runtime/jni/jni_internal_test.cc
@@ -1637,18 +1637,27 @@
   env_->GetStringUTFRegion(s, 0x7fffffff, 0x7fffffff, nullptr);
   ExpectException(sioobe_);
 
-  char bytes[4] = { 'x', 'x', 'x', 'x' };
+  char bytes[5] = { 'x', 'x', 'x', 'x', 'x' };
   env_->GetStringUTFRegion(s, 1, 2, &bytes[1]);
   EXPECT_EQ('x', bytes[0]);
   EXPECT_EQ('e', bytes[1]);
   EXPECT_EQ('l', bytes[2]);
-  EXPECT_EQ('x', bytes[3]);
+  // NB: The output string is null terminated so this slot is overwritten.
+  EXPECT_EQ('\0', bytes[3]);
+  EXPECT_EQ('x', bytes[4]);
 
   // It's okay for the buffer to be null as long as the length is 0.
   env_->GetStringUTFRegion(s, 2, 0, nullptr);
   // Even if the offset is invalid...
   env_->GetStringUTFRegion(s, 123, 0, nullptr);
   ExpectException(sioobe_);
+  // If not null we still have a 0 length string
+  env_->GetStringUTFRegion(s, 1, 0, &bytes[1]);
+  EXPECT_EQ('x', bytes[0]);
+  EXPECT_EQ('\0', bytes[1]);
+  EXPECT_EQ('l', bytes[2]);
+  EXPECT_EQ('\0', bytes[3]);
+  EXPECT_EQ('x', bytes[4]);
 }
 
 TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index 65da24e..429bd94 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -90,6 +90,35 @@
   CHECK(!env->ExceptionCheck());
 }
 
+extern "C" JNIEXPORT void JNICALL Java_Main_testUTFRegion(JNIEnv* env, jclass, jstring null_str) {
+  jstring foo_str = env->NewStringUTF("FOOBAR");
+  jstring emoji_str = env->NewStringUTF("SKI ⛷ SKI");
+  char buf[1024];
+  memset(buf, 'Y', sizeof(buf));
+  buf[1023] = '\0';
+
+  env->GetStringUTFRegion(foo_str, 3, 1, buf);
+  buf[1023] = '\0';
+  CHECK_EQ(strcmp("B", buf), 0) << buf;
+
+  // Null char on 0 len region
+  env->GetStringUTFRegion(foo_str, 3, 0, buf);
+  buf[1023] = '\0';
+  CHECK_EQ(strcmp("", buf), 0) << buf;
+
+  // No SEGV
+  env->GetStringUTFRegion(foo_str, 3, 0, nullptr);
+
+  env->GetStringUTFRegion(null_str, 1, 1, buf);
+  buf[1023] = '\0';
+  std::array<uint8_t, 3> nullbuf{ 0xc0, 0x80, 0x00 };
+  CHECK_EQ(memcmp(nullbuf.data(), buf, 3), 0);
+
+  env->GetStringUTFRegion(emoji_str, 1, 6, buf);
+  buf[1023] = '\0';
+  CHECK_EQ(strcmp("KI ⛷ S", buf), 0);
+}
+
 extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldSubclass(JNIEnv* env,
                                                              jclass,
                                                              jobject f_obj,
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 3341bbd..5ec70d4 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -65,6 +65,7 @@
         testClinitMethodLookup();
 
         testDoubleLoad(args[0]);
+        testUTFRegion("\0\0\0");
     }
 
     static class ABC { public static int XYZ = 12; }
@@ -78,6 +79,8 @@
       }
     }
 
+    public static native void testUTFRegion(String null_str);
+
     public static native int getFieldSubclass(Field f, Class sub);
 
     private static native boolean registerNativesJniTest();