Skip to content

[release/10.0] Release Android X509 chain certificate GREFs#128385

Open
github-actions[bot] wants to merge 5 commits into
release/10.0from
backport/pr-128284-to-release/10.0
Open

[release/10.0] Release Android X509 chain certificate GREFs#128385
github-actions[bot] wants to merge 5 commits into
release/10.0from
backport/pr-128284-to-release/10.0

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 19, 2026

Backport of #128284 to release/10.0

/cc @simonrozsival

Customer Impact

  • Customer reported
  • Found internally

A customer reported that their app was crashing due to a memory leak in X509Chain class (JNI global ref table overflow).

Regression

  • Yes
  • No

[If yes, specify when the regression was introduced. Provide the PR or commit if known.]

Testing

The new unit test (ChainTests.BuildChainRepeatedly_DoesNotExhaustGlobalReferences) passes locally on Android emulator.

Risk

Low. The change only affects Android and the code path is well covered by tests.

IMPORTANT: If this backport is for a servicing release, please verify that:

  • For .NET 8 and .NET 9: The PR target branch is release/X.0-staging, not release/X.0.
  • For .NET 10+: The PR target branch is release/X.0 (no -staging suffix).

Package authoring no longer needed in .NET 9

IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.

simonrozsival and others added 4 commits May 19, 2026 21:15
The Android X509 chain PAL returns JNI global references for chain certificates. Creating X509Certificate2 from those pointers duplicates the references for managed ownership, leaving the native-returned references caller-owned. Release those temporary references after conversion so repeated chain builds do not exhaust ART global refs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ref limit

Adds [OuterLoop] regression test
BuildChainRepeatedly_DoesNotExhaustGlobalReferences that builds a
6-certificate chain (root + 4 intermediates + endCert) 8,600 times.

Without the gref-release fix in PR #128284,
AndroidCryptoNative_X509ChainGetCertificates returns caller-owned JNI
global references that X509Certificate2 then duplicates, leaving the
native-returned refs orphaned. A 6-cert chain leaks 6 grefs per
successful build; 8,600 builds therefore leak 51,600 references, which
exceeds Android's default global-reference table limit (51,200) and
aborts the process with 'global reference table overflow (max=51200)'.

Threshold validation against the unfixed code path:
  - 8,400 iterations completed successfully
  - 8,500 iterations crashed with the gref-table overflow

With the managed try/finally cleanup in place
(Interop.X509Chain.X509ChainGetCertificates), 8,600 iterations complete
cleanly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The native AndroidCryptoNative_X509ChainGetCertificates writes a JNI
global ref into certs[i] per loop iteration and bails out via
ON_EXCEPTION_PRINT_AND_GOTO if a JNI exception fires partway through.
On that path it returns FAIL (0) with the previously-populated certs[]
entries still holding grefs.

The previous cleanup only ran when res > 0 and only walked the first
res entries, so any grefs left behind on the failure path leaked.
Move the res == 0 throw inside the try, and scan the whole certPtrs
array (with an IntPtr.Zero guard) in the finally so both the success
and partial-population failure paths release every entry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival marked this pull request as draft May 19, 2026 21:23
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @bartonjs, @vcsjones, @dotnet/area-system-security
See info in area-owners.md if you want to be subscribed.

@simonrozsival simonrozsival marked this pull request as ready for review May 19, 2026 22:42
Copilot AI review requested due to automatic review settings May 19, 2026 22:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Backport of #128284 to release/10.0. Fixes a JNI global-reference leak in the Android X509 chain PAL where caller-owned JNI global refs returned by AndroidCryptoNative_X509ChainGetCertificates were never released after X509Certificate2 duplicated them, eventually overflowing Android's global reference table (max 51,200).

Changes:

  • Wrap certificate materialization in a try/finally that releases every non-null certPtrs entry via JObjectLifetime.DeleteGlobalReference.
  • Add PlatformDetection.IsNotInHelix helper.
  • Add an Android-only [OuterLoop]-style regression test that builds an 8,600-iteration 6-cert chain to detect GREF exhaustion.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs Releases native-returned JNI global refs in a finally block after constructing X509Certificate2 instances.
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs Adds IsNotInHelix convenience property for conditional tests.
src/libraries/System.Security.Cryptography/tests/X509Certificates/ChainTests.cs Adds Android regression test exercising repeated chain builds to verify GREFs are not leaked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants