📜 ⬆️ ⬇️

Intel SGX Extensions Tutorial. Part 7, revision of the enclave

In the seventh part of a series of educational materials on the Intel Software Guard Extensions (Intel SGX) extensions , we will return to work with the enclave and modify it a little to make it easier and more efficient. We will look at how proxy functions transfer data between an unprotected memory area and an enclave, and also talk about one of the advanced features of the Enclave Definition Language (EDL) syntax.



Source code is provided with this part of the series. In this part, we migrated the application to the Intel SGX SDK version 1.7, and also use Microsoft Visual Studio * Professional 2015 as the development environment.

Proxy functions


When creating an enclave using the Intel SGX SDK, the interface to the enclave is specified in EDL. The EDL indicates which functions are enclave calls (ECALL, enclave functions) and which are external calls (OCALL, calls directed from inside the enclave to untrusted functions).
')
When building a project, the Edger8r tool, included in the Intel SGX SDK, analyzes the EDL file and creates a sequence of proxy functions. These proxy functions are wrappers around real functions defined in the EDL language. Each call to ECALL and OCALL receives a pair of proxy functions: the trusted half and the untrusted half. The trusted functions are placed in EnclaveProject_t.h and EnclaveProjct_t.c and added to the Autogenerated Files folder of the enclave project. Untrusted functions are placed in EnclaveProject_u.h and EnclaveProject_u.c and added to the project's Autogenerated Files folder, which will interact with the enclave.

Your program does not call ECALL and OCALL functions directly, it calls proxy functions. If you need to call ECALL, you should call an untrusted proxy function for this ECALL, which, in turn, calls the trusted proxy function inside the enclave. Then this proxy function calls the “real” ECALL function, and the returned value is passed to the untrusted function. This sequence is shown in Fig. 1. If you want to call OCALL, the reverse sequence is used: you should call the trusted proxy function for OCALL, which calls the untrusted proxy function outside the enclave, which, in turn, calls the "real" OCALL function.


Figure 1. Proxy functions for ECALL

Proxy functions are responsible for performing the following actions.

It follows that each ECALL or OCALL may have two return values. The first is the success of the call to the ECALL or OCALL function itself (that is, we were able to successfully enter or exit the enclave), the second is the return value of the function that is called inside ECALL or OCALL.
The syntax of the functions ECALL ve_lock () and ve_unlock () in the EDL language in the enclave of our Tutorial Password Manager application is shown below:

enclave { trusted { public void ve_lock (); public int ve_unlock ([in, string] char *password); } } 

But the prototypes of untrusted proxy functions created by the Edger8r tool.

 sgx_status_t ve_lock(sgx_enclave_id_t eid); sgx_status_t ve_unlock(sgx_enclave_id_t eid, int* retval, char* password); 

Note that additional arguments have been added to the parameter list of each function, and functions now return the type sgx_status_t .

Both proxy functions require an enclave identifier, which is transmitted with the first eid parameter. The ve_lock () function has no parameters and does not return a value, so no further changes are required. The ve_unlock () function, by contrast, uses parameters and returns a value. The second argument of the proxy function is a pointer to the address where the value returned by the present function ve_unlock () in the enclave will be stored. In our case, an int is returned. Then follows the parameter of the present function char * password.

Data transfer


The untrusted part of the application does not have access to the memory of the enclave. It cannot read and write data to these protected memory pages. Because of this, certain difficulties arise when the parameters of functions include pointers. Most of the problems are related to OCALL, since the memory allocated inside the enclave is not available for OCALL. However, there may be difficulties with ECALL calls. The memory of the enclave is allocated in the application's memory space; therefore, the memory pages of the enclave may be adjacent to unprotected memory pages. If you pass into the enclave a pointer to an untrusted area of ​​memory, and then do not perform proper checking of the boundaries in the enclave, you can inadvertently cross the enclave border while reading or writing to the memory in the ECALL function.

To solve this problem, the Intel SGX SDK suggests copying the contents of the data buffers to and from enclaves, and the ECALL and OCALL functions should work with these copies of the original memory buffers. When transmitting a pointer to an enclave, you must specify in the EDL language which direction the buffer to which the pointer refers is sent: to the call, from the call or in both directions; then the buffer size is indicated. The proxy functions created in the Edger8r program use this information to verify that the specified address range does not intersect with the enclave boundary; copy the data to the enclave or from the enclave according to the indicated direction, and then replace the original pointer with a pointer to a copy of the buffer.

This is a slow but safe approach to transferring data and pointers between unprotected memory and enclave memory. However, this approach has certain disadvantages, due to which in some cases it becomes undesirable.

In addition, there are cases where you only need to pass the raw pointer to ECALL and to OCALL without using it inside the enclave, for example, if you pass the pointer to the callback function directly to OCALL. In this case, as such, there is no data buffer; there is only the address of the pointer itself, and the data transfer functions created in Edger8r will prevent them.

Solution: user_check


The good news is that the EDL language supports the transfer of the pointer address to ECALL or to OCALL without checking the boundaries and without copying the data buffer. If you specify the user_check parameter, the Edger8r program passes the pointer without any additional actions, based on the fact that the developer himself took care of checking the boundaries of the address. By specifying user_check , you essentially increase performance by reducing some of the security.

The pointer with the user_check parameter has no direction ( in or out ), because the buffer does not copy. If you specify both user_check and in or out , an error will occur during compilation. Also, the count and size parameters are not specified.

In the Tutorial Password Manager application, the user_check parameter is best used in ECALL functions that load and store encrypted password stores. Our project limits set the limit of the storage itself, but in general such mass read and write operations greatly benefit in speed if the enclave is allowed to work directly with untrusted memory.

The original EDL syntax of ve_load_vault () and ve_get_vault () functions looks like this.

 public int ve_load_vault ([in, count=len] unsigned char *edata, uint32_t len); public int ve_get_vault ([out, count=len] unsigned char *edata, uint32_t len); 

If to rewrite them, having specified user_check , the following will turn out.

 public int ve_load_vault ([user_check] unsigned char *edata); public int ve_get_vault ([user_check] unsigned char *edata, uint32_t len); 

Note that the len parameter is removed from ve_load_vault () . If we recall the fourth part of this series of articles, there was a problem with this function: despite the fact that the storage length was stored as a variable in the enclave, the proxy functions did not have access to it. In order for ECALL's proxy functions to copy the incoming data buffer, we needed to specify the length in the EDL in order for the Edger8r program to receive information about the buffer size. If you use the user_check parameter, then this problem disappears because there is no buffer copy operation. The enclave can read data directly from untrusted memory and can use its own internal variable to determine how many bytes need to be read.

At the same time, we still specify the length as a parameter to the ve_get_vault () function. This is a security check, its purpose is to avoid accidental buffer overflow when receiving encrypted storage from the enclave.

Let's sum up


EDL has three options for passing pointers to ECALL or OCALL: in , out, and user_check . They are described in table 1.
Qualifier / DirectionECALLOCALL
inThe buffer is copied from the application to the enclave. Changes will only affect the buffer inside the enclave.The buffer is copied from the enclave to the application. Changes will only affect the buffer outside the enclave.
outThe buffer will be allocated inside the enclave and initialized with zero values. It will be copied to the original buffer when the ECALL exits.The buffer will be allocated outside the enclave and initialized with zero values. This untrusted buffer will be copied to the original buffer when the OCALL exits.
in, outData is copied back and forth.Data is copied back and forth.
user_checkThe pointer is not checked. The raw address is transmitted.The pointer is not checked. The raw address is transmitted.
Table 1. Pointer qualifiers and their values ​​in ECALL and OCALL.

If you use direction indicators, the data buffer referenced by the pointer will be copied, and you must also specify the size so that Edger8r can determine how many bytes are in the buffer. If you specify user_check , then only the raw pointer is passed to ECALL or OCALL without any changes.

Code example


The sample code for this article has been updated: it is designed to be built with the Intel SGX SDK version 1.7 using Microsoft Visual Studio 2015. This code should work with the Intel SGX SDK version 1.6 and Visual Studio 2013, but we recommend updating the Intel SGX SDK to the latest version.

In further releases


In the eighth part of this series, we will add support for power events. Follow the news!

Source: https://habr.com/ru/post/324384/


All Articles