My position on module security/access control
David M. Lloyd
david.lloyd at redhat.com
Tue Dec 15 16:51:11 UTC 2015
On 12/14/2015 01:45 PM, mark.reinhold at oracle.com wrote:
> 2015/12/10 8:00 -0800, david.lloyd at redhat.com:
>> On 12/09/2015 07:29 PM, mark.reinhold at oracle.com wrote:
>>> 2015/11/18 8:10 -0800, david.lloyd at redhat.com:
>>>> ... The concept being established here is the
>>>> ability to have a public class which is not exported but is still
>>>> accessible to anyone.
>>>
>>> If I understand correctly, you're proposing that a public type in a
>>> non-exported package be accessible via reflection, but not via static
>>> references in source or class files. (There would still have to be some
>>> way to declare whether the public types in a package are exported for
>>> use in static references, but how that's done is a different issue.)
>>>
>>> You argue that "public" should always mean "public", yet your proposal
>>> would change the meaning of "public". To tell whether something marked
>>> "public" is accessible statically from outside a module, a developer
>>> would still have to consult the file (in whatever format) in which the
>>> relevant package is declared to be exported (or not). "public" inside
>>> a module would still have a different meaning than "public" outside a
>>> module, and a different meaning from what "public" meant in Java 8.
>>
>> Not "will", "does". This is the current status quo. Types that are not
>> visible by a class loader cannot be linked against; this doesn't change
>> the definition of "public", which has been well-established since the
>> dawn of Java. Today I can instantiate public types and invoke public
>> methods on them regardless of what class loader they live in, yet I can
>> restrict linkage to such types, simply by arrangement of class loaders.
>> This is all possible and expected behavior today and I propose that
>> these concepts be leveraged, not changed.
>>
>>> So, do you want to preserve the meaning of "public", or not?
>>
>> Yes, I want to preserve it exactly with no changes to the JLS or JVM
>> specification.
>
> Okay, I think I'm starting to get it, but in your statement
>
>>>> The concept being established here is the
>>>> ability to have a public class which is not exported but is still
>>>> accessible to anyone.
>
> the clause "is not exported" suggests that some public types are
> "exported" and some are not. I read this to mean that public types would
> still be subject to some sort of access control based upon some kind of
> export declarations, but only for static linkage. If that's not the
> case, then what's the distinction between "not exported" and "exported"?
Right though I prefer the existing "visibility" term for this, as this
term is well over a decade old and reasonably well-understood at this point.
>>>> The second point is around module privacy. It is clear that
>>>> (language-wise) we need an access level that is module-private. Given
>>>> the relative uselessness of package-private accessibility in the module
>>>> world, I propose that any reference to a default-access-level member in
>>>> a Java 9 class should be considered to be a module-private access, not a
>>>> package-private access, in both javac and the JVM. The protected access
>>>> level should be similarly widened. ...
>>>
>>> If a Java 9 class is not inside a module, i.e., it's on the class path,
>>> then what is the default access mode? If it's package-private then it
>>> would work differently than from within a module. If it's private to the
>>> unnamed module in which all classes found on the class path are defined
>>> then it would work differently from any pre-9 classes on the class path.
>>> Either outcome is likely to confuse people.
>>
>> Class path private; see my other mail.
>
> Okay. Suppose that I upgrade a pre-9 component into a module using this
> approach, and ship it as a modular JAR file so that it can be used both
> as a module and on the class path. If it's used on the class path then
> any types that formerly were package-private are now accessible to all
> other code on the class path, regardless of whether that other code was
> compiled with 9 or with an earlier release, right?
Yes, potentially, if the class path is held as a single module (which it
could be, but does not have to be). There are pros and cons to each
approach though.
>>> If a Java 8 class is inside a module then I assume the default access
>>> mode would remain package-private. (We expect this to be common, with
>>> modular JAR files intended for use on the module path in 9 and on the
>>> class path in earlier releases.) Taken with the proposal in your first
>>> point then all public pre-9 classes would be available via the reflection
>>> APIs, regardless of whether their packages are exported. The only way to
>>> encapsulate such classes, therefore, would be to upgrade them to Java 9,
>>> at which point the modular JAR would no longer work on earlier releases.
>>> This limitation would add complexity to the migration story.
>>
>> It would add no complexity, because this is the status quo today.
>>
>> Public types are universally accessible today, and I do not propose that
>> to be changed. No application will be worse off security-wise, but they
>> will have a path to improve their encapsulation over time.
>
> Yes, they would have a migration path, but a very long one.
>
> The only way to encapsulate an existing public type within a module would
> be to change its access mode to the default and compile it -target 9. As
> a consequence the resulting class file would only be usable on Java 9 and
> later releases. The maintainer of an existing library would only be able
> to convert that library into a module after all existing users upgrade to
> Java 9. If history is any guide then, for a popular library, that's
> likely to be many years after Java 9 ships.
>
> Is that what we want?
I think so. I think users are looking at a long migration path either
way: either because everything is broken and in some cases must be
redesigned, or because there are new features that they can begin to
take advantage of (but are not compelled to do so).
I prefer the latter not only because the end user experience is
disrupted to a much lesser degree, but also because it is not clear that
the Jigsaw access rules can ever be accommodated by existing frameworks
like those in Java EE (including CDI and JPA) and other existing and
widely used libraries. On the other hand, by maintaining behavioral
compatibility but adding new security capabilities, it is far more
certain that these things will continue to work in both the short and
long term, while providing a useful path to increase security over time.
One other part of the appeal of this feature is that it can be done
completely independently of the implementation of Jigsaw or any other
module system, as the access level could apply to the class loader in
the same manner that package-private access does today, though this
based on the predicate that any later module implementation use class
loaders as its unit of run-time granularity.
In terms of the migration concerns you mention, there is no silver
bullet that I know of, but users do have a few reasonably good options
available:
* Use the new JDK multi-version JAR capabilities
* Take advantage of Maven's "classifier" functionality to provide
multiple artifacts for different JDK versions
Also I think it would not be too difficult to create some simple
third-party migration aid in the form of an annotation processor which
selectively translates certain of the user's Java 8 classes or packages
to Java 9 module-private, or vice-versa, using mutli-version JAR output.
This is probably what I would do for my various projects to get off
the ground quickly.
>>> At a higher level, I agree that the package-private access mode has
>>> proved to be of limited utility. It has, however, been a fundamental
>>> part of the language and the VM from the very beginning. To widen it in
>>> such an incompatible fashion now, after twenty years, would make it more
>>> difficult to migrate existing code to modules and would risk enabling
>>> further security vulnerabilities.
>>
>> I think this is objectively false. Existing code would continue to have
>> the same encapsulation; only new code would have the expanded capability
>> and only in reference to other new code.
>
> What about references from outside a module?
If class loaders are used as the representation of a module (for the
purposes of this discussion at least), then no code is outside a module,
which is highly appealing because it simplifies matters immensely.
> Would the default access mode on a Java 9 type mean "module private" if
> the code attempting to access that type is itself compiled -target 9, and
>"package private" otherwise?
I think references to Java 9 class members would have to mean "module
private" no matter who is accessing it, as this allows better piece-wise
migration to using the new access level (and from my examination and
understanding of the OpenJDK bytecode verifier implementation, I suspect
this would be simpler to implement as one only has to know what the
target class' version is, as opposed to applying a more complex rule on
both the source and target versions). In other words, a class of any
version accessing a Java 8 class file uses package-private, but a class
of any version accessing a Java 9 class file uses module-private.
In practice I can think of two places where this would be likely to apply:
* In multi-version JAR files, which were, by whatever means, compiled
such that a Java 8 class accesses a class which is public for Java 8 but
module-private for Java 9
* In the class path, if the class path is implemented as one module and
mixed JARs are present
In both cases it would seem to be beneficial to migration to allow the
legacy Java 8 code to take advantage of the Java 9 access level without
recompilation.
--
- DML
More information about the jpms-spec-experts
mailing list