User model stacking: current status

Dan Heidinga heidinga at redhat.com
Wed Jun 15 14:57:49 UTC 2022


On Tue, Jun 14, 2022 at 3:18 PM Brian Goetz <brian.goetz at oracle.com> wrote:
>
>
>
> On 6/14/2022 2:54 PM, Dan Heidinga wrote:
>
> But (and there's a whole conversation to be had here) it does mean that
> there is separate access control on LFoo vs QFoo,
>
> Pulling on this thread a little, is it the class that has different
> access control or something else?
>
>
> We've meandered a bit over the years on the distinction between the class Foo, the types Foo.ref and Foo.val, and their respective mirrors.  It's probably time for a check-in on where we are there.
>
> Today, Integer is a class, with a full-power mirror Integer.class; int is a type, with a limited mirror int.class, whose job is mostly limited to reflecting over field and method descriptors.
>
> With Valhalla, Point is a class, with types Point.ref and Point.val; Point.class is a full-power mirror for Point.ref, and Point.val.class is a limited mirror that is analogous to the int mirror.  If you ask a Point for its getClass(), it always returns Point.class.  It would not bother me if the Point.val.class mirror is fully limited, and the only way to do lookups (e.g., getMethods) is to switch over to the class mirror (for which we'd probably have a `Class::getPrimaryClass` method.)

That approach seems reasonable for Reflection. For MethodHandles, I
think we'll need to support MethodHandles.Lookup with both the L & Q
version to correctly type the receiver argument, at least for virtual
calls.

>
> Having the two encode separate accessibilities sounds a little messy, but the various Lookup::checkAccess methods key off of a Class, so that seems a reasonable place to hang this information.  I would assume such checks would check both the primary class and then the secondary class, or we'd arrange that the primary mirror always was at least as accessible as the secondary mirror.  (Protected isn't a useful option, and public/package/private are suitably ordered.)
>
> At the language level, there is a question about how to present the val class.  One obvious (but possibly bad) idea is to pretend it is a special kind of nested class (which is supported by the Point.val naming convention):
>
>     value class Rational {
>         private class val { }
>     }
>
> This is a little cheesy, but may fit mostly cleanly into user's mental models.   In this case, the accessibility is going on something that looks like a class declaration at the source level, and which has a class mirror at the VM level, but it really a "shadow class", just like the class described by int.class (or String[].class.)  This might be OK.
>
> At one point I considered whether we could hang this on the accessibility of the no-arg constructor, but I quickly soured on this idea.  But that has an accessibility too.
>
> Or we could invent a new kind of member, to describe the val projection, and hang accessibility on that.
>
> To create an identity object, we do access control on the both class
> (public/package) and the constructor (public/package/private (aka
> nest)).
> To create a value object, we do nest mate access control (aka private)
> on the bytecodes that create values (aconst_init / withfield).  This
> proposal extends the nest mates access check to the default values of
> Qs.
>
>
> It's not just nestmate access control; it would be reasonable to declare the val as package-access, and trust your package mates too.

At the VM-level, classes are either public or package.  While the
source code also allows specifying private or protected for
innerclasses, the VM doesn't use those bits for access control (though
Reflection sometimes does).  That's part of why I'm reaching for
something else to hang the private bit on (and thus trigger the nest
check).

I don't think we want to teach the VM to use the inner class flags for
*some* access checks as it will be easy to confuse when each set of
flags is used (long bug tail) and will lead to inconsistencies with
existing programs.

Without some extra "thing" to hang the accessibility bits on, I don't
think we can express public / package / private (nest) in the existing
public/package bits used for classes at the VM level.

>
> In both cases, we're looking at the access control of two things - the
> class and the "creator of instances".  Are we applying different
> access control to LFoo vs QFoo, or to construction mechanisms?
>
>
> The thing we're trying to protect is creation of uninitialized heap-based instances.  But it felt a little weird to draw such a complex line; seemed simpler (and not giving up much) to access-control the type name.  But we can explore this some more.  Maybe we are access-controlling the `defaultvalue` bytecode, since its effectively public if someone can create a flat array.
>

I think the latter is where we'll need to end up - it's the ability to
create a defaultvalue and leak the zeros that we're protecting
against.

--Dan



More information about the valhalla-spec-observers mailing list