JEP draft: Integrity and Strong Encapsulation
Dan Heidinga
heidinga at redhat.com
Thu May 4 20:32:14 UTC 2023
Hi Ron,
I’ve read this draft a number of times and each time I struggled with the
framing of the problem given Java’s success over the past almost 30 years.
Framing the problem with statements like:
“Strong encapsulation offers a solid foundation to build on.
Without it, code is a castle in the sand.”
sets the conversation off in the wrong direction. I started to respond in
terms of these kinds of statements and found the response wouldn’t have
been helpful to move the conversation forward.
Instead, I stepped back and looked at the larger context. In particular,
I’m reading this in light of JEP 411: Deprecate the Security Manager for
Removal [0] and the eventual goal of completely removing the
SecurityManager.
Let me lay out how I see this and you can correct me where I’ve gone off
the rails.
As JEP 411 states, the SecurityManager has:
* Brittle permission model
* Difficult programming model
* Poor performance
Which translates into a whole lot of cost both for maintainers of the JDK
and for all users who must pay the runtime costs related to the
SecurityManager (high when enabled, but non-zero always).
Although the SecurityManager has high costs, and is infrequently used at
runtime in production, it provides the only way to limit certain
capabilities like:
* JNI (SecurityManager::checkLink)
* Encapsulation (SecurityManager::checkPackageAccess)
* Launch new processes (SecurityManager::checkExec)
* Reflective access (accessDeclaredMembers, etc)
* and others
Some of those controls need replacements if the SecurityManager will go
away. JNI, surprisingly, is a key one here for large corporations.
If I understand correctly, this new Integrity JEP draft aims, amongst other
things, to replace the hard to maintain, expensive runtime checks of the
SecurityManager with configuration via command line options. This allows
those who previously relied on the SecurityManager to continue to control
the high-order bits of functionality without imposing a cost on the rest of
the ecosystem. It also makes it easier to determine which libraries are
relying on the restricted features.
Overall, this provides a smoother migration path for users, makes the
intention of users very clear (just read the command line vs auditing
SecurityManager implementation) and improves performance by shifting these
decisions to configuration time rather than paying cost of code complexity
and stack walks.
I also appreciate the “nudge” being made with this JEP by requiring
explicit opt-in to disabling protections versus the previous uphill battle
to enable the SecurityManager. It makes for an easier conversation to ask
for i.e. JNI to be enabled for one library on the command line rather than
having to deal with all the potential restrictions of the SecurityManager.
So while overall, when viewed from the lens of removing the
SecurityManager, this approach makes sense, I do want to caution on betting
against Java’s strengths, particularly against its use of speculative
optimizations.
> Neither a person reading the code nor the platform itself – as it
compiles and runs it – can fully be assured that the code does what it says
or that its meaning does not change over time as the program runs.
…..
> In the Java runtime, certain optimizations assume that conditions that
hold at the time the optimization is made hold forever.
This is the basis of all speculative optimization - the platform assumes
the meaning doesn’t change and compiles as though it won’t. If the
application is modified at runtime, the JVM applies the necessary
compensations such as deoptimization and recompilation.
Java has bet on dynamic features time and again (even when others have
championed static approaches) and those bets - backed by speculative
optimizations - have paid off time and again. So this can’t be what you’re
arguing against.
If the concern is that the runtime behaviour may appear to be different
than the intent expressed in the source code due to use of setAccessible or
changes by agents, then I think the JEP should be more explicit about that
concern. The current wording reads as equally applying to many of Java’s
existing dynamic behaviours (and belies the power of speculation coupled
with deoptimization!).
And a few smaller quibbles:
> For example, every developer assumes that changing the signature of a
private method, or removing a private field, does not impact the class's
clients.
Right. The private modifier defines a *contract* which states anyone
depending on the implementation details are on their own and shouldn’t be
surprised by changes. I understand that it can be problematic when large
successful frameworks are broken by such changes, but that doesn’t
invalidate the contract that’s in place. The risk is higher for the JDK
than for other libraries or applications given the common dependency on the
JDK.
> However, with deep reflection, doSensitiveOperation could be invoked from
anywhere without an isAuthorized check, nullifying the intended
restriction; even worse, an agent could modify the code of the isAuthorized
method to always return true.
And clearly, these would be bugs. Not much different than leaking a
privileged MethodHandles.Lookup object outside a Class’s nest (the boundary
for private access) for which there is no enhanced integrity check.
We can’t fully protect users from code that does the wrong thing even while
undertaking efforts to minimize the attack surface. “Superpowers” are
exactly that, while we support making them opt-in, we should be careful not
to overstate the risk as the same principle applies to all code running in
a process - it must be trusted as it has the same privileges as the process.
> A tool like jlink could remove unused strongly-encapsulated methods at
link time to reduce image size and class loading time.
Most of the benefit here is not time saved by not loading the methods, it’s
actually due to avoiding the need to load classes during verification. The
verifier needs to validate relationships between classes and every extra
method potentially asserts new relationships (such as class X subclasses
Throwable) and it is these extra classes that need loading that typically
increases the startup time.
> The guarantee that code may not change over time even opens the door to
ahead-of-time compilation (AOT).
AOT doesn’t depend on the code never changing. OpenJ9 has AOT code that is
resilient in the face of changes to the underlying Java class files. I’m
positive Hotspot will be able to develop similar resilient AOT code. The
cost of validating the assumptions made while AOT compiling is much lower
than doing the compile while still enabling Java’s dynamic features.
–Dan
[0] https://openjdk.org/jeps/411
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jigsaw-dev/attachments/20230504/eaf69df0/attachment.htm>
More information about the jigsaw-dev
mailing list