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();