My position on module security/access control
David M. Lloyd
david.lloyd at redhat.com
Thu Dec 17 17:12:00 UTC 2015
On 12/17/2015 10:27 AM, mark.reinhold at oracle.com wrote:
> 2015/12/15 8:51 -0800, david.lloyd at redhat.com:
>> 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:
>> ...
>>
>>> 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.
>
> Just to confirm: In your preferred approach an exported public type would
> be made visible to class loaders other than its own for the purpose of
> static linkage, but a non-exported public type would not, and whether a
> public type is exported would have no bearing upon the access-control
> decisions for that type made by both the VM and the reflection APIs.
>
> Is that right?
That is correct.
>>>>> 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 all classes on the class path are not loaded into a single module then
> what other approach do you have in mind?
Another option is to load each artifact into its own class loader, tying
each one to each other one. Or some hybrid approach wherein some
artifacts are joined, and some are not. I prefer the single-module
approach though, as that most closely mirrors existing behavior. But
see below...
>>>>> ...
>>>>>
>>>>> 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.
>
> Of course -- I was asking about references from other modules.
Ah, apologies, I interpreted the question differently.
I can't envision references from other modules having any access to
module private members at all, unless a friend mechanism were introduced
and was in use. But, see below...
>>> 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.
>
> What about existing source code that's recompiled -target 9?
>
> Suppose, e.g., that I maintain a library that dynamically generates
> classes whose bytecodes depend upon user input (say, an XSLT compiler),
> and that I load those classes into the same class loader as my library
> (arguably a bad practice, but I've seen this before). The synthesized
> classes are in their own special package, rather than in one of my
> library's packages, so today they cannot access any package-private
> members of the library itself. If I recompile my library -target 9
> then that will no longer be true.
>
> Do we really want the simple act of recompiling old code to open the
> door to new security vulnerabilities?
I see your point; the recompilation case is definitely problematic, and
I know of no way to mitigate this. Any approach which reduces security
compared to the status quo is probably going to be ultimately
unacceptable, at least barring some very good/clever mitigation strategy.
Another variation on the idea that preserves most of the strengths of
this plan is to keep access levels as-is, but enable a "friend package"
mechanism that operates at the package level. Internally (at run time),
each package determines which set of classloader+package names have
access, and this is checked by the class loader or bytecode verifier, or
by some combination of the two. At compile time friends may to be
declared somehow, especially the special/probably common case of
internal-to-a-module friends (this is something that would have to be
worked out, obviously, but it doesn't seem insurmountable to me), but
the important part of the idea is remaining compatible with existing
reflective code while at the same time increasing the usefulness of
package-level access controls. This seems somewhat similar to the
current Jigsaw approach in concept, establishing trust relationships
between modules, but without making public types globally unavailable.
Overall I'm hoping there is a possible and practical strategy which
involves (perhaps optionally) opening up non-public levels, rather than
non-optionally restricting public levels, with a view towards
compatibility and flexibility - and also does not introduce the second
dimension of access checking by visibility, for the same reasons.
--
- DML
More information about the jpms-spec-experts
mailing list