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.