Uh oh!
There was an error while loading. Please reload this page.
Guard primitive array value-manager routing - #12114
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e043be9-9070-43bc-aea4-a48438690bdf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e043be9-9070-43bc-aea4-a48438690bdf
simonrozsival
commented
Aug 19, 2026
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Resolved the merge conflict with |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds defensive JNI type guards around primitive-array marshaling so IList<T> conversions don’t accidentally invoke array JNI APIs on non-array objects (avoiding CheckJNI aborts), while still allowing java.util.List objects to be converted via JavaConvert when appropriate.
Changes:
- In
ReflectionJniValueManager, gate primitive-array marshalers on the actual JNI runtime type signature (e.g. only[Zmay enter the boolean-array marshaler), and route non-arrayIList<T>conversions via a newCreateNonArrayListValuehook only when JNI confirmsjava.util.List. - Apply equivalent guards to the trimmable typemap path (
PrimitiveArrayInfo). - Add on-device regression tests covering valid conversions and invalid-cast/ownership behavior for
IList<bool>scenarios.
Show a summary per file
| File | Description |
|---|---|
| tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JavaConvertTest.cs | Adds on-device regression tests for list/primitive-array IList<bool> conversions and failure/ownership behaviors. |
| src/Mono.Android/Microsoft.Android.Runtime/PrimitiveArrayInfo.cs | Adds JNI runtime-type guard for primitive array wrappers and allows java.util.List to fall back to JavaConvert for IList<T>. |
| src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs | Overrides new CreateNonArrayListValue hook to route guarded non-array list conversions through JavaConvert. |
| src/Mono.Android/Android.Runtime/AndroidRuntime.cs | Same override as above for the runtime value manager used by AndroidRuntime. |
| external/Java.Interop/src/Java.Interop/PublicAPI.Unshipped.txt | Declares the newly added virtual ReflectionJniValueManager.CreateNonArrayListValue(...) API. |
| external/Java.Interop/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs | Implements primitive-array signature checks, java.util.List assignability gate, and new CreateNonArrayListValue extensibility point. |
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 1
- Review effort level: Lite
| var reference = new JniObjectReference (JNIEnv.NewArray (new [] { true, false }), JniObjectReferenceType.Local); | ||
| var converted = JniEnvironment.Runtime.ValueManager.GetValue<IList<bool>> (ref reference, JniObjectReferenceOptions.CopyAndDispose); | ||
| CollectionAssert.AreEqual (new [] { true, false }, converted); |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d6744e2-1156-4eb8-b63e-cdb9102cd834
simonrozsival
commented
Aug 25, 2026
/review |
✅ Android PR Reviewer completed successfully!
|
There was a problem hiding this comment.
⚠️ Needs Changes
Findings: 1 error · 0 warnings · 0 suggestions
The new runtime checks correctly protect the intended IList<T> conversion and preserve ownership on rejection, but the reflection value-manager path still treats managed array targets as list targets. A java.util.List requested as bool[] therefore reaches JNIEnv.GetArray through JavaConvert instead of failing safely. The trimmable path already avoids this by restricting its fallback to an actual IList<> target.
CI build #1564780 is still running; all completed checks currently pass.
Generated by Android PR Reviewer for #12114 · gpt56 · 137.5 AIC · ⌖ 10.8 AIC · ⊞ 25.7K
Comment /review to run again
| if (IsMatchingPrimitiveArray (marshaler, jniType)) | ||
| return marshaler.CreateValue (ref reference, options, targetType); | ||
| if (GetListType (targetType) != null && jniType != null && !jniType.StartsWith ("[", StringComparison.Ordinal)) { |
There was a problem hiding this comment.
🤖 ❌ JNI interop — GetListType(targetType) is also non-null for managed arrays because bool[] implements IList<bool>. Consequently, asking for bool[] when the JNI object is a java.util.List enters this branch and CreateNonArrayListValue delegates to JavaConvert; its array converter then calls JNIEnv.GetArray on the non-array reference, preserving the fatal JNI misuse this guard is intended to prevent. Please exclude array targets from this fallback (and add the corresponding GetValue<bool[]> regression case) so this path throws safely instead.
Rule: Validate the runtime JNI type before array access
Uh oh!
There was an error while loading. Please reload this page.
Summary
IList<T>handles throughJavaConvertonly when JNI confirms the object implementsjava.util.ListPrimitiveArrayInfopathJniObjectReferenceOptionsbefore all new invalid-cast failuresCheckJNI evidence and safety boundary
The reproducer passed a
java.util.ArrayListhandle while requestingIList<bool>. The reflection value manager selectedJavaBooleanArray.ArrayMarshalerfrom the managed target type alone, leading CheckJNI to abort the process withjarray argument has non-array type: java.util.ArrayList. This change establishes the safety boundary before any array JNI call: only the matching primitive array signature (for example,[Z) may enter that marshaler. A non-array reference may enter collection conversion only when JNIIsInstanceOfconfirmsjava.util.List; other objects and mismatched arrays fail withInvalidCastException. Failure paths dispose and invalidate transferred references according to the requested ownership options.Retained behavior
A genuine Java primitive array still marshals to
IList<T>, and genuine Java List implementations still useJavaConvert. The guard compares the actual JNI runtime type rather than rejectingIList<T>generally.Tests
Added on-device coverage in
JavaConvertTestfor:java.util.ArrayList<Boolean>handle ->IList<bool>boolean[]handle ->IList<bool>Stringhandle ->IList<bool>safely throws, preservingCopyand consumingCopyAndDisposeint[]handle ->IList<bool>safely throws and consumesCopyAndDisposeValidation:
dotnet build external/Java.Interop/src/Java.Interop/Java.Interop.csproj ...passed with 0 warnings/errors after the review fixes.make prepareis required).Mono.Android.csprojbuild reaches the repository prerequisite failure becausebin/BuildDebug/net10.0/xa-prep-tasks.dllis absent.