Skip to content

[GR-69858] Make Native Image security provider inclusion metadata-driven - #14159

Open
graalvmbot wants to merge 63 commits into
masterfrom
vj/GR-69858-reflection-sec-providers
Open

[GR-69858] Make Native Image security provider inclusion metadata-driven#14159
graalvmbot wants to merge 63 commits into
masterfrom
vj/GR-69858-reflection-sec-providers

Conversation

@graalvmbot

Copy link
Copy Markdown
Collaborator

Summary

This change makes security provider inclusion consistent with Native Image reachability metadata.

Provider classes discovered during analysis are treated as candidates rather than being included automatically. A concrete provider and all of its valid, resolvable services are included when the provider has a supported construction path and reflection metadata registers:

  • the provider type;
  • its declared nullary constructor; or
  • its public static nullary provider() factory method.

The new --future-defaults=explicit-security-provider-registration behavior applies this requirement to providers selected through reachable JCA factories and security-service facades such as GSS and SASL. Without that future default, the existing service-driven inclusion behavior remains available for compatibility.

The Tracing Agent and native metadata tracing record provider lookups so applications can collect the required metadata automatically.

User impact

The -H:AdditionalSecurityProviders and -H:AdditionalSecurityServiceTypes options are now deprecated. Applications should instead:

  1. run with the Tracing Agent;
  2. register provider classes in reachability-metadata.json; or
  3. use -H:Preserve=all to retain all JDK providers.

For example, replace:

-H:AdditionalSecurityProviders=com.example.security.CustomProvider

with reflection metadata such as:

{
  "reflection": [
    {
      "type": "com.example.security.CustomProvider",
      "methods": [
        {
          "name": "<init>",
          "parameterTypes": []
        }
      ]
    }
  ]
}

The deprecated provider option remains accepted for compatibility and is translated into the corresponding reflection registration.

Use --future-defaults=explicit-security-provider-registration to adopt the metadata requirement now. It is also enabled by the preferred --future-defaults=all. This transition is independent of --future-defaults=run-time-initialize-security-providers, which constructs the configured provider list at run time and filters out unregistered providers while preserving the order of those that remain.

SecureRandom does not retain service-driven provider inclusion. A reachable SecureRandom acquisition path supplies a platform-owned conditional provider-registration signal. Native Image then processes each applicable configured provider using the same complete-provider semantics as reflection metadata. Applications do not need separate provider metadata for this case, and no provider is registered when no acquisition path is reachable.

Standard missing-registration diagnostics

With exact reachability metadata enabled, an omitted provider loaded reflectively is reported through the regular reflection missing-registration path rather than a provider-specific error. The resulting MissingReflectionRegistrationError identifies the provider class and prints the same ready-to-copy metadata entry as other missing reflection accesses, for example:

{
  "type": "com.example.security.CustomProvider"
}

Using the standard reflection path means existing missing-registration tooling handles provider failures automatically, while the Tracing Agent and native metadata tracing collect the same provider reflection metadata.

Programmatic provider registration through Security.addProvider(...) and Security.insertProviderAt(...) is traced as provider reflection access as well.

Architecture

Independent transition axes

SecurityProviderMode models provider inclusion and provider-list initialization as independent
axes. Hosted code reads that mode instead of consulting future-default options piecemeal, and
mode-specific substitutions are selected at image build time. Run-time system properties therefore
cannot change the policy with which the image was built.

Candidates, signals, and plans

SecurityServicesFeature coordinates the build lifecycle. Reachable Provider subtypes and
META-INF/services/java.security.Provider entries discover candidate classes only; neither is an
inclusion signal by itself.

SecurityProviderRegistrationPlanner records complete-provider intent and its provenance:

  • application reflection metadata;
  • -H:Preserve=all;
  • the platform-owned SecureRandom signal;
  • the deprecated additional-provider option; or
  • legacy service reachability.

Qualifying application metadata is read through a narrow ReflectionRegistrationView. The planner
keeps this input separate from reflection metadata emitted while realizing a plan, so generated
metadata cannot be mistaken for a new application signal.

Subtype callbacks only add candidates to concurrent collections. A serialized
duringAnalysis pass consumes signals, realizes newly completed plans, and requests another
analysis iteration. This avoids scheduling analysis iterations from concurrent reachability
callbacks.

Complete-provider realization

SecurityProviderCatalogRegistrar realizes each complete-provider plan. It selects the supported
construction path, processes every configured instance of the provider class, registers the union
of their valid and resolvable service catalogs, and records the JCE verification result.

Providers that Native Image can construct are marked as JDK-constructible. A provider class that
is registered for reflection but has no supported JDK construction path is recorded as
application-supplied-only: its verification result is preserved, but Native Image neither
reconstructs it nor infers its services.

Typed run-time manifest and enforcement

Hosted registration writes SecurityProviderRuntimeState, a layered manifest keyed by provider
class name. Each entry combines an acquisition kind (JDK_CONSTRUCTIBLE or
APPLICATION_SUPPLIED_ONLY) with the preserved JCE verification outcome. Entries from image
layers are merged so a JDK-constructible registration wins while verification failures remain
available.

Run-time responsibilities are split into small services:

  • BuiltInSecurityProviderLoader owns built-in provider aliases and construction;
  • SecurityProviderRuntimeAccess owns metadata tracing and missing-registration diagnostics; and
  • JceProviderVerificationSupport adapts manifest entries to the JDK verification contract.

Both provider-list initialization modes use this manifest. In build-time-list mode, the hosted
provider list and verification cache are filtered after provider plans are complete. In
run-time-list mode, substitutions rebuild the configured list from saved initial security
properties and allow the JDK to construct only providers marked JDK-constructible.

Descriptors, tracing, and compatibility

Explicit registration preserves java.security.Provider service descriptors without treating them
as registration signals. Provider lookup, enumeration, and programmatic registration are traced as
reflection type access by both native metadata tracing and the Tracing Agent, so the same metadata
drives subsequent builds.

Deprecated provider options and legacy service-reachability inclusion are confined to
LegacySecurityProviderCompatibility. Removing the transition behavior does not require changes to
the planner, catalog registrar, run-time manifest, or planned-default substitutions.

Hello World image size

Measured on 2026-07-24 using native-image optimization level 2, without -Ob.
The before images use CE master fdc2690a6e and EE master ea2a8732e6.

With --future-defaults=all:

Edition Before (master) After (change) Change
CE 17.94 MiB (18,811,840 bytes) 16.75 MiB (17,566,656 bytes) -1.19 MiB (-6.62%)
EE 21.63 MiB (22,678,464 bytes) 20.19 MiB (21,171,136 bytes) -1.44 MiB (-6.65%)

Without --future-defaults:

Edition Before (master) After (change) Change
CE 5.13 MiB (5,376,960 bytes) 5.13 MiB (5,376,960 bytes) 0 bytes (0.00%)
EE 6.25 MiB (6,556,608 bytes) 6.25 MiB (6,556,608 bytes) 0 bytes (0.00%)

@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Aug 4, 2026
@graalvmbot
graalvmbot force-pushed the vj/GR-69858-reflection-sec-providers branch from d5f7d38 to 970fe83 Compare August 5, 2026 08:30
vjovanov added 27 commits August 7, 2026 12:34
Security provider analysis changes the timing of reachability callbacks and exposed a race in JNIAccessFeature. Reachability callbacks add JNI classes, methods, and fields concurrently with duringAnalysis(), but duringAnalysis() previously iterated each positive-registration worklist and then cleared it. A registration added after the weakly consistent iterator had passed an entry, but before clear(), was silently discarded.

This could drop the JNI metadata registered for java.net.NetworkInterface. Its runtime native initializer then failed FindClass("java/net/NetworkInterface") with NoClassDefFoundError in the recurring-callback SVM test image.

Remove each observed positive registration atomically before processing it instead of clearing the worklists afterward. Registrations not observed by the current iteration, or added concurrently after removal, remain queued for the next analysis iteration.
This reverts commit a3b5ef097c6c26a4f20d564b7923618997aa25be.
vjovanov added 28 commits August 7, 2026 12:34
@graalvmbot
graalvmbot force-pushed the vj/GR-69858-reflection-sec-providers branch from 56a526d to 19aac29 Compare August 7, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants