Skip to content

Fix: Bridge JNI strings as standard UTF-8 - #128

Open
redaranj wants to merge 3 commits into
fix/jni-boundary-correctnessfrom
fix/utf8-string-bridging
Open

Fix: Bridge JNI strings as standard UTF-8#128
redaranj wants to merge 3 commits into
fix/jni-boundary-correctnessfrom
fix/utf8-string-bridging

Conversation

@redaranj

Copy link
Copy Markdown
Member

Changes in this pull request

Replaces the modified-UTF-8 string bridging (GetStringUTFChars/NewStringUTF) with a UTF-16 transcode and explicit standard UTF-8 conversion in native code, so supplementary-plane characters like emoji survive the JNI boundary instead of being CESU-8-mangled inbound or hitting undefined behavior outbound. Confined entirely to the two string helpers — no call-site or Kotlin changes — with a round-trip test for a manifest title containing supplementary-plane characters.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • All applicable changes have been documented
  • Any TO DO items (or similar) have been entered as GitHub issues and the link to that issue has been included in a comment

@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.25%. Comparing base (6187c58) to head (c4b28a2).

Additional details and impacted files
@@                       Coverage Diff                       @@
##             fix/jni-boundary-correctness     #128   +/-   ##
===============================================================
  Coverage                           72.25%   72.25%           
  Complexity                             38       38           
===============================================================
  Files                                  62       62           
  Lines                                2350     2350           
  Branches                              286      286           
===============================================================
  Hits                                 1698     1698           
  Misses                                493      493           
  Partials                              159      159           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

redaranj and others added 3 commits August 21, 2026 07:37
GetStringUTFChars and NewStringUTF operate in JNI modified UTF-8:
supplementary-plane characters such as emoji become CESU-8 surrogate
pairs the Rust FFI rejects on the way in, and NewStringUTF on a genuine
4-byte UTF-8 sequence from the core is undefined behavior that aborts
under CheckJNI on the way out. The string helpers now transcode through
UTF-16 (GetStringChars/NewString) with an explicit standard UTF-8
encoder and decoder in native code, mapping malformed input to U+FFFD in
both directions. All call sites, the String array path, and every Kotlin
signature are unchanged. Adds a shared test round-tripping a manifest
title with supplementary-plane characters through sign and read.
The UTF-8 encoder wrote U+0000 as a literal zero byte, so a Kotlin
string containing it reached the core as a NUL-terminated string cut
short at that point. Before the standard-UTF-8 bridge, modified UTF-8
encoded it as C0 80, which the core rejected as invalid, so the input
failed loudly instead of being truncated.

Treat U+0000 as invalid input in jstring_to_cstring: free the buffer and
throw IllegalArgumentException. Callers already handle the NULL return
with an exception pending. Add a shared test that a remote URL
containing U+0000 is rejected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The strdup existed when jstring_to_cstring returned a JVM-pinned buffer
that had to be released before the local ref. It now returns a malloc'd
buffer and release_cstring_array frees each element, so store the
conversion directly. This also removes a double throw: when the
conversion failed, the OutOfMemoryError was raised on top of the
exception the conversion had already left pending. Document why
release_cstring keeps its unused env and jstr parameters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@redaranj
redaranj force-pushed the fix/utf8-string-bridging branch from 27c2200 to c4b28a2 Compare August 21, 2026 07:43
@redaranj
redaranj requested a review from scouten-adobe August 21, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant