contributed by <zhanyangch
, tina0405
, LinRiver
>
在 android 上支援 TEE 的軟體,包含
TEE 處理器是獨立的微處理器或主處理器虛擬實例
TEE processor is isolated from the rest of the system using memory and I/O protection mechanisms supported by the hardware.
應用 DRM,mobile payments, secure banking, full-disk encryption, multi-factor authentication, device reset protection, replay-protected persistent storage, wireless display ("cast") of protected content, secure PIN and fingerprint processing, and even malware detection.
API
運行在主處理器的應用程式可以透過 Trusty APIs 連接可信任的應用並交換訊息,這個過程是completely asynchronous
Trusted applications run as isolated processes under the Trusty OS kernel. Each process runs in its own virtual memory sandbox utilizing the MMU capabilities of the TEE processor
Trusty 應用為單執行序
Trusty applications 只在載入時初始化一次且會一直在記憶體中直到 TEE 處理器被 reset,Trusty 不支援動態載入
Trusted applications are written as event-driven servers waiting for commands from other applications or from applications running on the main processor
Currently all Trusty applications are developed by a single party and packaged with the Trusty kernel image. The entire image is signed and verified by the bootloader during boot. Third-party application development is not supported in this version of Trusty
Intel moves on Blockchain, Self-Driving Cars
Intel 與 Microsoft 合作投入新興市場開發自動載具與區塊鏈, 此區塊鏈平台稱為 Coco Framwork
. 此平台整合了 Intel 的 Software Guard Extensions (SGX) 來強化在區塊鏈隱私和資料安全性. Coco Framework
應用了 SGX 所提供的 TEE 來保護執行中的資料與程式安全性.
IoT and TEE
經由醫療感測裝置所蒐集到的用戶資料, 一般若有需要會傳送到個人照護醫生手上進行了解. 然而有時候可能會因應其他機構或是裝置需求, 傳送至他處進行資料分析. 透過 TEE 所提供的隔離環境進行資料傳送可確保其正確性.
extern void setreturn(void *addr, size_t length);
/*
* Input:
* $r0 -- pointer to return buffer
* $r1 -- length of return buffer
*
* Output:
* none
*/
.globl setreturn
.type setreturn,@function
.text
setreturn:
ssr $r0, 6
ssr $r1, 7
ret
.Lend:
.size setreturn,.Lend-setreturn
先看了 wikipedia 介紹的 (ELF)Executable and Linkable Format
但因為中間有部份 section 被略過,而在課本深入理解計算機系統中有提到
典型的 ELF 可重定位目標文件格式,參考 7.4 節
| ELF header | ->
| .text | -> 放置程式碼
| .rodata | -> 只讀數據 ,ro 是 rom 的意思
| .data | -> 放置初始化的 globle 和 static 變數
| .bss | -> 放置未初始化或令為 0 的 globle 和 static 變數
| .symtab | -> 放置定義和引用的 function 還有 global 變數的信息
| .rel.text | -> 程式段的重定位資訊
| .rel.data | -> 資料段的重定位資訊
| .debug | -> 除錯資訊
| .line | -> 原本行號和 .text 的對應,加入 -g 後才會有這 table
| .strtab | -> 字串表
|section header table| ->
data 劃分為初始化與未初始化的原因:
利用 objdump -x 執行檔
察看 section 的類型
objdump -x rtlib
顯示格式: file format elf32-little
參考資料
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000512 00001000 00001000 00000074 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .init 0000000e 00001512 00001512 00000586 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .fini 00000008 00001520 00001520 00000594 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
3 .rodata 00000018 00001528 00001528 0000059c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .data 00000430 00001640 00001640 000005b4 2**2
CONTENTS, ALLOC, LOAD, DATA
5 .eh_frame 00000004 00001a70 00001a70 000009e4 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .ctors 00000008 00001a74 00001a74 000009e8 2**2
CONTENTS, ALLOC, LOAD, DATA
7 .dtors 00000008 00001a7c 00001a7c 000009f0 2**2
CONTENTS, ALLOC, LOAD, DATA
8 .bss 00000024 00001a84 00001a84 000009f8 2**2
ALLOC
9 .comment 0000003b 00000000 00000000 000009f8 2**0
CONTENTS, READONLY
10 .debug_aranges 00000150 00000000 00000000 00000a33 2**0
CONTENTS, READONLY, DEBUGGING
11 .debug_info 00003538 00000000 00000000 00000b83 2**0
CONTENTS, READONLY, DEBUGGING
12 .debug_abbrev 00000ead 00000000 00000000 000040bb 2**0
CONTENTS, READONLY, DEBUGGING
13 .debug_line 00000fb1 00000000 00000000 00004f68 2**0
CONTENTS, READONLY, DEBUGGING
14 .debug_frame 000001f8 00000000 00000000 00005f1c 2**2
CONTENTS, READONLY, DEBUGGING
15 .debug_str 00000a61 00000000 00000000 00006114 2**0
CONTENTS, READONLY, DEBUGGING
16 .debug_loc 000005eb 00000000 00000000 00006b75 2**0
CONTENTS, READONLY, DEBUGGING
17 .debug_ranges 00000098 00000000 00000000 00007160 2**0
CONTENTS, READONLY, DEBUGGING
2**n
意思:
.text
和 .init
來舉例的話, 1000+512 =1512 因為 .text
size 512 ,.text
的 file 結束再 74 ,所以 .init
的 file 結束在 74+512=586Idx Name Size VMA LMA File off Algn
0 .text 00000512 00001000 00001000 00000074 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .init 0000000e 00001512 00001512 00000586 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
參考使用 Gnu gprof 进行 Linux 平台下的程序分析
gcc -pg test.c
做出一份 a.out 執行檔
#include "stdio.h"
#include "stdlib.h"
void a(){
printf("\t\t+---call a() function\n");
}
void c(){
printf("\t\t+---call c() function\n");
}
int b(){
printf("\t+--- call b() function\n");
a();
c();
return 0;
}
int main(){
printf(" main() function()\n");
b();
}
./a.out
會排列出函式呼叫的順序,產生 gmon.out
的檔案 main() function()
+--- call b() function
+---call a() function
+---call c() function
gprof -b a.out gmon.out
會產生Flat profile:
Each sample counts as 0.01 seconds.
no time accumulated
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
0.00 0.00 0.00 1 0.00 0.00 a
0.00 0.00 0.00 1 0.00 0.00 b
0.00 0.00 0.00 1 0.00 0.00 c
Call graph
granularity: each sample hit covers 2 byte(s) no time propagated
index % time self children called name
0.00 0.00 1/1 b [2]
[1] 0.0 0.00 0.00 1 a [1]
-----------------------------------------------
0.00 0.00 1/1 main [9]
[2] 0.0 0.00 0.00 1 b [2]
0.00 0.00 1/1 a [1]
0.00 0.00 1/1 c [3]
-----------------------------------------------
0.00 0.00 1/1 b [2]
[3] 0.0 0.00 0.00 1 c [3]
-----------------------------------------------
Index by function name
[1] a [2] b [3] c
cflow test.c
生成出來的 gmon.out
gprof -b cflow gmon.out
會將函式使用的情況印出Each sample counts as 0.01 seconds.
no time accumulated
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
0.00 0.00 0.00 122 0.00 0.00 auto_processor
0.00 0.00 0.00 122 0.00 0.00 delete_parm_processor
0.00 0.00 0.00 121 0.00 0.00 move_parm_processor
0.00 0.00 0.00 114 0.00 0.00 _option_is_end
0.00 0.00 0.00 110 0.00 0.00 hash_string
0.00 0.00 0.00 110 0.00 0.00 hash_symbol_hasher
...
...
Call graph
granularity: each sample hit covers 2 byte(s) no time propagated
index % time self children called name
0.00 0.00 122/122 hash_do_for_each [31]
[1] 0.0 0.00 0.00 122 auto_processor [1]
-----------------------------------------------
0.00 0.00 122/122 hash_do_for_each [31]
[2] 0.0 0.00 0.00 122 delete_parm_processor [2]
-----------------------------------------------
0.00 0.00 121/121 hash_do_for_each [31]
[3] 0.0 0.00 0.00 121 move_parm_processor [3]
...
...
Graphviz 工具
將 test.c 畫成圖
GNU gprof 的原理,參考 GNU gprof
分析步驟 :
-pg
option also works with a command that both compiles and links-pg
是 compliation 和 link 的 option,如果沒使用就沒有 call-graph data通常在 compile 時加入 -pg
, 就代表著每一個 function 會去 call mcount (or _mcount, or __mcount, depending on the OS and compiler) 做為第一個操作之一
包含 profiling library 的 mcount routine,通常利用檢查 stack frame 去找 child 的地址和返回 original parent 的地址
SGX 之特性: 參考
SGX 之缺陷與改善:
SGX 之應用: 參考
當使用 native compiler 來編譯某程式時, 此程式只能執行於同編譯器的運行平台.
然而 cross complier 編譯的是預執行於目標平台上的程式. 舉例來說, 有一 cross compiler 雖運行於 windows 7 pc 上卻可編譯程式而運行於 Android smartphoe. Cross compiler 必須能夠在多重平台上編譯.
倘若以 GCC 為例來解釋, 則有三個概念須先了解:
Build
編譯 GCC 的平台
Host
運行 GCC 的平台
Target
GCC 編譯產生的程式所運行的平台
build = host = target 為 native compiler,例如 Ubuntu 的 GCC 就是native compiler
build = host,但 target 與前兩者不同,就是cross compiler
Calling Covention (呼叫慣例)
因為函式呼叫牽涉到參數的傳遞, 所以並不只是單純跳到那個 Address 執行程式碼再跳回來這麼簡單, 呼叫副程式(函式)的主程式, 需要知道怎麼填參數, 副程式(函式)才能接到參數後進行處理, 再將結果傳給主程式. 這段協定, 稱之為Calling Convention(呼叫慣例)
Libffi Package
有些程式在編譯期間可能不知道有什麼參數傳遞到函式. 舉例來說, 直譯器 (Interpreter) 可能在執行期間被告知關於呼叫函數上的參數的型態與數目, 而 Libffi 可以扮演這些函式從直譯器到編譯完成之程式碼的橋樑
Libffi 函式
在使用 Libffi 之前需要先創造出 ffi_cif 物件, 方便之後的多重呼叫. 在 ffi_cif 中的 cif 扮演著 call interface, ffi_prep_cif 是進一步為了 call interface object 所使用, 如下範例程式碼所示
#include <stdio.h>
#include <ffi.h>
int main()
{
ffi_cif cif;
ffi_type *args[1];
void *values[1];
char *s;
ffi_arg rc;
/* Initialize the argument info vectors */
args[0] = &ffi_type_pointer;
values[0] = &s;
/* Initialize the cif */
if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1,
&ffi_type_sint, args) == FFI_OK)
{
s = "Hello World!";
ffi_call(&cif, puts, &rc, values);
/* rc now holds the result of the call to puts */
/* values holds a pointer to the function's arg, so to
call puts() again all we need to do is change the
value of s */
s = "This is cool!";
ffi_call(&cif, puts, &rc, values);
}
return 0;
}
BOLOS 全名為 Blockchain Open Ledger Operating System, 是為一個嵌入式安全作業系統, 可執行於 Secure Elements, Hardware Security Modules 或 CPU enclave (TEE, Intel SGX)
在其系統架構中, 其應用程式運行在虛擬裝置上, 可在所有硬體上重新配置, 為其一大優點
下圖為透過應用程式來俯視整體系統運作, 在 Appliation 周圍的方塊可被視為 coprocessor, 皆受於 Application 的直接指令之下. 有些方塊除了接收指令外, 也能夠觸發事件產生, 舉例來說, 透過使用者按下按鈕後, application 下指令給 I/O peripheral 進行運作
BOLOS 被定義成兩層的 Delegation Model. 首先當應用程式執行時, BOLOS 便只提供基礎的服務給他們. 其後執行終了, BOLOS 不會有任何指令運作也就不會更動到 I/O 操作. 下圖為 USB delegation 流程
下圖 BOLOS 架構細節:
moxiebox execution environment 所對應 BOLOS 運作原理為:
自動機理論的主要目標是, 建立能夠對具有離散狀態的系統進行其動態行為的描述與分析, 在此系統中其訊號能夠被週期性地取樣.
這一類的機器具有以下三個特徵:
主動機有以下四種類型:
有限狀態主動機介紹:
A Model Checking-based Method for Verifying Web Application Design
IOT: Formal Security Analysis of Smart Embedded
Systems
Bounded Model Checker for C and C++
cbmc –unwind 20 binary_search.c
** Results:
[test_bsearch.assertion.1] assertion linear_search_result == binary_search_result: SUCCESS
[test_sort.assertion.1] assertion is_sorted(x, size): SUCCESS
** 0 of 2 failed (1 iteration)
VERIFICATION SUCCESSFUL
cbmc fft.c
[main.assertion.1] assertion !ok: FAILURE
** 1 of 1 failed (1 iteration)
VERIFICATION FAILED
利用 –trace 顯示詳細資料
cbmc –trace fft.c
State 1620 file fft.c line 175 function main thread 0
----------------------------------------------------
k=0u (00000000000000000000000000000000)
State 1621 file fft.c line 175 function main thread 0
----------------------------------------------------
k=0u (00000000000000000000000000000000)
State 1624 file fft.c line 175 function main thread 0
----------------------------------------------------
k=1u (00000000000000000000000000000001)
State 1628 file fft.c line 175 function main thread 0
----------------------------------------------------
k=2u (00000000000000000000000000000010)
State 1632 file fft.c line 175 function main thread 0
----------------------------------------------------
k=3u (00000000000000000000000000000011)
State 1636 file fft.c line 179 function main thread 0
----------------------------------------------------
expected_value=8u (00000000000000000000000000001000)
State 1637 file fft.c line 175 function main thread 0
----------------------------------------------------
k=4u (00000000000000000000000000000100)
State 1641 file fft.c line 171 function main thread 0
----------------------------------------------------
j=4u (00000000000000000000000000000100)
State 1644 file fft.c line 169 function main thread 0
----------------------------------------------------
i=4u (00000000000000000000000000000100)
Violated property:
file fft.c line 190 function main
assertion !ok
!(ok != FALSE)
可以發現錯誤發生在 i,j,k=4 時
full-stack-hello 利用 label as values 的方式實做 computed goto
修改 full-stack-hello 的 vm_run 部份,改為使用 switch dispatch,比較兩個實做的效能差異
修改後的 code
Performance counter stats for 'sudo chrt -f 99 taskset -c 0 ./as_exec tests/fib-iterative.s --input 46' (100 runs):
74 cache-misses # 14.327 % of all cache refs ( +- 5.00% )
516 cache-references ( +- 0.71% )
1,675 instructions # 0.45 insn per cycle
3,682 cycles ( +- 1.40% )
0.005200966 seconds time elapsed ( +- 7.80% )
Performance counter stats for 'sudo chrt -f 99 taskset -c 0 ./as_exec tests/fib-iterative.s --input 46' (100 runs):
78 cache-misses # 15.177 % of all cache refs ( +- 3.95% )
513 cache-references ( +- 0.77% )
1,675 instructions # 0.47 insn per cycle
3,591 cycles ( +- 2.02% )
0.005170761 seconds time elapsed ( +- 3.95% )
執行時間差距不明顯,computed goto 比 switch dispatch 快 0.5%
int fibonacciSearch(int number[], int length, int key) {
int k = findK(Fib, length + 1);
int m = length - Fib[k];
int x = k - 1;
int i = x;
if(number[i] < key)
i += m;
int result = -1;
while(Fib[x] > 0) {
if(number[i] < key)
i += Fib[--x];
else if(number[i] > key)
i -= Fib[--x];
else {
result = i;
break;
}
}
return result;
}
tail recursion :
我們拿 Fibonacci 的數列來做比較
// A tail recursive function to
// calculate n th fibnacci number
int fib(int n, int a = 0, int b = 1)
{
if (n == 0)
return a;
if (n == 1)
return b;
return fib(n-1, b, a+b);
}
int fib(int x) {
if (x == 0)
return 0;
if (x == 1)
return 1;
return fib(x-1)+fib(x-2);
}
fib (3,0,1) -> fib(2,1,1) -> fib(1,1,2)
return 2 <--- return 2 <--- return 2
fib(3)------> fib(2)------>fib(0)
| | return 2 return 1
----->fib(1) ------>fib(1)
return 1 return 1
這樣比較下來發現,因為 recursive call 因為已經先把一些能計算的值放在參數中便於使用(如迴圈的複雜度),自然會比 recursive (如樹狀複雜度)用到時才呼叫快
另一個好處是 STACK 的使用 , 參考
舉出具體而微的案例並用 perf 解釋細部差異 (特別是 cycle count 的落差)
Performance counter stats for 'sudo chrt -f 99 taskset -c 0 ./as_exec -x Fib' (100 runs):
1622 instructions # 0.33 insn per cycle
4914 cycles ( +- 1.46% )
0.008097402 seconds time elapsed ( +- 1.50% )
Performance counter stats for 'sudo chrt -f 99 taskset -c 0 ./as_exec -x Fib' (100 runs):
1633 instructions # 0.33 insn per cycle ( +- 0.69% )
4802 cycles ( +- 1.61% )
0.007691896 seconds time elapsed ( +- 1.39% )
以 input 為 10 , cycle 數量的確猶如上方分析的 tail_recursion 是花比較少時間而且 cycle 數也較少
做出不同 input 數值比較 (這裡列出 1~30 間隔為2,例如:1,3,5,7…)
但在 cycle 數方面
尋找鋸齒狀原因:目前猜測與 cache 有關,因為用 perf 觀察 cache-misses 發現 cycle 數的趨勢和 cache-misses 趨勢雷同
tail recursive 的 cycle
tail recursive 的 cache-misses
如何預測 input 為多少時會有較高的 cache-misses?