This FTP is rejected
The problem of transmitting large messages between peers over FIDL is important, a very current concern faced by developers, and a current deficiency of FIDL. The combination of channel limits, the ease of defining messages which can surpass those limits depending on runtime behavior, along with the inherent difficulty to size messages (e.g. max out pagination) make this problem untractable in application code (at least, at scale). A solution must be provided by FIDL.
In short, FIDL needs to provide a way for library authors to eliminate the possibility of runtime surprises due to protocol limits (e.g. hard limits of number of bytes and handles in the case of Zircon channels), and FIDL developers need to be able to rely on bindings to implement protocols which may exchange large messages.
This FTP has many desirable ingredients, and will serve as a reference in the future. In particular:
Despite the above, this FTP is rejected for the following reasons:
box<T>
notation helps identify a place in a message where boxing can occur, i.e. it is a fine grained mechanism to indicate boxing behavior at an (almost) arbitrary place in a message. Instead, the current thinking is that providing an annotation at the method level is preferable, i.e. a coarser description mechanism, at use site rather than declaration site.Today, an approach taken to address this in applications uses the fuchsia.mem/Data
type, a union representing data either inline or in a VMO. Generalizing this approach to any request and/or response using syntactic sugar as is done for errors, along with bindings support, is the current top contender to move forward on this topic.
Field | Value |
---|---|
Status | Rejected |
Authors | jeffbrown@google.com |
Submitted | 2018-09-27 |
Reviewed | 2020-06-17 |
Introduce a mechanism for transmitting large structured data objects through an auxiliary VMO associated with a FIDL message.
There is an upper bound on the maximum size of FIDL message that can be transmitted between processes through a Zircon channel, as defined by ZX_CHANNEL_MAX_MSG_BYTES. At the time of this writing, the limit is 64 KB including FIDL headers.
Although the limit is adequate for many applications, it is occasionally necessary to transmit larger objects. This poses a challenge for FIDL API designers since they must design alternative means for transmitting those objects, such as:
fuchsia.mem/Buffer
.Failure to anticipate this issue is a major cause of runtime instability.
We believe that FIDL should offer a built-in, safe, statically verifiable, and efficient mechanism for transmitting large data objects.
A box is a container for a possibly large data object which may need to be transmitted out of band when the total message size (including headers) exceeds the limit for a Zircon channel[1].
A box only holds data objects; it cannot hold objects that have handles[2].
At design time, a FIDL protocol author..
At compile time, the FIDL compiler...
At compile time, each FIDL code generator...
At runtime, the FIDL encoder..
At runtime, the FIDL decoder..
We introduce a new built-in FIDL type, denoted box<T>
. T
designates the type of object to be put in a box.
T
must be a reference type that does not directly or indirectly contain handles.T
must not be a primitive type.T
may be an optional type.The new type box<T>
can be used wherever reference types such as structs, unions, vectors, and strings would be accepted.
box<string>
box<int>
box<vector<T>:100>
box<vector<T>>
vector<box<T>>:100
box<string:100>
box<string>
box<MyStruct>
box<MyStruct?>
box<MyStruct>?
interface Database { // OK 1: SelectTop(string:1000 query) -> (box<Record> record); // ERROR: reply may exceed message size limit // consider wrapping large objects in a box<>, // “Record” size is unbounded 2: BadSelectTop(string:1000 query) -> (Record record); // OK 3: SelectAll(string:1000 query) -> (box<vector<Record>> records); // ERROR: reply may exceed message size limit // consider wrapping large objects in a box<>, // “vector<Record>” size is unbounded 4: BadSelectAll(string:1000 query) -> (vector<Record> records); }; struct Record { string name; string address; };
(Section is incomplete.)
Idea 1: during depth-first traversal for serialization add all boxes encountered to a queue, once finished first pass pack boxed items in order following out-of-line objects until no more space remains then calculate size of remaining boxed objects, allocate a single VMO, and continue packing box contents from there
Idea 2: like Idea 1 but put each box into its own VMO, slightly simpler to implement but may be more limiting
Idea 3: maybe we should drop the idea of boxes altogether and do something at the level of a method instead, like an annotation, e.g. [Huge]
(Section is incomplete.)
(Section is incomplete.)
Idea 1: define “ensure not shared” flag, verify VMO has exactly one handle, shares no pages with other VMOs, and is not mapped, can pass this flag to zx_vmo_read/write/map, etc.
Idea 2: define a new syscall to check if VMO is unshared
Idea 3: make it really check to reverse-COW snapshot a VMO if it is already unshared (should be a no-op)
Idea 4: punt on VMOs and use Views instead
(Section is incomplete.)
(Section is incomplete.)
(Section is incomplete.)
In addition to providing a mechanism for transferring large data objects, boxes are also intended to address static safety concerns.
Currently, programs that attempt to transmit FIDL messages that exceed channel limits will fail at runtime leading to system instability. Once boxes are available, it should be feasible to introduce static message size enforcement in the FIDL compiler such that we can guarantee at compile time that no message will ever exceed channel limits; the excedent content can simply be moved into a box by the FIDL protocol author.
However, enabling static message size enforcement all at once may break existing code and hinder migration efforts.
We propose to resolve this problem as follows:
(Section is incomplete.)
By replacing existing ad-hoc mechanisms with an official solution supported by FIDL language bindings, we have an opportunity to improve overall security discipline.
For example, the FIDL language bindings can ensure that a VMO containing boxed data only has a single owner before attempting to access its content. This resolves common shared memory threats such as:
Conversely, introducing this feature may contribute to increased use of large messages and therefore increase the likelihood of other threats such as:
(Section is incomplete.)
(Section is incomplete.)
(Section is incomplete.)
Editor's note: Rejected FTP-005: Method Impossible which proposed banning all methods which could go beyond protocol limits. This is too constraining due to the limitation of static analysis which prevent capturing runtime behavior working to properly paginate messages, or manually ‘box’ them.
Hypothetically, FIDL could be transmitted over other channels for which boxing may take on a different nature. How that would be implemented is out of scope for this proposal.
Editor's note: Since this FTP was authored in 2018, the distinction between value types and resources is formally part of the FIDL language, see FTP-057.