Skip to content

Conversation

@mosche
Copy link
Contributor

@mosche mosche commented Oct 27, 2025

Public-callers-finder did not correctly report if methods are externally visible due to the following issues:

  • checking if interfaces are exported (see findAccessibility) must be done using the exports of the interface’s module, not the exports of the current class
  • super classes must be treated the same way as interfaces, currently these are ignored
  • currently all public methods of a (potentially private, non-exported) class implementing a public, exported interface are considered accessible / visible regardless if part of the interface or not

This fixes visibility to be consistent with the JdkApiExtractor tool by implementing both using the same common logic in AccessibleJdkMethods.loadAccessibleMethods.

Note: this currently includes #137193, which will be merged independently.

Relates to ES-13117

@mosche mosche requested a review from a team as a code owner October 27, 2025 15:11
@elasticsearchmachine elasticsearchmachine added v9.3.0 needs:triage Requires assignment of a team area label labels Oct 27, 2025
@mosche mosche requested a review from ldematte October 27, 2025 15:11
@mosche mosche added :Core/Infra/Entitlements Entitlements infrastructure >refactoring and removed needs:triage Requires assignment of a team area label labels Oct 27, 2025
@elasticsearchmachine elasticsearchmachine added the Team:Core/Infra Meta label for core/infra team label Oct 27, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-core-infra (Team:Core/Infra)


import static java.util.Collections.emptySet;

public class AccessibleJdkMethods {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is moved here from JdkApiExtractor to make it accessible as common tool

return Collections.unmodifiableMap(modulesExports);
}

public static Map<String, String> loadClassToModuleMapping() throws IOException {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved here from JdkApiExtractor

protected FindUsagesClassVisitor(Set<String> moduleExports, MethodDescriptor methodToFind, CallerConsumer callers) {
protected FindUsagesClassVisitor(
MethodDescriptor methodToFind,
Predicate<MethodDescriptor> isPublicAccessible,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rather than calculating visibility (and potentially having to follow interfaces / super classes), use a predicate based on a precalculated set of accessible method descriptors leveraging the new AccessibleJdkMethods

Copy link
Contributor

@ldematte ldematte left a comment

Choose a reason for hiding this comment

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

Looks good!
I know this is "just a tool", but give its complexity I do wonder if some parts deserve "unit" tests. I'm thinking in particular at AccessibleJdkMethods and some of the logic in Main, e.g. findTransitiveUsages.
As a follow-up. Wdyt?

return;
}
if (moduleClass.clazz.startsWith("com/sun/") && moduleClass.clazz.contains("/internal/")) {
// skip com.sun.*.internal classes as they are not part of the supported JDK API
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

Choose a reason for hiding this comment

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

I do wonder though: should we instrument those as always denied? If they are publicly accessible?
(surely not part of this PR! But it just occurred to me now)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a tricky one, some of these implement public accessible interfaces. Theoretically, if leaked / returned somewhere we could invoke such code in user land. And obviously, without this filter, these would be reported.
In the end this is purely based on the assumption / hope that no JDK API leaks such instances to keep the set of reported methods more manageable and avoid general noise on internal stuff when diffing between different JDKs.

I do wonder though: should we instrument those as always denied?

But I agree, we probably want to verify that assumption. We could deny any access to such classes from "user code" or at least log warnings.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously this was done at the very end prior to writing the result, see

if (entry.getKey().clazz.startsWith("com/sun/") && entry.getKey().clazz.contains("/internal/")) {
// skip com.sun.*.internal classes as they are not part of the supported JDK API
// even if methods override some publicly visible API
return false;
}

In fact, that's the same as not visiting such classes at all.

If you prefer to not filter these I'll remove, certainly not as harmful as the exclusions in #137193.

Copy link
Contributor

Choose a reason for hiding this comment

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

We could deny any access to such classes from "user code" or at least log warnings.

That what I was thinking too.
RE: this PR, I'm fine either way (keeping it as-is or remove the excludes)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created a follow up task for this

this.accessibleImplementations = newSortedSet();
}

private ModuleClass getModuleClass(String name) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: to make the code more readable, getModuleClassFromName?

boolean publicMethod,
boolean protectedMethod
) {
public static EnumSet<ExternalAccess> fromPermissions(boolean publicAccessible, boolean publicMethod, boolean protectedMethod) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I do wonder if reducing to ExternalAccess is still a good choice? Or if we should just dump/propagate these 3 booleans to the CSV?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was actually thinking the same, but stopped myself from making yet another change. But I think you're right, I'll have another look to see if this can be done with some decent effort.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍
Can definitely be a follow-up though

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i'll do as a follow up, this will impact a lot of places

Copy link
Contributor

@ldematte ldematte left a comment

Choose a reason for hiding this comment

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

LGTM!

@mosche mosche added the auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) label Oct 28, 2025
@elasticsearchmachine elasticsearchmachine merged commit 24a0c72 into elastic:main Oct 29, 2025
34 checks passed
@mosche mosche deleted the entitlements/fix-public-callers-visibility branch October 29, 2025 13:58
shmuelhanoch pushed a commit to shmuelhanoch/elasticsearch that referenced this pull request Oct 29, 2025
Public-callers-finder did not correctly report if methods are externally
visible due to the following issues: - checking if interfaces are
exported (see findAccessibility) must be done using the exports of the
interface’s module, not the exports of the current class - super classes
must be treated the same way as interfaces, currently these are ignored
- currently all public methods of a (potentially private, non-exported)
class implementing a public, exported interface are considered
accessible / visible regardless if part of the interface or not

This fixes visibility to be consistent with the JdkApiExtractor tool by
implementing both using the same common logic in
`AccessibleJdkMethods.loadAccessibleMethods`.

Note: this currently includes
elastic#137193, which will be
merged independently.

Relates to ES-13117
chrisparrinello pushed a commit to chrisparrinello/elasticsearch that referenced this pull request Nov 3, 2025
Public-callers-finder did not correctly report if methods are externally
visible due to the following issues: - checking if interfaces are
exported (see findAccessibility) must be done using the exports of the
interface’s module, not the exports of the current class - super classes
must be treated the same way as interfaces, currently these are ignored
- currently all public methods of a (potentially private, non-exported)
class implementing a public, exported interface are considered
accessible / visible regardless if part of the interface or not

This fixes visibility to be consistent with the JdkApiExtractor tool by
implementing both using the same common logic in
`AccessibleJdkMethods.loadAccessibleMethods`.

Note: this currently includes
elastic#137193, which will be
merged independently.

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

Labels

auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) :Core/Infra/Entitlements Entitlements infrastructure >refactoring Team:Core/Infra Meta label for core/infra team v9.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants