Struct fortanix_sgx_abi::Usercalls [−][src]
pub struct Usercalls;Expand description
The list of all usercalls.
This is not a real structure, it’s just a convenient way to group
functions with rustdoc.
The usercall number is passed in the first register. Up to 4 arguments may be passed in the remaining registers. Unspecified arguments and return values must be 0. Userspace must check the arguments and the enclave must check the return value.
The usercall number may be one of the predefined numbers associated with
one of the usercalls defined below, or, if bit
USERCALL_USER_DEFINED is set, an
otherwise arbitrary number with an application-defined meaning.
Raw pointers must always point to user memory. When receiving raw pointers from userspace, the enclave must verify that the entire pointed-to memory space is outside the enclave memory range. It must then copy all data in user memory to enclave memory before operating on it.
Implementations
Asynchronous usercalls
Due to rustdoc, this section may appear at the top of the
Usercalls documentation. You might want to read the other sections
first and then come back to this one.
See also the async module documentation.
pub fn async_queues(
usercall_queue: *mut FifoDescriptor<Usercall>,
return_queue: *mut FifoDescriptor<Return>
) -> Result
pub fn async_queues(
usercall_queue: *mut FifoDescriptor<Usercall>,
return_queue: *mut FifoDescriptor<Return>
) -> Result
Request FIFO queues for asynchronous usercalls. usercall_queue
and return_queue must point to valid user memory with the correct
size and alignment for their types. On return, userspace will have
filled these structures with information about the queues. A single
set of queues will be allocated per enclave. Once this usercall has
returned succesfully, calling this usercall again is equivalent to
calling exit(true).
May fail if the platform does not support asynchronous usercalls.
The enclave must ensure that the data pointed to in the fields of
FifoDescriptor is outside the enclave.
Streams
The enclave must not assume anything about data read or written using these
calls. Data written may be piped directly to /dev/null by userspace, or
it may be published in the local newspaper. Similarly, data read may be
arbitrarily deleted, inserted, or changed. The enclave must use additional
security primitives such as sealing or TLS to obtain stronger guarantees.
When a stream is read from by multiple threads simultaneously, the read calls may be serialized in any order. This means the data returned by a read call appeared that way in the stream, and every single byte in the stream will be read and be read only once. However, the order in which all stream data is returned is not defined. The same applies when simultaneously submitting multiple read calls for the same stream asynchronously. This all applies similarly to writing to a stream.
To make sure to be able to re-assemble the stream, the enclave can take one of the following approaches:
- Submit all read calls to a single stream on a single thread.
- Serializing read calls by synchronizing access to a single stream.
In addition, the enclave should use cryptographic integrity protection of the stream data to ensure the stream data has not been tampered with.
Read up to len bytes from stream fd.
buf must point to a buffer in userspace with a size of at least
len. On a succesful return, the number of bytes written is returned.
The enclave must check that the returned length is no more than len.
If len is 0, this call should block until the stream is ready for
reading. If len is 0 or end of stream is reached, 0 may be
returned.
The enclave may mix calls to read and
read_alloc.
Read some data from stream fd, letting the callee choose the amount.
buf must point to a ByteBuffer in userspace, and buf.data must
contain null. On success, userspace will allocate memory for the read
data and populate ByteBuffer appropriately. The enclave must handle
and deallocate the buffer according to the ByteBuffer documentation.
Since every read operation using this usercall requires two usercalls, it is recommended to only call this usercall asynchronously.
The enclave may mix calls to read and
read_alloc.
Write up to len bytes to stream fd.
buf must point to a buffer in userspace with a size of at least
len. On a succesful return, the number of bytes written is returned.
The enclave must check that the returned length is no more than len.
If len is 0, this call should block until the stream is ready for
writing. If len is 0 or the stream is closed, 0 may be returned.
Flush stream fd, ensuring that all intermediately buffered contents
reach their destination.
Networking
In keeping with the design goals for this specification, the
networking/socket interface doesn’t use sockaddr types and doesn’t
have a separate API for name resolution. Userspace can’t be trusted to
do name resolution correctly, and even if it did, userspace can’t be
trusted to actually connect streams to the correct address specified by
the enclave. Therefore, addresses specified should merely be treated as a
suggestion, and additional measures must be taken by an enclave to verify
the stream is connected to the correct peer, e.g. TLS.
The networking API works with strings as addresses. All byte buffers representing network addresses should contain a valid UTF-8 string. The enclave should panic if it is passed an invalid string by userspace. It is suggested that userspace supports at least the following notations:
hostname:port-number(e.g.example.com:123)dotted-octet-ipv4-address:port-number(e.g.192.0.2.1:123)[ipv6-address]:port-number(e.g.[2001:db8::1]:123)
Additionally, other forms may be accepted, for example service names:
fully-qualified-service-name(e.g._example._tcp.example.com)address:service-name(e.g.address:example)
Errors
Networking calls taking an address may return the InvalidInput error if
the address could not be interpreted by userspace.
Setup a listening socket.
The socket is bound to the address specified in addr. addr must be
a buffer in user memory with a size of at least len.
On success, a file descriptor is returned which may be passed to
accept_stream or close.
The enclave may optionally request the local socket address be returned
in local_addr. On success, if local_addr is not NULL, userspace
will allocate memory for the address and populate ByteBuffer
appropriately. The enclave must handle and deallocate the buffer
according to the ByteBuffer documentation.
The enclave must not make any security decisions based on the local address received.
pub fn accept_stream(
fd: Fd,
local_addr: *mut ByteBuffer,
peer_addr: *mut ByteBuffer
) -> (Result, Fd)
pub fn accept_stream(
fd: Fd,
local_addr: *mut ByteBuffer,
peer_addr: *mut ByteBuffer
) -> (Result, Fd)
Accept a new connection from a listening socket.
fd should be a file descriptor previously returned from
bind_stream.
The enclave may optionally request the local or peer socket addresses
be returned in local_addr or peer_addr, respectively. On success,
if local_addr and/or peer_addr is not NULL, userspace will allocate
memory for the address and populate the respective ByteBuffer
appropriately.
The enclave must handle and deallocate each buffer according to the
ByteBuffer documentation.
The enclave must not make any security decisions based on the local or peer address received.
pub fn connect_stream(
addr: *const u8,
len: usize,
local_addr: *mut ByteBuffer,
peer_addr: *mut ByteBuffer
) -> (Result, Fd)
pub fn connect_stream(
addr: *const u8,
len: usize,
local_addr: *mut ByteBuffer,
peer_addr: *mut ByteBuffer
) -> (Result, Fd)
Create a new stream connection to the specified address.
The enclave may optionally request the local or peer socket addresses
be returned in local_addr or peer_addr, respectively. On success,
if local_addr and/or peer_addr is not NULL, userspace will allocate
memory for the address and populate the respective ByteBuffer
appropriately.
The enclave must handle and deallocate each buffer according to the
ByteBuffer documentation.
The enclave must not make any security decisions based on the local or peer address received.
Execution control
TCS event queues
Userspace will maintain a queue for each running TCS with events to be
delivered. Each event is characterized by a bitset with at least one bit
set. Userspace or the enclave (using the send usercall) can put events on
this queue.
If the enclave isn’t waiting for an event when an event is queued, the event
remains on the queue until it delivered to the enclave in a later wait
usercall. If an enclave is waiting for an event, and the queue contains an
event that is a subset of the waited-for event mask, that event is removed
from the queue and execution control is returned to the enclave.
Events not defined in this specification should not be generated.
In executables, this will instruct
userspace to enter another TCS in another thread. This TCS should have
the thread_entry entrypoint. As documented in thread_entry, the
enclave should keep track of how many threads it launched and reconcile
this with the number of entries into thread_entry. If no free TCSes
are immediately available, this may return an error.
This function will never be succesful in libraries. See the
library documentation on how to use threads with libraries.
Signals to userspace that this enclave needs to be destroyed.
The enclave must not rely on userspace to terminate other threads still running. Similarly, the enclave must not trust that it will no longer be entered by userspace, and it must safeguard against that in the entrypoints.
If panic is set to true, the enclave has exited due to a panic
condition. If the enclave was running in debug mode, the enclave may
have output a debug message according to the calling convention.
Wait for an event to occur, or check if an event is currently pending.
timeout must be WAIT_NO or WAIT_INDEFINITE or a positive
value smaller than u64::MAX specifying number of nanoseconds to wait.
If timeout is WAIT_INDEFINITE, this call will block and return
once a matching event is queued on this TCS. If timeout is
WAIT_NO, this call will return immediately, and the return value
will indicate if an event was pending. If it was, it has been dequeued.
If not, the WouldBlock error value will be returned. If timeout
is a value other than WAIT_NO and WAIT_INDEFINITE, this call
will block until either a matching event is queued in which case the
return value will indicate the event, or the timeout is reached in
which case the TimedOut error value will be returned.
A matching event is one whose bits are equal to or a subset of
event_mask. If event_mask is 0, this call will never return due
to an event. If timeout is also WAIT_INDEFINITE, this call will
simply never return.
Enclaves must not assume that this call only returns in response to
valid events generated by the enclave. This call may return for invalid
event sets, or before timeout has expired even though no event is
pending.
When executed synchronously, this gives userspace an opportunity to schedule something else in a cooperative multitasking environment.
When executed asynchronously, this may trigger an
EV_RETURNQ_NOT_EMPTY event on this or other TCSes. It is not
recommended to execute this call asynchronously with a timeout value
other than WAIT_NO.
Send an event to one or all TCSes.
If tcs is None, send the event event_set to all TCSes of this
enclave, otherwise, send it to the TCS specified in tcs.
Error
This will return the InvalidInput error if tcs is set but doesn’t
specify a valid TCS address.
This returns the number of nanoseconds since midnight UTC on January 1, 1970. The enclave must not rely on the accuracy of this time for security purposes, such as checking credential expiry or preventing rollback.
Memory
The enclave must not use any memory outside the enclave, except for memory
explicitly returned from usercalls. You can obtain arbitrary memory in
userspace using alloc.
Request user memory.
Request an allocation in user memory of size size and with alignment
align. If succesful, a pointer to this memory will be returned. The
enclave must check the pointer is correctly aligned and that the entire
range of memory pointed to is outside the enclave.
It is an error to call this function with size equal to 0.
Free user memory.
This must be called to deallocate memory in userspace. The pointer
ptr must have previously been returned by a usercall. The size and
alignment specified must exactly match what was allocated. This
function must be called exactly once for each user memory buffer.
Calling this function with size equal to 0 is a no-op.