【SGX系列教程】(三)Intel-SGX 官方示例分析(SampleCode)——SampleEnclave

文章目录

  • 一. 引言
  • 二. README
    • 2.1 项目目的
    • 2.2 构建和执行示例代码的步骤
    • 2.3 配置参数解释
    • 2.4 配置文件分析
    • 2.5 启动令牌初始化
  • 三. 重点代码分析
    • 3.1 App文件夹
      • 3.1.1 App/App.cpp
      • 3.1.2 App/Edger8rSyntax文件夹
        • 3.1.2.1 App/Edger8rSyntax/Arrays.cpp
        • 3.1.2.2 App/Edger8rSyntax/Functions.cpp
        • 3.1.2.3 App/Edger8rSyntax/Pointers.cpp
        • 3.1.2.4 App/Edger8rSyntax/Types.cpp
      • 3.1.3 App/TrustedLibrary文件夹
        • 3.1.3.1 App/TrustedLibrary/Libc.cpp
        • 3.1.3.2 App/TrustedLibrary/Libcxx.cpp
        • 3.1.3.3 App/TrustedLibrary/Thread.cpp
    • 3.2 Enclave文件夹
      • 3.2.1 Enclave/Enclave.cpp
      • 3.2.2 Enclave/Edger8rSyntax文件夹
        • 3.2.2.1 Enclave/Edger8rSyntax/Arrays.cpp
        • 3.2.2.2 Enclave/Edger8rSyntax/Functions.cpp
        • 3.2.2.3 Enclave/Edger8rSyntax/Pointers.cpp
        • 3.2.2.4 Enclave/Edger8rSyntax/Types.cpp
      • 3.2.3 Enclave/TrustedLibrary文件夹
        • 3.2.3.1 Enclave/TrustedLibrary/Libc.cpp
        • 3.2.3.2 Enclave/TrustedLibrary/Libcxx.cpp
        • 3.2.3.3 Enclave/TrustedLibrary/Thread.cpp
  • 四. 感谢支持

一. 引言

    SampleEnclave作为enclave开发的基础示例,主要包括enclave的一些基础用法介绍,本文将结合这个示例从中学习SGX的基本使用方法。关于 SGX 开发运行环境的搭建可参考之前的一篇博客:【SGX系列教程】(一)。

二. README

------------------------
Purpose of SampleEnclave
------------------------
The project demonstrates several fundamental usages of Intel(R) Software Guard 
Extensions (Intel(R) SGX) SDK:
- Initializing and destroying an enclave
- Creating ECALLs or OCALLs
- Calling trusted libraries inside the enclave------------------------------------
How to Build/Execute the Sample Code
------------------------------------
1. Install Intel(R) SGX SDK for Linux* OS
2. Enclave test key(two options):a. Install openssl first, then the project will generate a test key<Enclave_private_test.pem> automatically when you build the project.b. Rename your test key(3072-bit RSA private key) to <Enclave_private_test.pem> and put it under the <Enclave> folder.
3. Make sure your environment is set:$ source ${sgx-sdk-install-path}/environment
4. Build the project with the prepared Makefile:a. Hardware Mode, Debug build:1) Enclave with no mitigation:$ make2) Enclave with mitigations for indirects and returns only:$ make MITIGATION-CVE-2020-0551=CF3) Enclave with full mitigation:$ make MITIGATION-CVE-2020-0551=LOADb. Hardware Mode, Pre-release build:1) Enclave with no mitigation:$ make SGX_PRERELEASE=1 SGX_DEBUG=02) Enclave with mitigations for indirects and returns only:$ make SGX_PRERELEASE=1 SGX_DEBUG=0 MITIGATION-CVE-2020-0551=CF3) Enclave with full mitigation:$ make SGX_PRERELEASE=1 SGX_DEBUG=0 MITIGATION-CVE-2020-0551=LOADc. Hardware Mode, Release build:1) Enclave with no mitigation:$ make SGX_DEBUG=02) Enclave with mitigations for indirects and returns only:$ make SGX_DEBUG=0 MITIGATION-CVE-2020-0551=CF3) Enclave with full mitigation:$ make SGX_DEBUG=0 MITIGATION-CVE-2020-0551=LOADd. Simulation Mode, Debug build:$ make SGX_MODE=SIMe. Simulation Mode, Pre-release build:$ make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0f. Simulation Mode, Release build:$ make SGX_MODE=SIM SGX_DEBUG=0
5. Execute the binary directly:$ ./app
6. Remember to "make clean" before switching build mode------------------------------------------
Explanation about Configuration Parameters
------------------------------------------
TCSMaxNum, TCSNum, TCSMinPoolThese three parameters will determine whether a thread will be createddynamically  when there is no available thread to do the work.StackMaxSize, StackMinSizeFor a dynamically created thread, StackMinSize is the amount of stack availableonce the thread is created and StackMaxSize is the total amount of stack thatthread can use. The gap between StackMinSize and StackMaxSize is the stackdynamically expanded as necessary at runtime.For a static thread, only StackMaxSize is relevant which specifies the totalamount of stack available to the thread.HeapMaxSize, HeapInitSize, HeapMinSizeHeapMinSize is the amount of heap available once the enclave is initialized.HeapMaxSize is the total amount of heap an enclave can use. The gap betweenHeapMinSize and HeapMaxSize is the heap dynamically expanded as necessaryat runtime.HeapInitSize is here for compatibility.-------------------------------------------------    
Sample configuration files for the Sample Enclave
-------------------------------------------------
With below configurations, if the signed enclave is launched on a SGX2 platform
with SGX2 supported kernel, it will be loaded with EDMM enabled. Otherwise, it
will behave in way of SGX1.config.01.xml: There is no dynamic thread, no dynamic heap expansion.config.02.xml: There is no dynamic thread. But dynamic heap expansion can happen.config.03.xml: There are dynamic threads. For a dynamic thread, there's no stack expansion.config.04.xml: There are dynamic threads. For a dynamic thread, stack will expanded as necessary.Below configuration is only workable on a SGX2 platform with SGX2 supported kernel:config.05.xml: There is a user region where users could operate on.-------------------------------------------------
Launch token initialization
-------------------------------------------------
If using libsgx-enclave-common or sgxpsw under version 2.4, an initialized variable launch_token needs to be passed as the 3rd parameter of API sgx_create_enclave. For example,sgx_launch_token_t launch_token = {0};
sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, launch_token, NULL, &global_eid, NULL);

    根据上面的README,我们可以分析出SampleEnclave项目的目的和构建/执行步骤,有助于理解项目的实现流程。

2.1 项目目的

    SampleEnclave项目演示了Intel® SGX SDK的一些基本使用方法,其主要目的包括:

  1. 初始化销毁enclave;
  2. 创建ECALLs(Enclave Calls)OCALLs(Outside Calls);
  3. 调用enclave内的受信任库

2.2 构建和执行示例代码的步骤

  1. 安装Intel® SGX SDK for Linux OS
    这是项目构建和运行的基本前提,需要确保已正确安装SGX SDK,具体安装教程参考第一篇博客。
  2. 准备Enclave测试密钥(两种方式)
    a. 安装openssl,然后在构建项目时会自动生成一个测试密钥Enclave_private_test.pem
    b. 使用你自己的测试密钥(3072位RSA私钥),将其重命名为Enclave_private_test.pem并放入文件夹中。
  3. 设置环境变量: source ${sgx-sdk-install-path}/environment
  4. 构建项目,根据不同的模式配置进行构建,具体参考上述README。
  5. 直接执行生成的二进制文件
    ./app
  6. 切换构建模式前请记得清理构建
    make clean

2.3 配置参数解释

  1. 线程配置参数
    TCSMaxNum, TCSNum, TCSMinPool:这三个参数决定是否在没有可用线程工作时动态创建一个线程。
  2. 配置参数
    StackMaxSize, StackMinSize:对于动态创建的线程,StackMinSize 是线程创建时的栈大小,StackMaxSize 是线程可以使用的最大栈空间。对于静态线程,只需要设置StackMaxSize
  3. 配置参数
    HeapMaxSize, HeapInitSize,HeapMinSize:HeapMinSize 是enclave初始化时的堆大小,HeapMaxSize 是enclave可以使用的最大堆空间,HeapInitSize 是为了兼容而设置。

2.4 配置文件分析

    对于不同的SGX平台(SGX1和SGX2)和配置,enclave的行为会有所不同:

  1. SGX1平台的行为:
    config.01.xml:无动态线程,无动态堆扩展。
    config.02.xml:无动态线程,但动态堆扩展可以发生。
    config.03.xml:有动态线程,但没有栈扩展。
    config.04.xml:有动态线程,栈可以根据需要扩展。
  2. 仅在SGX2平台上有效的配置:
    config.05.xml:有一个用户区域,用户可以对其进行操作。

2.5 启动令牌初始化

    如果使用版本低于2.4(最新安装的都不会)的libsgx-enclave-commonsgxpsw,需要传递一个初始化的启动令牌变量作为第三个参数给API sgx_create_enclave,例如:

sgx_launch_token_t launch_token = {0};
sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, launch_token, NULL, &global_eid, NULL);

总结
    SampleEnclave项目展示了如何使用Intel® SGX SDK进行基本的enclave操作,包括初始化和销毁enclave创建ECALL和OCALL、以及调用enclave内的受信任库。通过详细的构建和执行步骤、配置参数解释及各种配置文件的分析,可以帮助用户更好地理解SGX的基本使用方法和配置技巧。

三. 重点代码分析

   文件目录如下图,App侧与Enclave侧的程序一一对应,其中App表示REE侧应用程序,Enclave表示enclave侧Ecall程序。
在这里插入图片描述

3.1 App文件夹

3.1.1 App/App.cpp

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pwd.h>
#define MAX_PATH FILENAME_MAX#include "sgx_urts.h"       // 包含SGX User Runtime库
#include "App.h"            // 包含应用程序自定义头文件
#include "Enclave_u.h"      // 包含Enclave生成的头文件,用于与Enclave进行交互/* 全局EID由多个线程共享 */
sgx_enclave_id_t global_eid = 0;/* 定义一个错误列表,用于记录sgx_create_enclave返回的错误码 */
typedef struct _sgx_errlist_t {sgx_status_t err;const char *msg; // 错误信息const char *sug; // 建议
} sgx_errlist_t;/* sgx_create_enclave可能返回的错误码及其对应信息 */
static sgx_errlist_t sgx_errlist[] = {{ SGX_ERROR_UNEXPECTED, "Unexpected error occurred.", NULL },{ SGX_ERROR_INVALID_PARAMETER, "Invalid parameter.", NULL },{ SGX_ERROR_OUT_OF_MEMORY, "Out of memory.", NULL },{ SGX_ERROR_ENCLAVE_LOST, "Power transition occurred.", "Please refer to the sample \"PowerTransition\" for details." },{ SGX_ERROR_INVALID_ENCLAVE, "Invalid enclave image.", NULL },{ SGX_ERROR_INVALID_ENCLAVE_ID, "Invalid enclave identification.", NULL },{ SGX_ERROR_INVALID_SIGNATURE, "Invalid enclave signature.", NULL },{ SGX_ERROR_OUT_OF_EPC, "Out of EPC memory.", NULL },{ SGX_ERROR_NO_DEVICE, "Invalid SGX device.", "Please make sure SGX module is enabled in the BIOS, and install SGX driver afterwards." },{ SGX_ERROR_MEMORY_MAP_CONFLICT, "Memory map conflicted.", NULL },{ SGX_ERROR_INVALID_METADATA, "Invalid enclave metadata.", NULL },{ SGX_ERROR_DEVICE_BUSY, "SGX device was busy.", NULL },{ SGX_ERROR_INVALID_VERSION, "Enclave version was invalid.", NULL },{ SGX_ERROR_INVALID_ATTRIBUTE, "Enclave was not authorized.", NULL },{ SGX_ERROR_ENCLAVE_FILE_ACCESS, "Can't open enclave file.", NULL },{ SGX_ERROR_MEMORY_MAP_FAILURE, "Failed to reserve memory for the enclave.", NULL },
};/* 检查加载enclave的错误条件 */
void print_error_message(sgx_status_t ret)
{size_t idx = 0;size_t ttl = sizeof sgx_errlist/sizeof sgx_errlist[0];for (idx = 0; idx < ttl; idx++) {if(ret == sgx_errlist[idx].err) {if(NULL != sgx_errlist[idx].sug)printf("信息: %s\n", sgx_errlist[idx].sug);printf("错误: %s\n", sgx_errlist[idx].msg);break;}}if (idx == ttl)printf("错误代码是 0x%X. 请参考 \"Intel SGX SDK Developer Reference\" 获取更多详情。\n", ret);
}/* 初始化enclave:* 调用sgx_create_enclave来初始化一个enclave实例*/
int initialize_enclave(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;/* 调用sgx_create_enclave来初始化一个enclave实例 *//* 调试支持: 将第二个参数设置为1 */ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);if (ret != SGX_SUCCESS) {print_error_message(ret);return -1;}return 0;
}/* OCall函数 */
void ocall_print_string(const char *str)
{/* 代理将会检查字符串的长度并在结尾添加空字符,防止缓冲区溢出 */printf("%s", str);
}/* 应用程序入口 */
int SGX_CDECL main(int argc, char *argv[])
{(void)(argc);(void)(argv);/* 初始化enclave */if(initialize_enclave() < 0){printf("在退出前按下任意键 ...\n");getchar();return -1; }/* 使用edger8r生成的属性调用 */edger8r_array_attributes();edger8r_pointer_attributes();edger8r_type_attributes();edger8r_function_attributes();/* 使用受信任的库函数 */ecall_libc_functions();ecall_libcxx_functions();ecall_thread_functions();/* 销毁enclave */sgx_destroy_enclave(global_eid);printf("信息: SampleEnclave成功返回。\n");printf("在退出前按下任意键 ...\n");getchar();return 0;
}

代码功能分析

  1. 初始化与配置
    头文件与库的引用:
    #include “sgx_urts.h”:包含SGX User Runtime库的头文件。
    #include “App.h”:包含应用程序自定义头文件。
    #include “Enclave_u.h”:包含enclave生成的头文件,用于与enclave进行交互。
    全局变量:
    sgx_enclave_id_t global_eid:用于存储enclave的全局ID,由多个线程共享。
    错误处理:
    sgx_errlist_t:定义错误列表结构,用于存储错误码、错误信息和建议。
    sgx_errlist:包含sgx_create_enclave可能返回的错误码及其对应的错误信息和建议。
    print_error_message:根据错误码打印相应的错误信息和建议。
  2. 初始化enclave
    initialize_enclave:
    调用 sgx_create_enclave 函数创建enclave实例,并将得到的enclave ID存储在 global_eid 中。
    如果创建enclave失败,则调用 print_error_message 打印错误信息,并返回-1。
  3. OCall函数
    ocall_print_string:
    将传入的字符串打印到标准输出。(在OCall过程中,代理会检查字符串长度并在字符串结尾添加空字符,以防止缓冲区溢出)。
  4. 应用程序入口
    main函数:
    初始化enclave:调用 initialize_enclave 函数初始化enclave。如果失败则打印错误信息并等待用户输入。
    调用edger8r生成的属性和函数:
    edger8r_array_attributes
    edger8r_pointer_attributes
    edger8r_type_attributes
    edger8r_function_attributes
    调用受信任的库函数:
    ecall_libc_functions
    ecall_libcxx_functions
    ecall_thread_functions
    销毁enclave:调用 sgx_destroy_enclave 函数销毁enclave实例。
    程序结束提示:显示提示信息,用户按任意键后退出程序。

总结
    通过这段代码,实现了SampleEnclave项目的核心功能,包括初始化和销毁enclave调用ECALL和OCALL、以及使用enclave内的受信任库。每一步操作都有详细的错误处理和用户提示,确保了代码的鲁棒性和易用性。

3.1.2 App/Edger8rSyntax文件夹

    Edger8r是SGX的一部分,是可信和不可信部分的边界层,用来提供一些在不可信的应用和enclave之间的一些边界路径。Edger8r在编译的时候自动执行但是一些高级的enclave开发人员会手动调用Edger8r。

3.1.2.1 App/Edger8rSyntax/Arrays.cpp
#include "../App.h"       // 包含应用程序的头文件
#include "Enclave_u.h"    // 包含Enclave生成的头文件,用于与Enclave进行交互/* edger8r_array_attributes:* 调用声明了数组属性的ECALL*/
void edger8r_array_attributes(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;/* user_check */int arr1[4] = {0, 1, 2, 3};  // 初始化数组arr1ret = ecall_array_user_check(global_eid, arr1); // 调用ecall_array_user_check函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序/* 确认arr1被修改 */for (int i = 0; i < 4; i++)assert(arr1[i] == (3 - i)); // 检查arr1的每个元素是否变成3-i/* in */int arr2[4] = {0, 1, 2, 3};  // 初始化数组arr2ret = ecall_array_in(global_eid, arr2); // 调用ecall_array_in函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序/* 确认arr2未被修改 */for (int i = 0; i < 4; i++)assert(arr2[i] == i); // 检查arr2的每个元素是否未改变/* out */int arr3[4] = {0, 1, 2, 3};  // 初始化数组arr3ret = ecall_array_out(global_eid, arr3); // 调用ecall_array_out函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序/* 确认arr3被修改 */for (int i = 0; i < 4; i++)assert(arr3[i] == (3 - i)); // 检查arr3的每个元素是否变成3-i/* in, out */int arr4[4] = {0, 1, 2, 3};  // 初始化数组arr4ret = ecall_array_in_out(global_eid, arr4); // 调用ecall_array_in_out函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序/* 确认arr4被修改 */for (int i = 0; i < 4; i++)assert(arr4[i] == (3 - i)); // 检查arr4的每个元素是否变成3-i/* isary */array_t arr5 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};  // 初始化数组arr5ret = ecall_array_isary(global_eid, arr5); // 调用ecall_array_isary函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序/* 确认arr5被修改 */for (int i = 0; i < 10; i++)assert(arr5[i] == (9 - i)); // 检查arr5的每个元素是否变成9-i
}

    该函数 edger8r_array_attributes 展示了如何通过SGX ECALL调用将数组传递给enclave进行处理,并在处理后检查数组是否按照预期进行了修改。函数中分别展示了四种不同的数组传递方式(user_check, in, out, in_out),并通过assert语句对修改后的数组进行验证。此示例演示了如何在SGX环境中进行数组相关的ECALL调用和数据验证。

3.1.2.2 App/Edger8rSyntax/Functions.cpp
#include "../App.h"       // 包含应用程序的头文件
#include "Enclave_u.h"    // 包含Enclave生成的头文件,用于与Enclave进行交互/* edger8r_function_attributes:* 调用使用调用约定属性声明的ECALL。* 调用使用[public]声明的ECALL。*/
void edger8r_function_attributes(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 调用公开的函数ecall_function_publicret = ecall_function_public(global_eid);if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 用户不应该在这里调用私有函数int runned = 0;ret = ecall_function_private(global_eid, &runned);// 确保返回值是SGX_ERROR_ECALL_NOT_ALLOWED,并且函数未被运行if (ret != SGX_ERROR_ECALL_NOT_ALLOWED || runned != 0)abort();  // 如果检查失败,终止程序
}/* ocall_function_allow:* 该OCALL调用被[allow]属性批准的edger8r_private。*/
void ocall_function_allow(void)
{int runned = 0;sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 调用私有函数ecall_function_privateret = ecall_function_private(global_eid, &runned);// 确保返回值是SGX_SUCCESS,并且函数已被运行if (ret != SGX_SUCCESS || runned != 1)abort();  // 如果检查失败,终止程序
}

这段代码展示了如何调用SGX ECALL时处理不同的函数属性:

  • edger8r_function_attributes 函数展示了如何调用具有公开属性的ECALL函数,以及检查并防止直接调用私有的ECALL函数。
  • ocall_function_allow 函数展示了如何通过OCALL调用被允许的私有ECALL函数。
    通过这些示例,可以清楚地了解如何在SGX环境中管理和调用具有不同属性的ECALL函数,以确保代码的安全性和正确性。
3.1.2.3 App/Edger8rSyntax/Pointers.cpp
#include "../App.h"       // 包含应用程序的头文件
#include "Enclave_u.h"    // 包含Enclave生成的头文件,用于与Enclave进行交互/* edger8r_pointer_attributes:* 调用声明了指针属性的ECALL。*/
void edger8r_pointer_attributes(void)
{int val = 0;sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 定义字符数组和长度变量char c[128] = {0};size_t len = 0;// 初始化字符数组cmemset(c, 0xe, 128);ret = ecall_pointer_user_check(global_eid, &len, &c, 128); // 调用ecall_pointer_user_check函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(strcmp(c, "SGX_SUCCESS") == 0); // 检查c是否被更改为"SGX_SUCCESS"// 测试[in]指针属性val = 1;ret = ecall_pointer_in(global_eid, &val); // 调用ecall_pointer_in函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(val == 1); // 确保val的值未被修改// 测试[out]指针属性val = 1;ret = ecall_pointer_out(global_eid, &val); // 调用ecall_pointer_out函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(val == 1234); // 确保val的值被修改为1234// 测试[in, out]指针属性val = 1;ret = ecall_pointer_in_out(global_eid, &val); // 调用ecall_pointer_in_out函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(val == 1234); // 确保val的值被修改为1234ret = ocall_pointer_attr(global_eid); // 调用OCALL函数ocall_pointer_attrif (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 测试字符串指针char str1[] = "1234567890";ret = ecall_pointer_string(global_eid, str1); // 调用ecall_pointer_string函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(strlen(str1) == 10 && memcmp(str1, "0987654321", strlen(str1)) == 0); // 确保str1的值被正确修改// 测试字符串指针常量const char str2[] = "1234567890";ret = ecall_pointer_string_const(global_eid, str2); // 调用ecall_pointer_string_const函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(strlen(str2) == 10 && memcmp(str2, "1234567890", strlen(str2)) == 0); // 确保str2未被修改// 测试指针大小char str3[] = "1234567890";ret = ecall_pointer_size(global_eid, (void*)str3, strlen(str3)); // 调用ecall_pointer_size函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(strlen(str3) == 10 && memcmp(str3, "0987654321", strlen(str3)) == 0); // 确保str3的值被正确修改// 测试只读指针char str4[] = "1234567890";ret = ecall_pointer_isptr_readonly(global_eid, (buffer_t)str4, strlen(str4)); // 调用ecall_pointer_isptr_readonly函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序assert(strlen(str4) == 10 && memcmp(str4, "1234567890", strlen(str4)) == 0); // 确保str4未被修改// 测试带计数的指针int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};ret = ecall_pointer_count(global_eid, arr, 10); // 调用ecall_pointer_count函数if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序for (int i = 0; i < 10; i++)assert(arr[i] == (9 - i)); // 确保arr的值被正确修改为9-ireturn;
}/* ocall_pointer_user_check:* 声明为[user_check]的OCALL。*/
void ocall_pointer_user_check(int* val)
{(void)val;  // 安静未使用的参数警告assert(val != NULL);  // 确保指针val不为NULL
}/* ocall_pointer_in:* 声明为[in]的OCALL。*/
void ocall_pointer_in(int* val)
{*val = 1234;  // 将指针val指向的值设置为1234
}/* ocall_pointer_out:* 声明为[out]的OCALL。*/
void ocall_pointer_out(int* val)
{*val = 1234;  // 将指针val指向的值设置为1234
}/* ocall_pointer_in_out:* 声明为[in, out]的OCALL。*/
void ocall_pointer_in_out(int* val)
{*val = 1234;  // 将指针val指向的值设置为1234
}

ECALL函数总结

   在函数 edger8r_pointer_attributes中,多个不同的ECALL被调用来测试SGX环境下的指针操作。这些ECALL通过不同的指针属性(如 user_check, in, out, in, out,以及字符串指针)来演示如何在enclave内部和外部之间传递和操作数据。每次调用都使用 assert 检查数据是否已按预期进行了修改,以验证指针操作的正确性。

OCALL函数总结

  • ocall_pointer_user_check
    验证传入的指针是否为非空指针,以确保数据完整性。
  • ocall_pointer_in
    将传入的指针值设置为 1234,用于模拟出站(outbound)数据的例子。
  • ocall_pointer_out
    将传入的指针值设置为 1234,与 ocall_pointer_in 类似,但用于表示入站(inbound)数据。
  • ocall_pointer_in_out
    同时设置传入指针值为 1234,展示了入站和出站指针的双向操作。

    通过这个文件,SampleEnclave项目展示了用于实现不同指针操作的ECALL和OCALL方法。这些示例确保了在enclave内进行指针操作时的安全性和正确性,同时也增强了对指针的使用和数据传递机制的理解。

3.1.2.4 App/Edger8rSyntax/Types.cpp
#include "../App.h"       // 包含应用程序的头文件
#include "Enclave_u.h"    // 包含Enclave生成的头文件,用于与Enclave进行交互/* edger8r_type_attributes:* 调用声明了基本类型的ECALL。*/
void edger8r_type_attributes(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 调用使用char类型的ECALLret = ecall_type_char(global_eid, (char)0x12); // 传递一个char类型的值0x12if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 调用使用int类型的ECALLret = ecall_type_int(global_eid, (int)1234); // 传递一个int类型的值1234if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 调用使用float类型的ECALLret = ecall_type_float(global_eid, (float)1234.0); // 传递一个float类型的值1234.0if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 调用使用double类型的ECALLret = ecall_type_double(global_eid, (double)1234.5678); // 传递一个double类型的值1234.5678if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 调用使用size_t类型的ECALLret = ecall_type_size_t(global_eid, (size_t)12345678); // 传递一个size_t类型的值12345678if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 调用使用wchar_t类型的ECALLret = ecall_type_wchar_t(global_eid, (wchar_t)0x1234); // 传递一个wchar_t类型的值0x1234if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 创建并初始化一个struct_foo_t类型的结构体struct struct_foo_t g = {1234, 5678};ret = ecall_type_struct(global_eid, g); // 传递一个结构体gif (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 创建并初始化一个union_union_foo_t类型的联合体union union_foo_t val = {0};ret = ecall_type_enum_union(global_eid, ENUM_FOO_0, &val); // 传递一个枚举值ENUM_FOO_0和一个联合体valif (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 确认联合体val的值是否被正确修改assert(val.union_foo_0 == 2);
}

    这段代码展示了如何通过SGX调用包含基本类型的ECALL。函数 edger8r_type_attributes 分别演示了如何传递 charintfloatdoublesize_twchar_t 类型的数据,以及如何传递 struct union 类型的数据。通过检查每次ECALL的返回值,代码确保了ECALL调用的正确性,同时也演示了如何在SGX环境中进行各种数据类型的操作。

3.1.3 App/TrustedLibrary文件夹

3.1.3.1 App/TrustedLibrary/Libc.cpp
#include "../App.h"       // 包含应用程序的头文件
#include "Enclave_u.h"    // 包含Enclave生成的头文件,用于与Enclave进行交互/* ecall_libc_functions:* 调用标准C函数。*/
void ecall_libc_functions(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 调用ECALL来演示malloc和free的使用ret = ecall_malloc_free(global_eid); // 调用malloc和free的ECALLif (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 初始化cpuid数组int cpuid[4] = {0x0, 0x0, 0x0, 0x0};// 调用ECALL来获取CPUID信息ret = ecall_sgx_cpuid(global_eid, cpuid, 0x0); // 调用获取CPUID信息的ECALLif (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序
}

    通过这段代码,SampleEnclave项目展示了如何在SGX enclave中调用标准C库函数。函数 ecall_libc_functions 通过两个ECALL演示了 mallocfree 的使用,以及如何获取CPUCPUID信息。每次ECALL调用后都检查其返回值以确保操作的成功。此示例有助于理解如何在SGX环境中使用标准C库函数以及执行基本的内存管理和系统操作。

3.1.3.2 App/TrustedLibrary/Libcxx.cpp
#include <stdio.h>        // 包含标准输入输出头文件#include "../App.h"       // 包含应用程序的头文件
#include "Enclave_u.h"    // 包含Enclave生成的头文件,用于与Enclave进行交互/* ecall_libcxx_functions:* 调用标准C++函数。*/
void ecall_libcxx_functions(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 调用ECALL函数来演示C++异常处理ret = ecall_exception(global_eid); // 调用处理异常的ECALLif (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序// 调用ECALL函数来演示C++标准库中的map容器ret = ecall_map(global_eid); // 调用处理map的ECALLif (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序
}

   通过这段代码,SampleEnclave项目展示了如何在SGX enclave中调用标准C++库函数。函数 ecall_libcxx_functions 通过两个ECALL函数演示了如何在enclave中处理C++异常以及使用C++标准库中的 map 容器。每次ECALL调用后都检查其返回值以确保操作的成功。此示例有助于理解如何在SGX环境中使用标准C++库函数以及执行基本的异常处理和容器操作。

3.1.3.3 App/TrustedLibrary/Thread.cpp
#include <thread>        // 包含标准C++线程库头文件
#include <stdio.h>       // 包含标准输入输出头文件
using namespace std;     // 使用标准命名空间#include "../App.h"      // 包含应用程序的头文件
#include "Enclave_u.h"   // 包含Enclave生成的头文件,用于与Enclave进行交互static size_t counter = 0;  // 全局计数器/* 增加计数器的函数 */
void increase_counter(void)
{size_t cnr = 0;sgx_status_t ret = SGX_ERROR_UNEXPECTED;ret = ecall_increase_counter(global_eid, &cnr);  // 调用ECALL增加计数器if (cnr != 0) counter = cnr;  // 如果cnr不为0,更新全局计数器if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序
}/* 数据生产者函数 */
void data_producer(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;ret = ecall_producer(global_eid);  // 调用ECALL数据生产者if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序
}/* 数据消费者函数 */
void data_consumer(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;ret = ecall_consumer(global_eid);  // 调用ECALL数据消费者if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序
}/* ecall_thread_functions:* 调用线程函数,包括互斥锁、条件变量等。*/
void ecall_thread_functions(void)
{// 创建和启动四个增加计数器的线程thread adder1(increase_counter);thread adder2(increase_counter);thread adder3(increase_counter);thread adder4(increase_counter);// 等待所有线程完成adder1.join();adder2.join();adder3.join();adder4.join();// 确认计数器的值是否符合预期assert(counter == 4 * LOOPS_PER_THREAD);printf("信息: 正在执行线程同步,请稍候...  \n");// 创建和启动消费和生产线程thread consumer1(data_consumer);thread producer0(data_producer);thread consumer2(data_consumer);thread consumer3(data_consumer);thread consumer4(data_consumer);// 等待所有线程完成consumer1.join();consumer2.join();consumer3.join();consumer4.join();producer0.join();
}

    这段代码展示了如何在SGX enclave中通过ECALL实现基本的线程操作,包括线程同步、互斥锁和条件变量等。通过创建多个线程并调用相应的ECALL函数,该示例演示了如何在enclave中进行并发操作,并验证了全局计数器的正确性。同时,该代码还演示了数据生产者和消费者的模式,以展示更复杂的线程同步机制。每个ECALL后都检查其返回值,以确保操作的成功。

3.2 Enclave文件夹

3.2.1 Enclave/Enclave.cpp

/* * printf: *   Invokes OCALL to display the enclave buffer to the terminal.*/
int printf(const char* fmt, ...)
{char buf[BUFSIZ] = { '\0' };va_list ap;va_start(ap, fmt);vsnprintf(buf, BUFSIZ, fmt, ap);va_end(ap);ocall_print_string(buf);return (int)strnlen(buf, BUFSIZ - 1) + 1;
}

    在Enclave中的主函数Enclave.cpp主要提供printf的OCALL函数,也就是将enclave通过该函数在REE中打印出来。

3.2.2 Enclave/Edger8rSyntax文件夹

3.2.2.1 Enclave/Edger8rSyntax/Arrays.cpp
/* 测试数组属性 */
#include "sgx_trts.h"       // 包含SGX运行时库的头文件
#include "../Enclave.h"     // 包含Enclave自定义头文件
#include "Enclave_t.h"      // 包含由Edger8r工具生成的头文件,用于定义ECALL和OCALL接口
#include <string.h>         // 包含标准C库的字符串操作头文件/* ecall_array_user_check:*   [user_check] 参数不会执行拷贝操作。*/
void ecall_array_user_check(int arr[4])
{// 检查数组是否位于Enclave外部if (sgx_is_outside_enclave(arr, 4 * sizeof(int)) != 1)abort();// 遍历数组并修改其值for (int i = 0; i < 4; i++) {assert(arr[i] == i); // 确认数组元素的值是否为预期// 以下代码执行arr[i] = (3 - i)// 它将4个字节写入不受信任的内存,非8字节对齐。// 因此出于安全考虑需要使用memcpy_verw()int tmp = 3 - i;memcpy_verw(&arr[i], &tmp, sizeof(int));}
}/* ecall_array_in:*   arr[] 被拷贝到受信任域中,但修改后的结果不会反映到不受信任的一侧。*/
void ecall_array_in(int arr[4])
{// 遍历数组并修改其值for (int i = 0; i < 4; i++) {assert(arr[i] == i); // 确认数组元素的值是否为预期arr[i] = (3 - i);    // 修改数组元素的值}
}/* ecall_array_out:*   arr[] 在Enclave内部分配,并且会被拷贝到不受信任的一侧。*/
void ecall_array_out(int arr[4])
{// 遍历数组并修改其值for (int i = 0; i < 4; i++) {/* arr 不是从应用程序中拷贝过来的 */assert(arr[i] == 0); // 确认数组元素的初始值为0arr[i] = (3 - i);    // 修改数组元素的值}
}/* ecall_array_in_out:*   arr[] 会在Enclave内部分配,其内容也会被拷贝。*   ECALL返回后,结果会被拷贝到外部。*/
void ecall_array_in_out(int arr[4])
{// 遍历数组并修改其值for (int i = 0; i < 4; i++) {assert(arr[i] == i); // 确认数组元素的值是否为预期arr[i] = (3 - i);    // 修改数组元素的值}
}/* ecall_array_isary:*   [isary] 告诉Edger8r用户定义的 'array_t' 是一个数组类型。*/
void ecall_array_isary(array_t arr)
{// 检查数组是否位于Enclave外部if (sgx_is_outside_enclave(arr, sizeof(array_t)) != 1)abort();int n = sizeof(array_t) / sizeof(arr[0]); // 计算数组元素的个数// 遍历数组并修改其值for (int i = 0; i < n; i++) {assert(arr[i] == i);  // 确认数组元素的值是否为预期// 以下代码执行arr[i] = (n - 1 - i);// 它将4个字节写入不受信任的内存,非8字节对齐。// 因此出于安全考虑需要使用memcpy_verw()int tmp = n - 1 - i;memcpy_verw(&arr[i], &tmp, sizeof(int));}
}

    这段代码展示了多个ECALL函数,演示了如何在Enclave中处理不同的数组操作,包括传入传出双向传输数组。每个函数都通过遍历数组元素,执行各种检查和修改操作,并考虑到内存访问安全性的因素。尤其是在需要写入不受信任内存时,使用 memcpy_verw 来确保安全性。这些示例有助于理解如何在SGX环境中安全地处理数组数据。

  • Arrays.edl
/* Arrays.edl - Samples for array attributes. */
enclave {/* *  Only for fixed-size array (size is explicitly specified).*/trusted {/** []:  can be used to declare an array.* [user_check]:*      pointer of the array won't be verified, and the buffer pointed by 'arr' *      is not copied into the enclave either. But enclave can modify the memory outside.*/public void ecall_array_user_check([user_check] int arr[4]);/** [in]:*      buffer for the array will be allocated inside the enclave, *      content of the array will be copied into the new allocated memory inside. *      Any changes performed inside the enclave will not affect the array outside.*/public void ecall_array_in([in] int arr[4]);/** [out]:*      buffer for the array will be allocated inside the enclave,*      but the content of the array won't be copied. After ECALL returns, *      the buffer inside the enclave will copied into outside array.*/public void ecall_array_out([out] int arr[4]);/** [in, out]:*      buffer for the array will be allocated inside the enclave,*      the content of the array will be copied either. After ECALL returns, *      the buffer inside the enclave will by copied into outside array again.*/public void ecall_array_in_out([in, out] int arr[4]);/** [isary]:*      tells Edger8r the user defined 'array_t' is an array type, 'arr' will be *      treated as a pointer, no memory copied either due to [user_check].*      For OCALLs, 'arr' shall point to the memory outside the enclave. */public void ecall_array_isary([user_check, isary] array_t arr);};untrusted {/** [user_check|in|out|in,out|isary] can also be used in OCALLs, refer to the "User Guide" for details.*/};};

这个 Arrays.edl 文件通过定义多个ECALL接口,详细展示了如何在SGX Enclave中处理数组数据以及不同的数组属性(如 [user_check], [in], [out], [in, out], [isary])。每种属性都影响了数组数据在Enclave中的操作和在Enclave外部的反映方式。这个文件提供了丰富的示例,有助于理解在Enclave环境中如何安全且灵活地操作数组数据。

3.2.2.2 Enclave/Edger8rSyntax/Functions.cpp
/* 测试调用约定 */
#include <string.h>       // 包含标准C库的字符串操作头文件
#include <stdio.h>        // 包含标准输入输出头文件#include "../Enclave.h"   // 包含Enclave自定义头文件
#include "Enclave_t.h"    // 包含由Edger8r工具生成的头文件,用于定义ECALL和OCALL接口/* ecall_function_public:*   一个公开的ECALL,调用OCALL 'ocall_function_allow'。*/
void ecall_function_public(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;// 调用OCALL函数'ocall_function_allow'ret = ocall_function_allow();if (ret != SGX_SUCCESS)abort();  // 如果调用失败,终止程序return;
}/* ecall_function_private:*   一个私有的ECALL,仅能在OCALL 'ocall_function_allow'中被调用。*/
int ecall_function_private(void)
{return 1;  // 返回值1表示函数成功调用
}

    这段代码展示了如何在SGX Enclave中处理不同调用类型(公开 or 私有)的函数。函数 ecall_function_public 是一个公开的ECALL,它调用了一个OCALL函数 ocall_function_allow,并且检查调用是否成功;如果失败则终止程序。而 ecall_function_private是一个私有的ECALL,仅能在OCALL ocall_function_allow 中被调用,返回一个整数 1 表示成功。

    这个示例有助于理解在SGX环境中如何区分和处理公开与私有的ECALL函数,以及如何通过OCALL来调用这些ECALL函数。

  • Functions.edl
/* Functions.edl - Samples for function attributes. */
enclave {/* * Following keywords/attributes are supported for untrusted functions: *      cdecl, stdcall, fastcall, dllimport (only for Windows).*      [public] is only supported for the trusted functions.* Trusted function will be treated as [private] w/o the [public].*/trusted {/** [public]:*      public ECALL can be called directly in App.*/public void ecall_function_public(void);/** [private]:*      private ECALL cannot be called directly in App.*/int ecall_function_private(void);};untrusted {/** [allow]:*      OCALL 'ocall_function_allow' can invoke ECALL 'ecall_function_private' in App side. ** Note: No ECALL can be called in OCALL w/o [allow].*/void ocall_function_allow(void) allow(ecall_function_private);};
};

    这个Functions.edl文件定义了两个ECALL和一个OCALL,同时展示了如何使用函数属性来控制它们的调用权限。

  1. 公开ECALL:ecall_function_public 使用 [public] 属性,可以直接在应用程序中调用,没有限制。
  2. 私有ECALL:ecall_function_private 没有使用 [public] 属性,因此被视为私有的,不能直接在应用程序中调用。只能在被授权的OCALL中调用。
  3. OCALL与授权:ocall_function_allow 是一个OCALL,使用 [allow] 属性,授权该OCALL调用特定的私有ECALL ecall_function_private。这种机制确保了只有特定的OCALL可以调用私有的ECALL,从而增强了安全性和控制。

    这个示例有助于理解在SGX环境中如何使用EDL来定义和控制不同类型的函数调用,以及如何通过函数属性来管理这些调用的权限。

3.2.2.3 Enclave/Edger8rSyntax/Pointers.cpp
#include <string.h>
#include <sys/types.h>#include "../Enclave.h"
#include "Enclave_t.h"
#include "sgx_lfence.h"
#include "sgx_trts.h"/* checksum_internal:*   计算输入缓冲区和长度的简单校验和*/
int32_t checksum_internal(char* buf, size_t count)
{register int32_t sum = 0;int16_t* ptr = (int16_t*)buf;/* 主循环求和 */while (count > 1) {sum = sum + *ptr++;count = count - 2;}/* 如果有剩余字节,加上它 */if (count > 0) {sum = sum + *((char*)ptr);}return ~sum;
}/* ecall_pointer_user_check, ecall_pointer_in, ecall_pointer_out, ecall_pointer_in_out:*   测试[in]、[out]、[user_check]属性的根ECALL。*/
size_t ecall_pointer_user_check(void* val, size_t sz)
{/* 检查缓冲区是否在enclave外分配 */if (sgx_is_outside_enclave(val, sz) != 1)abort();/* 在sgx_is_outside_enclave检查后插入fence */sgx_lfence();char tmp[100] = { 0 };size_t len = sz > 100 ? 100 : sz;/* 将内存复制到enclave中,以确保在checksum_internal()中不更改'val' */memcpy(tmp, val, len);int32_t sum = checksum_internal((char*)tmp, len);printf("Checksum(0x%p, %zu) = 0x%x\n",val, len, (unsigned int)sum);/* 直接修改外部内存 */memcpy_verw(val, "SGX_SUCCESS", len > 12 ? 12 : len);return len;
}/* ecall_pointer_in:*   val的缓冲区被复制到enclave中。*/
void ecall_pointer_in(int* val)
{if (sgx_is_within_enclave(val, sizeof(int)) != 1)abort();assert(*val == 1);*val = 1234;
}/* ecall_pointer_out:*   val的缓冲区被复制到不可信的一侧。*/
void ecall_pointer_out(int* val)
{if (sgx_is_within_enclave(val, sizeof(int)) != 1)abort();assert(*val == 0);*val = 1234;
}/* ecall_pointer_in_out:*   val的缓冲区被双重复制。*/
void ecall_pointer_in_out(int* val)
{if (sgx_is_within_enclave(val, sizeof(int)) != 1)abort();assert(*val == 1);*val = 1234;
}/* ocall_pointer_attr:*   测试OCALL [in]、[out]、[user_check]的根ECALL。*/
void ocall_pointer_attr(void)
{sgx_status_t ret = SGX_ERROR_UNEXPECTED;int val = 0;ret = ocall_pointer_user_check(&val);if (ret != SGX_SUCCESS)abort();val = 0;ret = ocall_pointer_in(&val);if (ret != SGX_SUCCESS)abort();assert(val == 0);val = 0;ret = ocall_pointer_out(&val);if (ret != SGX_SUCCESS)abort();assert(val == 1234);val = 0;ret = ocall_pointer_in_out(&val);if (ret != SGX_SUCCESS)abort();assert(val == 1234);return;
}/* ecall_pointer_string:*   [string]定义一个字符串。*/
void ecall_pointer_string(char* str)
{strncpy(str, "0987654321", strlen(str));
}/* ecall_pointer_string_const:*   const [string]定义一个不能修改的字符串。*/
void ecall_pointer_string_const(const char* str)
{char* temp = new char[strlen(str)];strncpy(temp, str, strlen(str));delete[] temp;
}/* ecall_pointer_size:*   'len'需要指定以告诉Edger8r 'str'的长度。*/
void ecall_pointer_size(void* ptr, size_t len)
{strncpy((char*)ptr, "0987654321", len);
}/* ecall_pointer_count:*   'cnt'需要指定以告诉Edger8r 'arr'中的元素数量。*/
void ecall_pointer_count(int* arr, size_t count)
{int cnt = (int)count;for (int i = (cnt - 1); i >= 0; i--)arr[i] = (cnt - 1 - i);
}/* ecall_pointer_isptr_readonly:*   'buf'是用户定义类型,应标记为[isptr]。*   如果它不可写,应指定[readonly]。*/
void ecall_pointer_isptr_readonly(buffer_t buf, size_t len)
{strncpy((char*)buf, "0987654321", len);
}

代码功能分析
    这段代码展示了如何在Intel SGX中处理指针属性的示例,包括[in]、[out]、[user_check]、[string]、[size]、[count]和[readonly]等属性。以下是主要功能和步骤的详细分析:

  1. 校验和计算:
    checksum_internal函数计算输入缓冲区的简单校验和。
  2. 用户检查指针:
    ecall_pointer_user_check函数检查指针是否在enclave外部分配,并计算校验和。
  3. 指针属性测试:
    ecall_pointer_in函数验证指针在enclave内并修改其值。
    ecall_pointer_out函数验证指针在enclave内并修改其值。
    ecall_pointer_in_out函数验证指针在enclave内并修改其值。
  4. OCALL指针属性测试:
    ocall_pointer_attr函数测试OCALL中的指针属性,包括用户检查、输入和输出指针。
  5. 字符串指针:
    ecall_pointer_string函数处理可修改的字符串指针。
    ecall_pointer_string_const函数处理不可修改的字符串指针。
  6. 指针大小和计数:
    ecall_pointer_size函数处理指定长度的指针。
    ecall_pointer_count函数处理指定元素数量的指针数组。
  7. 只读指针:
    ecall_pointer_isptr_readonly函数处理只读指针。
  • Pointers.edl
/* Pointers.edl - Samples for pointer attributes. */
enclave {/* * Following keywords/attributes are supported for pointers in Edger8r: *      in, out, user_check, *      string, wstring,*      const, size, count, isptr, readonly*/trusted {/** [user_check]:*      the pointer won't be validated, and the buffer pointed by*      'val' is not copied into the enclave either. But Enclave *      can modify the memory pointed by 'val'.*/public size_t ecall_pointer_user_check([user_check] void *val, size_t sz);/** [in]:*      buffer with the same size will be allocated inside the enclave,*      content pointed by 'val' will be copied into the new allocated*      memory inside. Any changes performed inside the enclave will not *      affect the buffer outside.*/public void ecall_pointer_in([in] int *val);/** [out]:*      buffer with the same size will be allocated inside the enclave,*      but the content pointed by 'val' won't be copied. But after return, *      the buffer inside the enclave will copied into outside 'val'.*/public void ecall_pointer_out([out] int *val);/** [in, out]:*      buffer with the same size will be allocated inside the enclave,*      the content pointed by 'val' will be copied either. After return, *      the buffer inside the enclave will by copied into outside 'val' again.*/public void ecall_pointer_in_out([in, out] int *val);/** [string]:*      the attribute tells Edger8r 'str' is NULL terminated string, so strlen *      will be used to count the length of buffer pointed by 'str'.*/public void ecall_pointer_string([in, out, string] char *str);/** [const]:*      the attribute tells Edger8r the buffer pointed by 'str' cannot be modified,*      so users cannot decorate 'str' with [out] attribute anymore.*/public void ecall_pointer_string_const([in, string] const char *str);/** [size]:*      the attribute tells Edger8r the length of buffer in byte pointed by 'ptr' *      (shall be copied or not). * Note: Users shall not specify [size] on [string] parameters.*/public void ecall_pointer_size([in, out, size=len] void *ptr, size_t len);/** [count]:*      the attribute tells Edger8r the number of integers to be copied from 'arr'.*/public void ecall_pointer_count([in, out, count=cnt] int *arr, size_t cnt);/** [isptr]:*      tells Edger8r the user defined type is a pointer; * [readonly]:*      forbids the buffer allocated inside the enclave to be copied back to App*      (cannot use with [out]).*/public void ecall_pointer_isptr_readonly([in, isptr, readonly, size=len] buffer_t buf, size_t len);};/** Users can define multiple trusted/untrusted blocks, * edger8r will merged them into one trusted/untrusted block.*/trusted {/** Test pointer attributes in OCALLs*/public void ocall_pointer_attr(void);};untrusted {/** [user_check]:*      the pointer won't be verified, and the buffer pointed by 'val' is not *      copied to outside buffer either. Besides 'App' cannot modify the memory *      pointer by 'val'.*/void ocall_pointer_user_check([user_check] int *val);/** [in]:*      buffer with the same size will be allocated in 'App' side, the content *      pointed by 'val' will be copied into the new allocated memory outside. *      Any changes performed by 'App' will not affect the buffer pointed by 'val'.*/void ocall_pointer_in([in] int *val);/** [out]:*      buffer with the same size will be allocated in 'App' side, the content*      pointed by 'val' won't be copied. But after return, the buffer outside*      will be copied into the enclave.*/void ocall_pointer_out([out] int *val);/** [in, out]:*      buffer with the same size will be allocated in 'App' side, the content*      pointed by 'val' will be copied either. After return, the buffer outside *      will copied into the enclave.*/void ocall_pointer_in_out([in, out] int *val);};};
3.2.2.4 Enclave/Edger8rSyntax/Types.cpp
#include "sgx_trts.h"
#include "../Enclave.h"
#include "Enclave_t.h"
#include <limits>
#include <cmath>/* 用于消除“未使用变量”警告 */
#define UNUSED(val) (void)(val)#define ULP 2/* 用于比较double变量以避免编译警告 */
bool  almost_equal(double x, double y)
{/* 机器epsilon必须根据较大值的量级进行缩放并乘以所需的ULP(最后一位的单位)精度 */return std::abs(x-y) <= std::numeric_limits<double>::epsilon() * std::abs(x+y) * ULP;
}/* 用于比较float变量以避免编译警告 */
bool  almost_equal(float x, float y)
{/* 机器epsilon必须根据较大值的量级进行缩放并乘以所需的ULP(最后一位的单位)精度 */return std::abs(x-y) <= std::numeric_limits<float>::epsilon() * std::abs(x+y) * ULP;
}/* ecall_type_char:*   [char]值由App传递。*/
void ecall_type_char(char val)
{assert(val == 0x12);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_int:*   [int]值由App传递。*/
void ecall_type_int(int val)
{assert(val == 1234);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_float:*   [float]值由App传递。*/
void ecall_type_float(float val)
{assert(almost_equal(val, (float)1234.0));
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_double:*   [double]值由App传递。*/
void ecall_type_double(double val)
{assert(almost_equal(val, (double)1234.5678));
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_size_t:*   [size_t]值由App传递。*/
void ecall_type_size_t(size_t val)
{assert(val == (size_t)12345678);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_wchar_t:*   [wchar_t]值由App传递。*/
void ecall_type_wchar_t(wchar_t val)
{assert(val == (wchar_t)0x1234);
#ifndef DEBUGUNUSED(val);
#endif
}/* ecall_type_struct:*   struct_foo_t在EDL中定义,可以在ECALL中使用。*/
void ecall_type_struct(struct struct_foo_t val)
{assert(val.struct_foo_0 == 1234);assert(val.struct_foo_1 == 5678);
#ifndef DEBUGUNUSED(val);
#endif
}/** ecall_type_enum_union:*   enum_foo_t/union_foo_t在EDL中定义*   并且可以在ECALL中使用。*/
void ecall_type_enum_union(enum enum_foo_t val1, union union_foo_t *val2)
{if (sgx_is_outside_enclave(val2, sizeof(union union_foo_t)) != 1)abort();val2->union_foo_0 = 1;val2->union_foo_1 = 2; /* 覆盖union_foo_0 */assert(val1 == ENUM_FOO_0);
#ifndef DEBUGUNUSED(val1);
#endif
}

    这段代码展示了如何在SGX中处理各种基本类型,包括浮点数、整数、结构体、枚举和联合体。通过详细的注释和步骤分析,读者可以更好地理解SGX中基本类型的工作原理和实现细节。代码中涵盖了如何接收和验证不同类型的值,以及如何处理浮点数比较等操作。

  • Types.edl
/* Types.edl - Samples for basic types. */
enclave {/* * Following types can be supported in Edger8r: *      char, short, int, float, double, void, *      int8_t, int16_t, int32_t, int64_t,*      size_t, wchar_t, *      uint8_t, uint16_t, uint32_t, uint64_t, *      unsigned, struct, enum, union.*//** We will demo few types in ECALL functions, data * types in OCALL functions can be handled either.*//* structure definition */struct struct_foo_t {/* Basic types can be used in structure. */uint32_t struct_foo_0;uint64_t struct_foo_1;};/* enum definition */enum enum_foo_t {ENUM_FOO_0 = 0,ENUM_FOO_1 = 1};/* union definition */union union_foo_t {uint32_t union_foo_0;uint32_t union_foo_1;uint64_t union_foo_3;};trusted {public void ecall_type_char(char val);public void ecall_type_int(int val);public void ecall_type_float(float val);public void ecall_type_double(double val);public void ecall_type_size_t(size_t val);public void ecall_type_wchar_t(wchar_t val);public void ecall_type_struct(struct struct_foo_t val);public void ecall_type_enum_union(enum enum_foo_t val1, [user_check] union union_foo_t *val2);/* for using user defined types, please refer to Pointers.edl, Arrays.edl. */};
};

3.2.3 Enclave/TrustedLibrary文件夹

3.2.3.1 Enclave/TrustedLibrary/Libc.cpp
#include <string.h>
#include "sgx_cpuid.h"
#include "sgx_trts.h"
#include "../Enclave.h"
#include "Enclave_t.h"/* ecall_malloc_free:*   使用malloc/free分配/释放受信任的内存。*/
void ecall_malloc_free(void)
{void *ptr = malloc(100); // 分配100字节的受信任内存assert(ptr != NULL); // 确保内存分配成功memset(ptr, 0x0, 100); // 将分配的内存设置为0free(ptr); // 释放内存
}/* ecall_sgx_cpuid:*   使用sgx_cpuid获取CPU特性和类型。*/
void ecall_sgx_cpuid(int cpuinfo[4], int leaf)
{sgx_status_t ret = sgx_cpuid(cpuinfo, leaf); // 获取指定leaf的CPU信息if (ret != SGX_SUCCESS) // 如果获取失败,则终止执行abort();
}

    这段代码展示了如何在Intel SGX中使用malloc/free分配和释放受信任的内存,以及如何使用sgx_cpuid获取CPU特性和类型。以下是主要功能和步骤的详细分析:

  • 内存分配和释放:
    ecall_malloc_free函数使用malloc分配100字节的受信任内存,并确保内存分配成功。
    使用memset将分配的内存设置为0。
    使用free释放内存。
  • 获取CPU特性和类型:
    ecall_sgx_cpuid函数使用sgx_cpuid获取指定leaf的CPU信息。
    如果获取CPU信息失败,则终止执行。
3.2.3.2 Enclave/TrustedLibrary/Libcxx.cpp
#include <cstdlib>
#include <string>#include "../Enclave.h"
#include "Enclave_t.h"/** ecall_exception:*   在enclave内部抛出/捕获C++异常。*/void ecall_exception(void)
{std::string foo = "foo";try {throw std::runtime_error(foo); // 抛出runtime_error异常}catch (std::runtime_error const& e) {assert(foo == e.what()); // 捕获runtime_error异常,并验证异常信息std::runtime_error clone(""); // 创建一个空的runtime_error异常clone = e; // 复制异常assert(foo == clone.what()); // 验证复制的异常信息}catch (...) {assert(false); // 捕获所有其他异常,并断言失败}
}#include <map>
#include <algorithm>using namespace std;/** ecall_map:*   在enclave中使用STL <map>。*/
void ecall_map(void)
{typedef map<char, int, less<char> > map_t; // 定义一个字符到整数的映射类型typedef map_t::value_type map_value; // 定义映射的值类型map_t m; // 创建一个映射实例m.insert(map_value('a', 1)); // 插入键值对('a', 1)m.insert(map_value('b', 2)); // 插入键值对('b', 2)m.insert(map_value('c', 3)); // 插入键值对('c', 3)m.insert(map_value('d', 4)); // 插入键值对('d', 4)assert(m['a'] == 1); // 验证键'a'的值为1assert(m['b'] == 2); // 验证键'b'的值为2assert(m['c'] == 3); // 验证键'c'的值为3assert(m['d'] == 4); // 验证键'd'的值为4assert(m.find('e') == m.end()); // 验证键'e'不存在于映射中return;
}

    这段代码展示了在Intel SGX中使用C++异常处理STL容器(如std::map)的示例。以下是主要功能和步骤的详细分析:

  • 异常处理:
    ecall_exception函数在enclave内部抛出和捕获C++异常
    使用try-catch块抛出std::runtime_error异常,并在catch块中捕获异常。
    验证捕获的异常信息是否与预期值一致。
    创建并复制异常对象,验证复制的异常信息是否与原始异常一致。
  • STL容器std::map的使用:
    ecall_map函数在enclave内部使用STL容器std::map
    定义一个字符到整数的映射类型,并插入一些键值对。
    验证插入的键值对是否正确。
    检查某个键是否存在于映射中。
3.2.3.3 Enclave/TrustedLibrary/Thread.cpp
#include "../Enclave.h"
#include "Enclave_t.h"#include "sgx_thread.h"static size_t global_counter = 0; // 全局计数器
static sgx_thread_mutex_t global_mutex = SGX_THREAD_MUTEX_INITIALIZER; // 全局互斥锁#define BUFFER_SIZE 50 // 缓冲区大小typedef struct {int buf[BUFFER_SIZE]; // 缓冲区数组int occupied; // 已占用的缓冲区数量int nextin; // 下一个写入位置int nextout; // 下一个读取位置sgx_thread_mutex_t mutex; // 互斥锁sgx_thread_cond_t more; // 条件变量,表示有更多数据sgx_thread_cond_t less; // 条件变量,表示有更少数据
} cond_buffer_t;static cond_buffer_t buffer = {{0, 0, 0, 0, 0, 0}, 0, 0, 0,SGX_THREAD_MUTEX_INITIALIZER, SGX_THREAD_COND_INITIALIZER, SGX_THREAD_COND_INITIALIZER}; // 初始化缓冲区/** ecall_increase_counter:*   在enclave中使用线程API。*/
size_t ecall_increase_counter(void)
{size_t ret = 0;for (int i = 0; i < LOOPS_PER_THREAD; i++) {sgx_thread_mutex_lock(&global_mutex); // 加锁/* 互斥地增加计数器 */size_t tmp = global_counter;global_counter = ++tmp;if (4*LOOPS_PER_THREAD == global_counter)ret = global_counter;sgx_thread_mutex_unlock(&global_mutex); // 解锁}return ret;
}void ecall_producer(void)
{for (int i = 0; i < 4*LOOPS_PER_THREAD; i++) {cond_buffer_t *b = &buffer;sgx_thread_mutex_lock(&b->mutex); // 加锁while (b->occupied >= BUFFER_SIZE)sgx_thread_cond_wait(&b->less, &b->mutex); // 等待缓冲区有空闲空间b->buf[b->nextin] = b->nextin; // 写入数据b->nextin++;b->nextin %= BUFFER_SIZE; // 循环使用缓冲区b->occupied++; // 增加占用计数sgx_thread_cond_signal(&b->more); // 通知消费者有更多数据sgx_thread_mutex_unlock(&b->mutex); // 解锁}
}void ecall_consumer(void)
{for (int i = 0; i < LOOPS_PER_THREAD; i++) {cond_buffer_t *b = &buffer;sgx_thread_mutex_lock(&b->mutex); // 加锁while(b->occupied <= 0)sgx_thread_cond_wait(&b->more, &b->mutex); // 等待缓冲区有数据b->buf[b->nextout++] = 0; // 读取数据b->nextout %= BUFFER_SIZE; // 循环使用缓冲区b->occupied--; // 减少占用计数sgx_thread_cond_signal(&b->less); // 通知生产者有空闲空间sgx_thread_mutex_unlock(&b->mutex); // 解锁}
}

    这段代码展示了如何在Intel SGX中使用线程同步原语(如互斥锁和条件变量)进行多线程编程。以下是主要功能和步骤的详细分析:

  1. 全局计数器的增加:
    ecall_increase_counter函数使用互斥锁global_mutex来保护全局计数器global_counter
    在循环中,函数锁定互斥锁,安全地增加计数器,然后解锁互斥锁。
  2. 生产者-消费者模型:
  • 使用cond_buffer_t结构体定义一个带有互斥锁条件变量的缓冲区。
  • ecall_producer函数实现生产者逻辑:
    在循环中,函数锁定缓冲区的互斥锁。
    如果缓冲区已满,等待less条件变量。
    向缓冲区写入数据,更新写入位置和占用计数。
    发出more条件变量信号,通知消费者有更多数据。
    解锁互斥锁。
  • ecall_consumer函数实现消费者逻辑:
    在循环中,函数锁定缓冲区的互斥锁。
    如果缓冲区为空,等待more条件变量。
    从缓冲区读取数据,更新读取位置和占用计数。
    发出less条件变量信号,通知生产者有空闲空间。
    解锁互斥锁。

四. 感谢支持

    完结撒花!后续将持续输出,形成Intel SGX的系列教程,并结合密码学算法设计更复杂功能。希望看到这里的小伙伴能点个关注,也欢迎志同道合的小伙伴一起广泛交流。
    码字实属不易,如果本文对你有10分帮助,就赏个10分把,感谢各位大佬支持!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/37785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文全概括,建议收藏,那些你不可错过的IC设计书籍合集(可下载)

集成电路设计工程师的角色不仅是推动技术创新的中坚力量&#xff0c;更是实现产品从概念到现实的关键桥梁。随着对高性能、低功耗芯片的需求不断增长&#xff0c;IC设计工程师的专业技能和知识深度成为了衡量其职业价值的重要标准。无论是在数字逻辑设计、功能验证、可测试性设…

JMeter--定时执行的方法

原文网址&#xff1a;JMeter--定时执行的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍JMeter如何使用定时器定时执行测试任务。 Java技术星球&#xff1a;way2j.com 方法 第一步&#xff1a;新建定时器 右键测试任务> Add > Timer> Constant Timer 如下图所示…

qt中数据库和excel互导数据————附带详细步骤和代码

文章目录 0 背景1 准备QXlsx环境1.1 cmake安装使用1.2 qmake使用 2 把excel数据导出到mysql数据库3 把mysql数据库的数据写入到excel4 完整代码5 项目代码仓库 0 背景 因为需要批量导入和导出数据&#xff0c;所以需要用到excel。实现把数据库的数据导入到excel中&#xff0c;…

解析QAnything启动命令过程

一.启动命令过程日志 启动命令bash ./run.sh -c local -i 0 -b hf -m Qwen-1_8B-Chat -t qwen-7b-chat。输入日志如下所示&#xff1a; rootMM-202203161213:/mnt/l/20230918_RAG方向/QAnything# bash ./run.sh -c local -i 0 -b hf -m Qwen-1_8B-Chat -t qwen-7b-chat From …

理性决策的艺术:从购房到择偶的数学智慧;37% 规则,做出最佳决策的秘诀;用数学模型解决人生难题

在面对人生重大决策时&#xff0c;如购房或择偶&#xff0c;我们常常感到迷茫和困惑。然而&#xff0c;如果我们能够将这些看似复杂的问题简化为数学模型&#xff0c;我们就能以更加理性和系统的方式做出决策。 37%规则 1950年代&#xff0c;当时几位数学家开始研究这样一个问…

值得收藏!盘点那些适合普通人方便又好用的AIGC工具!(下)

【导读】接上一篇文章&#xff0c;盘点国内外适合普通人能够轻松上手的AIGC工具&#xff08;上&#xff09;。今天又为大家整理了一些好用又方便的AI设计工具、AI办公工具、AI编程工具、AI指令工具和AI检测工具&#xff0c;如果有没更新到的工具也欢迎大家评论区交流。 一 、A…

C#/WPF 自制截图工具

在日常使用电脑办公时&#xff0c;我们经常遇到需要截图然后保存图片&#xff0c;我们往往需要借助安装截图工具才能实现&#xff0c;现在我们通过C#自制截图工具&#xff0c;也能够轻松进行截图。 我们可以通过C#调用WindousAPI来实现截图&#xff0c;实例代码如下&#xff1a…

AI基本概念(人工智能、机器学习、深度学习)

人工智能 、 机器学习、 深度学习的概念和关系 人工智能 &#xff08;Artificial Intelligence&#xff09;AI- 机器展现出人类智慧机器学习 &#xff08;Machine Learning) ML, 达到人工智能的方法深度学习 &#xff08;Deep Learning&#xff09;DL,执行机器学习的技术 从范围…

算法 —— 滑动窗口

目录 长度最小的子数组 无重复字符的最长子串 最大连续1的个数 将x减到0的最小操作数 找到字符串中所有字母异位词 长度最小的子数组 sum比target小就进窗口&#xff0c;sum比target大就出窗口&#xff0c;由于数组是正数&#xff0c;所以相加会使sum变大&#xff0c;相减…

DiskGeniusV5.6.0.1565发布!

DiskGenius是一款功能强大的磁盘管理和数据恢复工具&#xff0c;V5.6.0.1565上线。新版本变化比较大&#xff0c;增加新的功能&#xff0c;修正已经问题&#xff0c;值得试一下。提醒大家&#xff0c;磁盘管理软件涉及数据安全&#xff0c;请始终使用最新版本&#xff01; 下面…

C++ initializer_list类型推导

目录 initializer_list C自动类型推断 auto typeid decltype initializer_list<T> C支持统一初始化{ }&#xff0c;出现了一个新的类型initializer_list<T>&#xff0c;一切类型都可以用列表初始化。提供了一种更加灵活、安全和明确的方式来初始化对象。 class…

IO-Link OD介绍

IO-Link OD&#xff08;On-request Data&#xff0c;按需数据&#xff09;是IO-Link通信中的一种重要数据类型&#xff0c;主要用于参数读写、指令交互、事件上传等动作。以下是关于IO-Link OD的结构、构成以及功能使用的详细说明&#xff1a; 结构与构成 定义&#xff1a;OD…

同一个excel表格,为什么在有的电脑上会显示#NAME?

一、哪些情况会产生#NAME?的报错 1.公式名称拼写错误 比如求和函数SUM&#xff0c;如果写成SUN就会提示#NAME&#xff1f;报错。 2.公式中的文本值未添加双引号 如下图&#xff1a; VLOOKUP(丙,A:B,2,0) 公式的计算结果会返回错误值#NAME?&#xff0c;这是因为公式中文本…

【PLC】三菱PLC如何和汇川伺服实现485通信

前言 一开始选用的是汇川SV660P脉冲型伺服&#xff0c;由于生产需求需要对伺服的个别参数进行读取和写入操作&#xff0c;但是SV660P并不支持这种情况&#xff0c;因此需要使用485通信来满足。PLC这边选用的是三菱FX5U。 开始 1、首先准备按照下图的引脚提示准备好一根带屏蔽…

(七)glDrawArry绘制

几何数据&#xff1a;vao和vbo 材质程序&#xff1a;vs和fs(顶点着色器和片元着色器) 接下来只需要告诉GPU&#xff0c;使用几何数据和材质程序来进行绘制。 #include <glad/glad.h>//glad必须在glfw头文件之前包含 #include <GLFW/glfw3.h> #include <iostrea…

PostgreSQL的学习心得和知识总结(一百四十七)|深入理解PostgreSQL数据库之transaction chain的使用和实现

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

2024年文化传播与对外交流国际学术会议(ICCCFE 2024)

2024年文化传播与对外交流国际学术会议&#xff08;ICCCFE 2024&#xff09; 2024 International Conference on Cultural Communication and Foreign Exchange(ICCCFE 2024) 会议简介&#xff1a; 2024年文化传播与对外交流国际学术会议&#xff08;ICCCFE 2024&#xff09;定…

clion开发51 没有创建成功可能是Clion版本问题

安装插件 PlatformlO for CLion 进入这个网站下载get-platformio.py https://docs.platformio.org/en/latest/core/installation/methods/installer-script.html#local-download-macos-linux-windows 点击 Installation Methods 选择 Local Download (macOS/Linux/Windows) 点…

小阿轩yx-案例:MySQL主从复制与读写分离

小阿轩yx-案例&#xff1a;MySQL主从复制与读写分离 案例分析 概述 实际生产环境中 如果对数据库读和写都在同一个数据库服务器中操作&#xff0c;无论在安全性、高可用性还是高并发等各个方面都完全不能满足实际需求一般都是通过主从复制&#xff08;Master-Slave&#xf…

MSPG3507——蓝牙接收数据显示在OLED,滴答定时器延时500MS

#include "ti_msp_dl_config.h" #include "OLED.h" #include "stdio.h"volatile unsigned int delay_times 0;//搭配滴答定时器实现的精确ms延时 void delay_ms(unsigned int ms) {delay_times ms;while( delay_times ! 0 ); } int a0; …