Questions w.r.t. plans to restrict the FFM API

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu Apr 29 18:30:02 UTC 2021


On 29/04/2021 17:33, Mike Hearn wrote:
> Thanks! OK, so let's zoom in on two of the issues here:
>
> 1. The exact meaning of safety in the post-SM world.
>
> 2. The usability benefits of a mechanism different to module visibility.
>
> I suspected we might end up discussing the definition of safety, hence
> the opening discourse on it. I'm still struggling a bit to wrap my
> head around what this concept means now. The issue of APIs that change
> final fields feels orthogonal to the other aspects of safety (security
> and "nice" crashes). You can change final fields from both Java and
> JNI today and whilst it restricts optimizations, going slower or
> faster isn't something I think most people associate with the notion
> of safety. Or if anything people would think slower=safer and
> faster=less safe by analogy to the real world, but here the meaning is
> inverted.
>
> The other reason it feels orthogonal is that this is an API design
> issue very specific to Java as it stands in 2021, and it's really
> primarily a HotSpot developer concern rather than a Java developer
> concern (modulo that everyone loves performance). Safety feels like
> it's meant to be a runtime design principle that could also apply to
> V8 or .NET. If a new Java release allows truly final fields then this
> aspect of safety would disappear, but a truly general principle
> shouldn't change in major ways from one version to the next. Perhaps
> I'm over-focusing on final fields, but I'm not sure how to generalize
> this appropriately because I'm not totally sure what the connections
> are between the aspects. A clear definition of safety and the
> justification for it for each group of stakeholders would be a really
> good thing to have.

When I (and JEP 412) speak about safety, they don't really mean 
"security", which seems to be your assumption here.

By an API being safe, what I mean is that it is not possible for a user, 
either maliciously or accidentally, to alter invariants in the Java 
runtime. Examples of such invariants are (but are not limited to):

* corrupt data structures inside the JVM itself (which might, but also 
might not, result in a crash)
* access to Java API points that would otherwise be out of reach (e.g. 
private fields, non-exported types, etc.)
* updates contents of heap in ways that are not possible using Java code 
(e.g. write only "half" a long value in a field)

...

When such invariants are violated, there's no telling as to what could 
happen next; maybe you get a crash, maybe the application will misbehave 
in a much more subtle fashion, even in ways which might be hard to 
detect. Sometimes, as in the case of final field updates, it might 
prevent certain kinds of VM optimizations - although that's just an 
example. In the more common case, running a program which relies unsafe  
behavior is akin to the dreaded "unspecified" behavior in the C 
specifications. All bets are off. Anything can happen. The Java Memory 
Model might be compromised. The JVM itself might be compromized. This is 
what we mean by _safety_ - and this is why we'd like for a Java platform 
that is _safe_ by default. While it might sometimes be necessary for a 
library to dip into unsafe waters to perform a certain task, it is 
ultimately up to the user as to whether to trust that library to do that 
"dip". Depending on the context, the answer might vary.


>
> OK, re: usability.
>
>> The places where opt-in will occur will be the "applications" which
>> package one or more libraries using FFM. These applications will already
>> come with launchers, or will be generated via tools like `jpackage`.
> Yes, I understand that. My point is that developers are users too. If
> I am writing an app and add a new top level library dependency that
> pulls in another 4 levels of libraries (quite possible in today's
> ecosystem), then if the library author at the bottom of that stack
> wants to start using FFM then they must do a new release that adds to
> their readme "you now need --enable-native-access on JDK >X for this
> module to work". And then because there's no mechanism to propagate
> this, the authors of the next level up must do the same thing, and so
> on, until I upgrade that top level dependency and hopefully spot the
> new warning somehow in the release notes. And then finally the action
> is on me to adjust my JVM arguments. This is kind of awkward and a lot
> of work for everyone in the chain. Whilst the ultimate non-technical
> end users don't have to do anything, developers-as-library-users
> definitely do.
Developers are users too - sure! I'm far from disputing that. But the 
ones which will pay the price for the measures we're adding are not just 
"any developers". They are "application developers". And application 
developers have (as I wrote previously) already 10 different ways to 
package up this or that option (e.g. enable that particular garbage 
collector, or ramp up the heap size), in order to make their application 
behave optimally. So, while I won't dispute that there will be _some_ 
cost _somewhere_, your claim that _every single developer_ using FFM API 
will have to think about this is, IMHO, exaggerated.
>
>> Maybe it will be possible, for a module that is "inside the bubble" to
>> grant (reflectively) native access to other modules.
> This is certainly a solution, although it complicates app development
> and the goal is to simplify it.
The goal is to make the transition between safe and unsafe explicit, and 
to have application packagers explicitly acknowledge which libraries to 
trust. It is possible, that, in making the platform safer, and making 
this transition more explicit, some extra complexity will be added as a 
side-effect. And we should do our best in order to keep that to a 
minimum. But saying that, because making the platform safer for 
_everybody_ will add some complexity to _some_ parties, then we should 
just not pursue that goal is, IMHO, not a very good argument.
>
>> * you don't see the flipside; consider now an application which supports
>> plugins but which, for whatever reason, _doesn't want_ plugins to access
>> native code. The proposed solution gives you a way to do that (if required).
> I did consider that flip side but, as mentioned, I can't think of a
> situation in which anyone will actually do this.
>
> Again, this comes back to the definition of safety. With an IDE or
> build system, the exact nature of HotSpot's optimizations aren't
> something that directly affects people. Debugging experience does
> affect them so they might care about that, but in which circumstances
> would an IDE developer want to stop plugins accessing native code? The
> plugin cannot be sandboxed anymore and that's what users would really
> value. So restricting native code but not java.io.File feels a bit
> pointless - it could be helpful if for some reason the userbase keeps
> segfaulting their IDE with terrible native plugins and we want a
> REALLY big hammer to stop that happening, at the risk of upsetting
> users whose plugins are now broken. But that'd be a very rare
> situation. Java apps could use the SecurityManager to restrict JNI in
> the same way until now but nobody ever did.
I can imagine a lot of situations where libraries could be audited by 
some security team and only granted access on a more detailed look at 
what the library is actually trying to do. Knowing that a library is 
attempting to perform native access is information: it's up to you what 
to do with that information. You can drop it on the floor and add the 
required flags just "to make it work"; but in some cases you might 
wonder as to why this seemingly innocuous (random example) JSON API is 
attempting to use native memory, and ask yourself if that library is 
really what you want to use/depend on, and how much you are willing to 
bet that this API is not messing up the environment in which your 
application will execute on.
>
> W.R.T. separate modules, yes it's true, splitting the modules
> introduces discoverability problems and makes the API worse. So maybe
> this is suggesting there's a need for a more heavily designed
> mechanism. The JVM now has quite a lot of ad-hoc flags that act as
> "yes I know what I'm doing" markers:
>
> * Incubation
> * Opening modules
> * Native access
You forgot one: preview features/API (JEP 12)
>
> Gradle and Kotlin have similar mechanisms now too. All of these have
> the same flavour: "you don't want me to do something but I definitely
> want to do it, so here's a flag to make you get out of the way". It
> feels like there's the potential to unify these, perhaps with
> solutions to the module refactoring, dependency chain and plugin
> loading problems.

There is indeed a lot of overlapping between these. For now it doesn't 
look like there is an existing solution which we can just borrow from. 
There are lots of "near misses", for sure. One thing that will be worth 
investigating, moving forward is how much the removal of SecurityManager 
(JEP 411) will require "replacement" mechanisms. For instance, how to 
protect from a client calling System.exit(). Now that problem is exactly 
the same as the problem we're solving here and it is possible that, in 
the future there will be some unification (we will _not_ add a new 
command line for different set of "restricted" methods). For now we're 
still exploring as to whether the FFM API is the only client for this 
mechanism, or if there are more. If it turns out there are more, we'd be 
more than happy to come up with something slightly more general.

Maurizio




More information about the panama-dev mailing list