PSA: fuchsia.mem/Buffer is deprecated, use a VMO in new APIs instead

9 views
Skip to first unread message

James Robinson

unread,
Oct 4, 2021, 2:45:13 PM10/4/21
to api-c...@fuchsia.dev
tl,dr: Use zx.handle:VMO instead of fuchsia.mem/Buffer in new protocols, consider replacing fuchsia.mem/Buffer with zx.handle:VMO in existing protocols as convenient.

# Background

Many protocols involve transferring an amount of data that may exceed the capacity of a single channel message. A good way to achieve this is to create a Virtual Memory Object (VMO), place the data in this object, and transfer a handle to this object in a message. The receiver can then read the data from the VMO through zx_vmo_read(), mapping the memory, or similar means. This technique also requires telling the receiver how many bytes are in the received data as VMOs are allocated units of system pages. In the olden days, we did not have a mechanism in the Zircon kernel to keep track of the amount of data in an VMO, only the number of physical pages allocated. In this time we invented the fuchsia.mem/Buffer FIDL type as a VMO + uint64 pair. Since then, we've added the ZX_PROP_VMO_CONTENT_SIZE property to VMOs to track this. The FIDL rubric and fidldoc for fuchsia.mem have been updated with this recommendation. Use the tracking issue https://fxbug.dev/85452 for any issues or tasks related to this.

In addition to being simpler, using the zx.handle:VMO type also permits specifying the specific rights that should be associated with the object in each FIDL protocol. Please review the "gotchas" below before deciding on a specific rights mask.

# How to use

## Reading

The content size of a VMO can be queried by reading the ZX_PROP_VMO_CONTENT_SIZE from the VMO object:

uint64_t content_size;
status = zx_object_get_property(vmo_handle, ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size));

Or using the language-specific wrappers. zx::vmo provides a get_prop_content_size(uint64_t*) helper for this purpose.  Then, read the data.

Another option is to use a stream to read the data. A stream_readv operation will not read past the content size of a VMO.

## Writing

A VMO will remember the size used at creation time in the ZX_PROP_VMO_CONTENT_SIZE property even if that value is not a multiple of the system page granularity. Thus to create a VMO to hold a certain amount of data simply pass the content size of the data to zx_vmo_create().

If the content size is not known at the time of VMO creation and the VMO is resizable, call zx_vmo_set_size() with the desired content size. This will set the ZX_PROP_VMO_CONTENT_SIZE property to the specified value and resize the VMO to the requested size rounded up as needed.

If the content size is not known at the time of VMO creation and the VMO is not resizable, set the ZX_PROP_VMO_CONTENT_SIZE using zx_object_set_property() or a wrapper like zx::vmo::set_prop_content_size() once the size is known.

Another option is to use a stream object to write data. A stream_writev operation will update the content size property if the written data exceeds the current size.

# Gotchas

## Rights
Today, reading or writing the content size property directly on a VMO requires the ZX_RIGHT_GET_PROPERTY and ZX_RIGHT_SET_PROPERTY writes, respectively. Manipulating or accessing the value (indirectly) through a stream object requires only ZX_RIGHT_READ or ZX_RIGHT_WRITE on the VMO object. If you are writing a specific rights mask on a FIDL protocol or carefully validating the set of rights provided keep this mismatch in mind. The discrepancy is odd - we may want to adjust the syscall interface to smooth this out, for example by adding operations that need only _READ / _WRITE to get / set the content size.

## Transitioning from fuchsia.mem/Buffer

When considering logic using fuchsia.mem/Buffer or transitioning it to use a VMO, keep in mind that the size recorded in the fuchsia.mem/Buffer.size field may differ from the content size property on the contained VMO. This can happen if a caller is not careful when setting up a fuchsia.mem/Buffer or if a caller is malicious and trying to confuse the receiver. Be careful when validating data or allocating buffers for data in this object to use the canonical size. It's probably wise to treat a mismatch in size as a validation error.

# fuchsia.mem/Range

Another related type to consider is the fuchsia.mem/Range type. This type can be used to refer to a subset of data within a VMO and is useful in protocols where data exists in an underlying allocation and avoiding copies is important. Keep in mind that the range provided is advisory to the receiver - they still have access to the full VMO.

Thanks,
- James
Reply all
Reply to author
Forward
0 new messages