Ubuntu 18.04下Intel SGX應用程式程式開發——獲得OCALL呼叫的返回值

CoderZjz發表於2021-03-05

本文中,我們介紹在Enclave函式中呼叫不可信OCALL函式,並獲得OCALL函式的返回值。

1. 複製SampleEnclave示例並建立自己的OcallRetSum專案

SampleEnclave示例實現了安全printf函式,可以被用來安全列印我們需要列印的字串。我們使用OCALL計算兩個整數之間的所有整數的累加和(包括這兩個整數),並呼叫printf函式列印複製的目的字串。我們把SampleEnclave示例複製到自己的資料夾中,基於該示例,我們開發一個在Enclave函式中呼叫OCALL函式計算兩個整數之間的所有整數的累加和(包括這兩個整數)。我們把複製得到的SampleEnclave資料夾名稱改成OcallRetSum資料夾。

將檔案中的冗餘檔案刪除僅剩下圖目錄

image-20210305113512956

2. 各個檔案內容介紹

2.1 App資料夾

該資料夾存放應用程式中的不可信程式碼部分,共有2個檔案和2個資料夾,App.cpp檔案、App.h檔案、Edger8rSyntax資料夾和TrustedLibrary資料夾

  • App.cpp檔案:該檔案是應用程式中的不可信部分程式碼,其中包括了建立Enclave及銷燬Enclave的程式碼,也定義了一些相關的返回碼供使用者檢視Enclave程式的執行狀態。其中的main函式是整個專案的入口函式。是我們要修改的檔案。

  • App.h檔案:該檔案是應用程式中的不可信部分程式碼的標頭檔案,定義了一些巨集常量和函式宣告。我們不用進行更改。

  • Edger8rSyntax資料夾:該資料夾提供了一些工具,我們不用進行更改。

  • TrustedLibrary資料夾:該資料夾提供了一些函式庫,我們不用進行更改。、

2.2 Enclave資料夾

該資料夾存放應用程式中的可信程式碼部分和可信與不可信程式碼介面檔案,共有6個檔案和2個資料夾,Enclave.config.xml檔案、Enclave.cpp檔案、Enclave.h檔案、Enclave.edl檔案、Enclave.lds檔案、Enclave_private.pem檔案、Edger8rSyntax資料夾和TrustedLibrary資料夾

  • Enclave.config.xml檔案:該檔案是Enclave的配置檔案,定義了Enclave的後設資料資訊,我們可以不用更改。

  • Enclave.cpp檔案:該檔案是應用程式中的可信部分程式碼,包括了可信函式的實現,是我們要修改的檔案。

  • Enclave.h檔案:該檔案是應用程式中的可信部分程式碼的標頭檔案,定義了一些巨集常量和函式宣告。是我們要修改的檔案。

  • Enclave.edl檔案:該檔案是Enclave的介面定義檔案,定義了不可信程式碼呼叫可信程式碼的ECALL函式介面和可信程式碼呼叫不可信程式碼的OECALL函式介面,trusted{}中定義了ECALL函式介面,untrusted{}中定義了OECALL函式介面是我們要修改的檔案。

  • Enclave.lds檔案:該檔案定義了一些Enclave可執行檔案資訊,我們不用更改。

  • Enclave_private.pem檔案:該檔案是SGX生成的私鑰,我們不用更改。

  • Edger8rSyntax資料夾:該資料夾提供了一些工具,我們不用進行更改。

  • TrustedLibrary資料夾:該資料夾提供了一些函式庫,我們不用進行更改。

2.3 Include資料夾

該資料夾存放被Enclave介面定義檔案Enclave.edl使用的標頭檔案,包括一些巨集定義,我們不用管它。共只有一個user_types.h檔案。

  • user_types.h檔案:該檔案定義了使用者自定義的型別。

2.4 Makefile檔案

該檔案是專案的編譯檔案,定義了專案的編譯資訊,詳細說明還請參考http://blog.csdn.net/daichunkai123/article/details/78027895。是需要修改的部分。

3. 改寫程式碼

需要修改的檔案問為Enclave.edl檔案、Enclave.cpp檔案、Enclave.h檔案、App.cpp檔案和Makefile檔案

3.1 Enclave.edl檔案修改

我們要在Enclave.edl檔案中定義不可信程式碼呼叫可信函式的ECALL介面,比如我們定義的可信函式是ecall_myretsum,在該函式中我們呼叫OCALL函式ocall_retsum計算兩個整數之間的所有整數累加和,該函式沒有引數也沒有返回值。

但是定義的可信函式式ocall_retsum時,該函式有2個引數:起始整數start和結束整數end。該函式有一個整數返回值。

3.1.1 新增ECALL函式ecall_myretsum,將以下程式碼新增到Enclave.edl檔案中的untrusted{}之前。

trusted{
        public void ecall_myretsum();
};

同時因為需要start和end引數的返回值,還將下列程式碼新增到``untrusted{}`中

size_t ocall_retsum(size_t start, size_t end);

3.1.2 修改後的Enclave.edl檔案如下所示

enclave {
    
    include "user_types.h" /* buffer_t */

    /* Import ECALL/OCALL from sub-directory EDLs.
     *  [from]: specifies the location of EDL file. 
     *  [import]: specifies the functions to import, 
     *  [*]: implies to import all functions.
     */
    
    from "Edger8rSyntax/Types.edl" import *;
    from "Edger8rSyntax/Pointers.edl" import *;
    from "Edger8rSyntax/Arrays.edl" import *;
    from "Edger8rSyntax/Functions.edl" import *;

    from "TrustedLibrary/Libc.edl" import *;
    from "TrustedLibrary/Libcxx.edl" import ecall_exception, ecall_map;
    from "TrustedLibrary/Thread.edl" import *;

    /* 
     * ocall_print_string - invokes OCALL to display string buffer inside the enclave.
     *  [in]: copy the string buffer to App outside.
     *  [string]: specifies 'str' is a NULL terminated buffer.
     */

		trusted{
       	 	public void ecall_myretsum();
    	};


    untrusted {
        void ocall_print_string([in, string] const char *str);
				size_t ocall_retsum(size_t start, size_t end);
    };

};

3.2 Enclave.cpp檔案修改

在Enclave.cpp檔案中實現ecall_myretsum函式,該函式比較簡單,就是定義兩個整數引數和一個儲存返回值的整數變數,並呼叫OCALL函式ocall_retsum計算累加和,獲得計算結果,然後呼叫printf函式列印計算的累加和。

這個地方需要注意的是:獲得OCALL函式的返回值和獲得普通的C/C++函式的返回值是不一樣的,獲得C/C++函式的返回值可以直接通過賦值得到,但是獲得OCALL函式的返回值需要把儲存返回值的變數的地址傳給Enclave函式,OCALL函式執行完之後,SGX會把返回值複製到地址引數指向的記憶體地址處,從而將返回值返回給想返回的變數。

事實上,在SGX中,OCALL函式執行時是有返回值的,返回的是該函式執行的狀態,可以通過賦值獲得OCALL函式執行的狀態,從而判斷有沒有正確執行。

3.2.1 將以下程式碼新增到Enclave.cpp檔案中

void ecall_myretsum()
{
    size_t start = 5;
    size_t end = 10;
    size_t sum = 0;
 
    ocall_retsum(&sum,start, end);
    printf("%d\n", sum);
}

3.2.2 修改後的Enclave.cpp檔案如下所示

#include "Enclave.h"
#include "Enclave_t.h" /* print_string */
#include <stdarg.h>
#include <stdio.h> /* vsnprintf */
#include <string.h>

/* 
 * 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;
}

void ecall_myretsum()
{
    size_t start = 5;
    size_t end = 10;
    size_t sum = 0;
 
    ocall_retsum(&sum,start, end);
    printf("%d\n", sum);
}

3.3 Enclave.h檔案修改

我們在Enclave.h檔案中新增ecall_myretsum函式宣告,該檔案修改比較簡單。

3.3.1將以下程式碼新增到Enclave.h檔案中

void ecall_myretsum();

3.3.2 修改後的Enclave.h檔案如下所示

#ifndef _ENCLAVE_H_
#define _ENCLAVE_H_

#include <stdlib.h>
#include <assert.h>

#if defined(__cplusplus)
extern "C" {
#endif

void printf(const char *fmt, ...);
void ecall_myretsum();

#if defined(__cplusplus)
}
#endif

#endif /* !_ENCLAVE_H_ */

3.4 App.cpp檔案修改

我們在該檔案中呼叫自定義的OCALL函式ocall_retsum並呼叫自定義的ECALL函式ecall_myretsum。

3.4.1 修改main函式

由於我們不需要Edger8rSyntax和TrustedLibrary相關的函式呼叫,所以我們把main函式中與這兩部分相關的函式呼叫刪除,也就是把下面部分刪除:

/* Utilize edger8r attributes */
    edger8r_array_attributes();
    edger8r_pointer_attributes();
    edger8r_type_attributes();
    edger8r_function_attributes();

/* Utilize trusted libraries */
		ecall_libc_functions();
		ecall_libcxx_functions();
		ecall_thread_functions();    

image-20210304164923690

3.4.2 main函式中插入函式呼叫

在main函式之前插入OCALL函式ocall_retsum的實現,該函式計算兩個整數之間的所有整數的累加和(包括這兩個整數),並返回計算的累加值。即將以下程式碼插入到main函式之前

size_t ocall_retsum(size_t start, size_t end){  
    size_t sum = 0;  
    for (; start <= end; start++)  
        sum += start;  
    return sum;  
}

在main函式中的Enclave建立和銷燬之間插入我們定義的ecall_myretsum函式的呼叫程式碼,也就是把以下程式碼放到sgx_destroy_enclave(global_eid);之前。global_eid引數是必須的,是enclave的id。

ecall_myretsum(global_eid);

3.4.3 刪除無關程式碼

我們也可以將main函式後面的無關程式碼給刪了,也就是刪除以下程式碼。

printf("Info: SampleEnclave successfully returned.\n");
printf("Enter a character before exit ...\n");
getchar();

修改後的main函式及其之前如下所示

size_t ocall_retsum(size_t start, size_t end){  
    size_t sum = 0;  
    for (; start <= end; start++)  
        sum += start;  
    return sum;  
}

/* Application entry */
int SGX_CDECL main(int argc, char *argv[])
{
    (void)(argc);
    (void)(argv);


    /* Initialize the enclave */
    if(initialize_enclave() < 0){
        printf("Enter a character before exit ...\n");
        getchar();
        return -1; 
    }

    ecall_myretsum(global_eid);
    /* Destroy the enclave */
    sgx_destroy_enclave(global_eid);
    
    return 0;
}

3.5 Makefile檔案修改

我們在Makefile檔案中修改編譯相關的資訊。主要更改

SGX_SDK ?= /opt/intel/sgxsdk
App_Cpp_Files := App/App.cpp $(wildcard App/Edger8rSyntax/*.cpp) $(wildcard App/TrustedLibrary/*.cpp)

Enclave_Cpp_Files := Enclave/Enclave.cpp $(wildcard Enclave/Edger8rSyntax/*.cpp) $(wildcard Enclave/TrustedLibrary/*.cpp)

3.5.1 修改SGX SDK路徑

如果SGX SDK路徑不是/opt/intel/sgxsdk,要將其改成為所在目錄下/home/zjz/sgxsdk,

image-20210304170155481

3.5.2 修改不可信程式碼編譯的原始檔

由於我們的不可信程式碼可能是多個檔案,因此,我們最好在App_Cpp_Files中包括App資料夾中的所有cpp檔案,更改後的結果如下。本專案中App_Cpp_Files可以不用更改。

image-20210304170232535

3.5.3 修改可信程式碼編譯的原始檔

由於我們的可信程式碼可能是多個檔案,因此,我們最好在Enclave_Cpp_Files中包括App資料夾中的所有cpp檔案,更改後的結果如下。本專案中Enclave_Cpp_Files可以不用更改。

image-20210304170133548

4. 編譯檔案

開啟檔案下的終端輸入make

image-20210305163529173

image-20210305163601054

5. 執行程式

執行 ./app 執行程式

其中遇到一個lib庫的問題 使用export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/zjz/sgxsdk/lib64 解決後重新執行

最終成功輸出兩數之和為45

image-20210305163623973

相關文章