[foreign] RFR 8218772: Limit struct member pointers to size of the field
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Feb 13 16:57:40 UTC 2019
Hi Jorn,
I was looking at something very related to this - e.g. relationship
between pointers and arrays, and, in general bulk-write operations for
structs and arrays (with Pointer::set) and I thought I might add
something to this discussion, to see if the issues that you are running
into are just bugs, or symptoms of something deeper.
Over the last few weeks I've been toying with the idea of merging Array
and Pointers - after all a BoundedPointer is expressive enough to
represent both. We could e.g. setup an approach like the one below:
- Pointer has array-like accessors Pointer::get(long),
Pointer::set(long, X)
- regular accessor defaults to zero offset - that is, Pointer::get() ->
Pointer::get(0)
- jextract does NOT generate setters for array struct fields, or array
global variables - only getters
- an API is provided (well, one exists already) to do bulk copy between
different arrays/structs
While I like the unification this brings about (only one abstraction
instead of two, Pointer and Array), and I also like the fact that we
move towards a model where Pointer::get, Pointer::set are O(1)
operations (with bulk operations left to higher level APIs), there is
something that doesn't 100% convinces me: if we go down this path, the
following layouts:
u64:u64:i32 (pointer to pointer to int)
and
u64:[5 i32] (pointer to array of int)
will effectively have the same carrier type:
Pointer<Pointer<Integer>>
The difference will be visible only upon closer inspection: we could
call Pointer::type() obtain a LayoutType, then do LayoutType::layout()
and see whether the pointee layout is a sequence or an address.
Now, when we perform a get() on such a pointer, we can, given the
layout, construct the right pointer with the right size info (if any).
But what about set() ? I see different options here, none of which seems
particularly satisfying:
1) Pointer::set should throw if you are trying to set an array-like
pointer (which would require bulk copy)
2) Pointer::set will silently perform bulk copy of the incoming pointer
into the pointed region
Of these, (2) is similar to what we have now, whereas (1) would be a
stricter variant.
The thing that puzzles me is that, looking at the code it will be
absolutely impossible to understand what's going on - e.g. if native
arrays and native pointers map to the same Java carrier (Pointer) it
then becomes super hard to explain/understand/predict why a given
operation (e.g. Pointer::set) resulted in bulk copy/exception.
One of the principles I've been trying to adhere to when designing the
foreign API is to avoid surprises - e.g. the semantics of a given
operation should be clear from the carrier types involved in the
operation. In that respect, maybe a pointer with an optional length() is
tolerable, but having Pointer::set behaving differently depending on the
result of Pointer::type() is that acceptable or too subtle?
This seems to suggest that the current Pointer vs. Array split carries
some weight. If carrier type are different then it's easy to see for the
user which operation might or might not be supported (or might have
bulk-logic).
<sidebar>
On the other hand, one might argue that this is already happening with
pointers to incomplete objects - if I have a Pointer<?> and I call get()
I don't know if I'll get an exception or not. It again depends on
whether the layout complete or not - e.g. if it's the void layout or, if
it's partial layout (because it refers to an unresolved struct) an
exception will be thrown.
</sidebar>
If we do retain Pointer vs. Array then I think we are free to decide
whether for Pointer::set we want (1) or (2) - that is, when we have
Pointer<Array<Integer>>
or
Pointer<StructFoo>
should Pointer::set do bulk copy, or should it throw? If the latter,
should jextract even emit setters for struct fields of type
array/struct? (or, perhaps, emulate such setters by performing explicit
copy, rather than by using Pointer::set internally).
These are all questions that are relevant, I believe, to the fix you
brought up - I'm of course fine with the fix, but I'd like to also
understand whether the bulk-copy on Pointer::set is one magic trick too
far and also, more generally, what do Panama-land feels about the Array
vs. Pointer split/lump.
Feedback welcome.
Cheers
Maurizio
On 13/02/2019 15:47, Jorn Vernee wrote:
> Hi,
>
> I found a bug where it was possible to overwrite trailing fields of a
> struct by writing an oversized array to a previous array field (see
> bug). Overwriting is also possible in other cases by forcing an
> oversized write to a struct field. This can be fixed (relatively
> easily) by limiting the size of memory regions of pointers to struct
> members to the size of their fields.
>
> Please review the following.
>
> Bug: https://bugs.openjdk.java.net/browse/JDK-8218772
> Webrev:
> http://cr.openjdk.java.net/~jvernee/panama/webrevs/8218772/webrev.00/
>
> Thanks,
> Jorn
More information about the panama-dev
mailing list