From 34038d7e2607c706d5cbd4fb89470319fbf97a53 Mon Sep 17 00:00:00 2001 From: Simon Lightfoot Date: Fri, 3 Nov 2023 09:24:03 +0000 Subject: [PATCH] Fix buffer overflow issue when converting strings from JNI to Dart (#416) --- jni/CHANGELOG.md | 3 +++ jni/lib/src/jni.dart | 10 +++++----- jni/lib/src/lang/jstring.dart | 6 +----- jni/test/global_env_test.dart | 20 +++++++++++++++++--- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/jni/CHANGELOG.md b/jni/CHANGELOG.md index e4f919c5..2065bb14 100644 --- a/jni/CHANGELOG.md +++ b/jni/CHANGELOG.md @@ -19,6 +19,9 @@ - **Breaking Change**: `JArray.filled` now uses the generated type class of the `fill` object and not its Java runtime type. +## 0.7.2 +- Fixed a bug where reading non-null terminated strings would overflow. + ## 0.7.1 - Removed macOS Flutter plugin until package:jni supports it ([#41](https://github.com/dart-lang/jnigen/issues/41)). diff --git a/jni/lib/src/jni.dart b/jni/lib/src/jni.dart index 2554041d..4993aee9 100644 --- a/jni/lib/src/jni.dart +++ b/jni/lib/src/jni.dart @@ -330,8 +330,7 @@ extension ProtectedJniExtensions on Jni { } extension AdditionalEnvMethods on GlobalJniEnv { - /// Convenience method for converting a [JStringPtr] - /// to dart string. + /// Convenience method for converting a [JStringPtr] to dart string. /// if [releaseOriginal] is specified, jstring passed will be deleted using /// DeleteGlobalRef. String toDartString(JStringPtr jstringPtr, {bool releaseOriginal = false}) { @@ -342,7 +341,8 @@ extension AdditionalEnvMethods on GlobalJniEnv { if (chars == nullptr) { throw ArgumentError('Not a valid jstring pointer.'); } - final result = chars.cast().toDartString(); + final length = GetStringLength(jstringPtr); + final result = chars.cast().toDartString(length: length); ReleaseStringChars(jstringPtr, chars); if (releaseOriginal) { DeleteGlobalRef(jstringPtr); @@ -377,7 +377,7 @@ extension StringMethodsForJni on String { extension CharPtrMethodsForJni on Pointer { /// Same as calling `cast` followed by `toDartString`. - String toDartString() { - return cast().toDartString(); + String toDartString({int? length}) { + return cast().toDartString(length: length); } } diff --git a/jni/lib/src/lang/jstring.dart b/jni/lib/src/lang/jstring.dart index 9e542ff9..9b8fbbb6 100644 --- a/jni/lib/src/lang/jstring.dart +++ b/jni/lib/src/lang/jstring.dart @@ -4,7 +4,6 @@ import 'dart:ffi'; -import 'package:ffi/ffi.dart'; import 'package:jni/src/jreference.dart'; import '../jni.dart'; @@ -59,10 +58,7 @@ class JString extends JObject { /// after conversion and this object will be marked as released. String toDartString({bool releaseOriginal = false}) { ensureNotNull(); - final length = Jni.env.GetStringLength(reference); - final chars = Jni.env.GetStringChars(reference, nullptr); - final result = chars.cast().toDartString(length: length); - Jni.env.ReleaseStringChars(reference, chars); + final result = Jni.env.toDartString(reference); if (releaseOriginal) { release(); } diff --git a/jni/test/global_env_test.dart b/jni/test/global_env_test.dart index 9b6a8b21..a5588cd9 100644 --- a/jni/test/global_env_test.dart +++ b/jni/test/global_env_test.dart @@ -4,6 +4,7 @@ import 'dart:io'; +import 'package:ffi/ffi.dart'; import 'package:jni/jni.dart'; import 'package:jni/src/jvalues.dart'; import 'package:test/test.dart'; @@ -118,18 +119,31 @@ void run({required TestRunnerCallback testRunner}) { })); testRunner( - "Convert back & forth between Dart & Java strings", + "Convert back & forth between Dart & Java strings (UTF-8)", () => using((arena) { const str = "ABCD EFGH"; - // This is what asJString and asDartString do internally final jstr = env.NewStringUTF(str.toNativeChars(arena)); final jchars = env.GetStringUTFChars(jstr, nullptr); - final dstr = jchars.toDartString(); + final jlen = env.GetStringUTFLength(jstr); + final dstr = jchars.toDartString(length: jlen); env.ReleaseStringUTFChars(jstr, jchars); expect(str, equals(dstr)); env.DeleteGlobalRef(jstr); })); + testRunner( + "Convert back & forth between Dart & Java strings (UTF-16)", + () => using((arena) { + const str = "ABCD EFGH"; + final jstr = env.NewString(str.toNativeUtf16().cast(), str.length); + final jchars = env.GetStringChars(jstr, nullptr); + final jlen = env.GetStringLength(jstr); + final dstr = jchars.cast().toDartString(length: jlen); + env.ReleaseStringChars(jstr, jchars); + expect(str, equals(dstr)); + env.DeleteGlobalRef(jstr); + })); + testRunner( "Print something from Java", () => using((arena) {