My position on module security/access control

David M. Lloyd david.lloyd at redhat.com
Thu Dec 10 16:00:18 UTC 2015


On 12/09/2015 07:29 PM, mark.reinhold at oracle.com wrote:
> 2015/11/18 8:10 -0800, david.lloyd at redhat.com:
>> I wanted to summarize my position on module security and access control,
>> so that all my thoughts are in one place instead of scattered across
>> various threads.
>>
>> The first point I want to make is that I think that the idea of having
>> public classes that are not public is a red flag.  If a class and member
>> is public, it should always be accessible to everyone regardless of who
>> imports what module.  There should only be one access control mechanism
>> in the language.  Users already seem keen to find ways to break out of
>> the Jigsaw export-based restrictions and I think this is an indicator
>> that, while expedient, the Jigsaw approach is far from ideal.  In
>> addition I think this is an essential step towards the ultimate goal of
>> removing the dreaded setAccessible() method; if frameworks cannot even
>> access public members then they will never be able break away from using
>> reflection as a back door.  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.

> Your proposal would, further, introduce an asymmetry between access
> control in the language and the VM vs. access control in the reflection
> APIs.  With the exception of escape hatches such as setAccessible, the
> reflection APIs have always worked in exactly the same way as the
> corresponding static constructs.

It introduces nothing - this is how it works today.  Visibility is an 
existing concept that is useful and used.  Accessibility is a distinct 
existing concept that is also useful and used.

> Your proposal would address the issue of making it easier for frameworks
> to migrate to modules, but it is not the only approach to that problem
> (about which more later).

Of course; my position is to make the minimum set of changes (code and 
concept) that meet the requirements, which I believe is a substantially 
smaller set of changes than what is presently on the table.  This gives 
us a somewhat objective way to measure the quality of the implementation.

>> 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.

> 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.

> 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.

> The design as proposed, by contrast, narrows rather than widens an
> existing access mode (public), so security is not an issue, and it
> narrows that mode in a way that's relatively easy to explain -- just
> consult the relevant module declaration.

Yes, but this then will break many things.  Why not allow application 
developers to do this themselves, over time?

>> The third point is around "friend" modules.  It seems clear that we have
>> a requirement (which can be extrapolated easily from the document) that
>> some modules need the ability to provide selective access to nonpublic
>> members to other modules.  ...
>
> There's no need to extrapolate, since we captured this requirement
> explicitly [1].
>
> (I don't see a specific proposal here, so I have no further comments
>   on this point.)

See other thread.

>> The fourth point relates to ServiceLoader.  If ServiceLoader has a
>> unique and special blessing to bypass the access mechanism, then we've
>> failed to design an adequately flexible (and secure) access control
>> mechanism.  By implementing the three points above, ServiceLoader no
>> longer needs to be a special citizen, because all service implementation
>> classes are public (as they are today, and as they should be).  A user
>> could implement ServiceLoader in unprivileged code and it would work
>> with no special trapdoors.
>
> I agree that, as a proof point, it'd be nice to implement ServiceLoader
> using only reflective operations that are also available to non-system
> modules.  ServiceLoader is, after all, just a small kind of framework.
>
> Services are bound and provisioned by the module system itself, however,
> so ServiceLoader does, of necessity, have some special privileges, and I
> don't think that can be avoided.

To me this is just a convenience feature of the module API: the ability 
to locate and instantiate a "global" set of services, the accomplishment 
of which is simply a feature of the thing doing the module loading.  One 
simple realization of this is to use the set of services in modules 
visible to the started application module, which allows each application 
to have its own set of global services; using this strategy, global 
services can be found simply by using the application module class 
loader.  The only real involvement of the service loader with this 
strategy is the use of privilege to get that class loader; otherwise 
(using my proposed approach) there is no special case here.  It's just a 
convenience API for users.

-- 
- DML


More information about the jpms-spec-experts mailing list