Skip to content

Commit 7215e29

Browse files
author
Dmitrii Kuvaiskii
committed
[PAL/Linux-SGX] Add AEX-Notify enabling code
This is a preparatory commit for AEX-Notify support. This commit: - Introduces the `sgx.experimental_enable_aex_notify` manifest option. - Adds architectural flags/bits for SECS, TCS, SSA data structures. - Adds a Gramine startup check whether AEX-Notify hardware feature is supported by the platform. - Adds dynamic enablement/disablement of AEX-Notify feature per enclave thread (enable on thread creation, disable on thread termination). Currently per-thread enablement is commented out, as otherwise Gramine would segfault (as in-enclave code doesn't yet implement AEX-Notify). Signed-off-by: Dmitrii Kuvaiskii <[email protected]>
1 parent bca2d41 commit 7215e29

14 files changed

+127
-2
lines changed

Documentation/manifest-syntax.rst

+24
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,30 @@ In addition, the application manifest must also contain ``sgx.debug = true``.
13921392

13931393
See :ref:`vtune-sgx-profiling` for more information.
13941394

1395+
Enabling AEX-Notify
1396+
^^^^^^^^^^^^^^^^^^^
1397+
1398+
::
1399+
1400+
sgx.experimental_enable_aex_notify = [true|false]
1401+
(Default: false)
1402+
1403+
When enabled, this option instructs Gramine to use the AEX-Notify hardware feature.
1404+
AEX-Notify is a flexible hardware extension that makes SGX enclaves interrupt
1405+
aware: enclaves can register a trusted handler to be run after an interrupt or
1406+
exception. AEX-Notify can be used as a building block for implementing
1407+
countermeasures against different types of interrupt-based attacks in software.
1408+
1409+
For more information on AEX-Notify, refer to the `academic paper
1410+
<https://www.usenix.org/conference/usenixsecurity23/presentation/constable>`__
1411+
and to the `official whitepaper
1412+
<https://cdrdv2-public.intel.com/736463/aex-notify-white-paper-public.pdf>`__.
1413+
1414+
.. warning::
1415+
Support for AEX-Notify in Gramine is not yet thoroughly tested and may
1416+
contain security vulnerabilities. This is temporary; the prefix
1417+
``experimental_`` will be removed in the future after thorough validation.
1418+
13951419
Deprecated options
13961420
------------------
13971421

pal/src/host/linux-sgx/generated_offsets.c

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const struct generated_offset generated_offsets[] = {
1515
/* defines from sgx_arch.h */
1616
DEFINE(SGX_FLAGS_DEBUG, SGX_FLAGS_DEBUG),
1717
DEFINE(SGX_FLAGS_MODE64BIT, SGX_FLAGS_MODE64BIT),
18+
DEFINE(SGX_FLAGS_AEXNOTIFY, SGX_FLAGS_AEXNOTIFY),
1819
DEFINE(SGX_XFRM_LEGACY, SGX_XFRM_LEGACY),
1920
DEFINE(SGX_XFRM_AVX, SGX_XFRM_AVX),
2021
DEFINE(SGX_XFRM_MPX, SGX_XFRM_MPX),
@@ -54,6 +55,7 @@ const struct generated_offset generated_offsets[] = {
5455
OFFSET_T(SGX_GPR_RFLAGS, sgx_pal_gpr_t, rflags),
5556
OFFSET_T(SGX_GPR_RIP, sgx_pal_gpr_t, rip),
5657
OFFSET_T(SGX_GPR_EXITINFO, sgx_pal_gpr_t, exitinfo),
58+
OFFSET_T(SGX_GPR_AEXNOTIFY, sgx_pal_gpr_t, aexnotify),
5759
DEFINE(SGX_GPR_SIZE, sizeof(sgx_pal_gpr_t)),
5860

5961
/* sgx_cpu_context_t */
@@ -103,6 +105,7 @@ const struct generated_offset generated_offsets[] = {
103105
OFFSET(SGX_HEAP_MIN, pal_enclave_tcb, heap_min),
104106
OFFSET(SGX_HEAP_MAX, pal_enclave_tcb, heap_max),
105107
OFFSET(SGX_CLEAR_CHILD_TID, pal_enclave_tcb, clear_child_tid),
108+
OFFSET(SGX_READY_FOR_AEX_NOTIFY, pal_enclave_tcb, ready_for_aex_notify),
106109

107110
/* struct pal_host_tcb aka PAL_HOST_TCB */
108111
OFFSET(PAL_HOST_TCB_TCS, pal_host_tcb, tcs),
@@ -123,6 +126,7 @@ const struct generated_offset generated_offsets[] = {
123126
OFFSET_T(TCS_OFS_LIMIT, sgx_arch_tcs_t, ofs_limit),
124127
OFFSET_T(TCS_OGS_LIMIT, sgx_arch_tcs_t, ogs_limit),
125128
DEFINE(TCS_SIZE, sizeof(sgx_arch_tcs_t)),
129+
DEFINE(TCS_FLAGS_AEXNOTIFY, TCS_FLAGS_AEXNOTIFY),
126130

127131
/* sgx_attributes_t */
128132
OFFSET_T(SGX_ATTRIBUTES_XFRM, sgx_attributes_t, xfrm),

pal/src/host/linux-sgx/host_framework.c

+18
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,24 @@ bool is_wrfsbase_supported(void) {
175175
return true;
176176
}
177177

178+
bool is_aexnotify_supported(void) {
179+
uint32_t cpuinfo[4];
180+
181+
cpuid(INTEL_SGX_LEAF, 1, cpuinfo);
182+
if (!((cpuinfo[CPUID_WORD_EAX] >> 10) & 0x1)) {
183+
log_error("AEX-Notify hardware feature is not supported.");
184+
return false;
185+
}
186+
187+
cpuid(INTEL_SGX_LEAF, 0, cpuinfo);
188+
if (!((cpuinfo[CPUID_WORD_EAX] >> 11) & 0x1)) {
189+
log_error("ENCLU[EDECCSSA] leaf instruction is not supported.");
190+
return false;
191+
}
192+
193+
return true;
194+
}
195+
178196
int create_enclave(sgx_arch_secs_t* secs, sgx_arch_token_t* token) {
179197
assert(secs->size && IS_POWER_OF_2(secs->size));
180198
assert(IS_ALIGNED(secs->base, secs->size));

pal/src/host/linux-sgx/host_internal.h

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ void* realloc(void* ptr, size_t new_size);
6666

6767
int open_sgx_driver(void);
6868
bool is_wrfsbase_supported(void);
69+
bool is_aexnotify_supported(void);
6970

7071
int read_enclave_token(int token_file, sgx_arch_token_t* out_token);
7172
int create_dummy_enclave_token(sgx_sigstruct_t* sig, sgx_arch_token_t* out_token);

pal/src/host/linux-sgx/host_main.c

+18
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,10 @@ static int initialize_enclave(struct pal_enclave* enclave, const char* manifest_
552552
tcs->ogs_base = tls_area->addr - enclave->baseaddr + t * g_page_size;
553553
tcs->ofs_limit = 0xfff;
554554
tcs->ogs_limit = 0xfff;
555+
tcs->flags |= (enclave_token.body.attributes.flags & SGX_FLAGS_AEXNOTIFY)
556+
? TCS_FLAGS_AEXNOTIFY : 0;
555557
tcs_addrs[t] = (void*)tcs_area->addr + g_page_size * t;
558+
556559
}
557560
} else if (areas[i].data_src == BUF) {
558561
memcpy(data, areas[i].buf, areas[i].buf_size);
@@ -690,6 +693,21 @@ static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info,
690693
goto out;
691694
}
692695

696+
bool aex_notify_enabled;
697+
ret = toml_bool_in(manifest_root, "sgx.experimental_enable_aex_notify",
698+
/*defaultval=*/false, &aex_notify_enabled);
699+
if (ret < 0) {
700+
log_error("Cannot parse 'sgx.experimental_enable_aex_notify'");
701+
ret = -EINVAL;
702+
goto out;
703+
}
704+
705+
if (aex_notify_enabled && !is_aexnotify_supported()) {
706+
log_error("Cannot enable AEX-Notify on this platform (hardware doesn't support it)");
707+
ret = -EPERM;
708+
goto out;
709+
}
710+
693711
int64_t thread_num_int64;
694712
ret = toml_int_in(manifest_root, "sgx.max_threads", /*defaultval=*/-1, &thread_num_int64);
695713
if (ret < 0) {

pal/src/host/linux-sgx/pal_exception.c

+29
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,35 @@
2222

2323
#define ADDR_IN_PAL(addr) ((void*)(addr) > TEXT_START && (void*)(addr) < TEXT_END)
2424

25+
bool g_aex_notify_enabled = false;
26+
27+
void init_aex_notify_for_thread(void) {
28+
if (!g_aex_notify_enabled)
29+
return;
30+
31+
SET_ENCLAVE_TCB(ready_for_aex_notify, 1UL);
32+
MB();
33+
#if 0
34+
/*
35+
* FIXME: Re-enable in the following commit, when all AEX-Notify flows are added.
36+
* Currently this would fail, as the untrusted runtime expects AEX-Notify flows but
37+
* in-enclave runtime doesn't yet implement AEX-Notify flows.
38+
*/
39+
GET_ENCLAVE_TCB(gpr)->aexnotify = 1U;
40+
MB();
41+
#endif
42+
}
43+
44+
void fini_aex_notify_for_thread(void) {
45+
if (!g_aex_notify_enabled)
46+
return;
47+
48+
SET_ENCLAVE_TCB(ready_for_aex_notify, 0UL);
49+
MB();
50+
GET_ENCLAVE_TCB(gpr)->aexnotify = 0U;
51+
MB();
52+
}
53+
2554
/* Restore an sgx_cpu_context_t as generated by .Lhandle_exception. Execution will
2655
* continue as specified by the rip in the context. */
2756
__attribute_no_sanitize_address

pal/src/host/linux-sgx/pal_linux.h

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ void save_xregs(PAL_XREGS_STATE* xsave_area);
9595
void restore_xregs(const PAL_XREGS_STATE* xsave_area);
9696
noreturn void _restore_sgx_context(sgx_cpu_context_t* uc, PAL_XREGS_STATE* xsave_area);
9797

98+
extern bool g_aex_notify_enabled;
99+
void init_aex_notify_for_thread(void);
100+
void fini_aex_notify_for_thread(void);
101+
98102
void _PalExceptionHandler(uint32_t trusted_exit_info_,
99103
uint32_t untrusted_external_event, sgx_cpu_context_t* uc,
100104
PAL_XREGS_STATE* xregs_state, sgx_arch_exinfo_t* exinfo);

pal/src/host/linux-sgx/pal_main.c

+9
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,13 @@ noreturn void pal_linux_main(void* uptr_libpal_uri, size_t libpal_uri_len, void*
722722
ocall_exit(1, /*is_exitgroup=*/true);
723723
}
724724

725+
ret = toml_bool_in(g_pal_public_state.manifest_root, "sgx.experimental_enable_aex_notify",
726+
/*defaultval=*/false, &g_aex_notify_enabled);
727+
if (ret < 0) {
728+
log_error("Cannot parse 'sgx.experimental_enable_aex_notify'");
729+
ocall_exit(1, /*is_exitgroup=*/true);
730+
}
731+
725732
/* Get host information about domain name configuration only for the first process.
726733
* This information will be checkpointed and restored during forking of the child
727734
* process(es). */
@@ -764,6 +771,8 @@ noreturn void pal_linux_main(void* uptr_libpal_uri, size_t libpal_uri_len, void*
764771
assert(!g_pal_linuxsgx_state.enclave_initialized);
765772
g_pal_linuxsgx_state.enclave_initialized = true;
766773

774+
init_aex_notify_for_thread();
775+
767776
/* call main function */
768777
pal_main(instance_id, parent, first_thread, arguments, environments, post_callback);
769778
}

pal/src/host/linux-sgx/pal_tcb.h

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct pal_enclave_tcb {
4242
uint64_t ocall_exit_called;
4343
uint64_t thread_started;
4444
uint64_t ready_for_exceptions;
45+
uint64_t ready_for_aex_notify;
4546
uint64_t manifest_size;
4647
void* heap_min;
4748
void* heap_max;

pal/src/host/linux-sgx/pal_threading.c

+4
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ void pal_start_thread(void) {
160160
pal_set_tcb_stack_canary(stack_protector_canary);
161161
PAL_TCB* pal_tcb = pal_get_tcb();
162162
memset(&pal_tcb->libos_tcb, 0, sizeof(pal_tcb->libos_tcb));
163+
init_aex_notify_for_thread();
164+
163165
callback((void*)param);
164166
_PalThreadExit(/*clear_child_tid=*/NULL);
165167
/* UNREACHABLE */
@@ -227,6 +229,8 @@ void _PalThreadYieldExecution(void) {
227229
noreturn void _PalThreadExit(int* clear_child_tid) {
228230
struct pal_handle_thread* exiting_thread = GET_ENCLAVE_TCB(thread);
229231

232+
fini_aex_notify_for_thread();
233+
230234
/* thread is ready to exit, must inform LibOS by erasing clear_child_tid;
231235
* note that we don't do it now (because this thread still occupies SGX
232236
* TCS slot) but during handle_thread_reset in assembly code */

pal/src/host/linux-sgx/sgx_arch.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ typedef uint8_t sgx_isvfamily_id_t[SGX_ISV_FAMILY_ID_SIZE];
6666
#define SGX_FLAGS_MODE64BIT 0x04ULL
6767
#define SGX_FLAGS_PROVISION_KEY 0x10ULL
6868
#define SGX_FLAGS_LICENSE_KEY 0x20ULL
69+
#define SGX_FLAGS_AEXNOTIFY 0x400ULL
6970

7071
/* EINIT must verify *all* SECS.ATTRIBUTES[63..0] bits (FLAGS bits) against
7172
* SIGSTRUCT.ATTRIBUTES[63..0].
@@ -168,7 +169,8 @@ typedef struct {
168169
} sgx_arch_tcs_t;
169170
static_assert(sizeof(sgx_arch_tcs_t) == 4096, "incorrect struct size");
170171

171-
#define TCS_FLAGS_DBGOPTIN (01ULL)
172+
#define TCS_FLAGS_DBGOPTIN (01ULL)
173+
#define TCS_FLAGS_AEXNOTIFY (02ULL)
172174

173175
typedef struct {
174176
uint64_t rax;
@@ -192,10 +194,12 @@ typedef struct {
192194
uint64_t ursp;
193195
uint64_t urbp;
194196
uint32_t exitinfo;
195-
uint32_t reserved;
197+
uint8_t reserved[3];
198+
uint8_t aexnotify;
196199
uint64_t fsbase;
197200
uint64_t gsbase;
198201
} sgx_pal_gpr_t;
202+
static_assert(offsetof(sgx_pal_gpr_t, aexnotify) == 167, "Wrong offset of AEX-Notify bit in SSA");
199203

200204
typedef struct {
201205
uint64_t rax;
@@ -443,6 +447,7 @@ static inline int enclu(uint32_t eax, uint64_t rbx, uint64_t rcx, uint64_t rdx)
443447
#define EACCEPT 5
444448
#define EMODPE 6
445449
#define EACCEPTCOPY 7
450+
#define EDECCSSA 9
446451

447452
#define SGX_LAUNCH_KEY 0
448453
#define SGX_PROVISION_KEY 1

python/graminelibos/manifest.py

+1
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ def __init__(self, manifest_str):
309309
sgx.setdefault('debug', False)
310310
sgx.setdefault('enable_stats', False)
311311
sgx.setdefault('edmm_enable', False)
312+
sgx.setdefault('experimental_enable_aex_notify', False)
312313

313314
if sgx['edmm_enable']:
314315
sgx.setdefault('enclave_size', DEFAULT_ENCLAVE_SIZE_WITH_EDMM)

python/graminelibos/manifest_check.py

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
},
8282
'debug': bool,
8383
'edmm_enable': bool,
84+
'experimental_enable_aex_notify': bool,
8485
'enable_stats': bool,
8586
'enclave_size': _size,
8687
'file_check_policy': Any('strict', 'allow_all_but_log'),

python/graminelibos/sgx_sign.py

+6
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def collect_cpu_feature_bits(manifest_cpu_features, options_dict, val, mask, sec
100100
def get_enclave_attributes(manifest_sgx):
101101
flags_dict = {
102102
'debug': offs.SGX_FLAGS_DEBUG,
103+
'experimental_enable_aex_notify': offs.SGX_FLAGS_AEXNOTIFY,
103104
}
104105
flags = collect_bits(manifest_sgx, flags_dict)
105106
if ARCHITECTURE == 'amd64':
@@ -271,6 +272,9 @@ def set_tls_field(t, offset, value):
271272
set_tcs_field(t, offs.TCS_OFS_LIMIT, '<L', 0xfff)
272273
set_tcs_field(t, offs.TCS_OGS_LIMIT, '<L', 0xfff)
273274

275+
if attr['enable_aex_notify']:
276+
set_tcs_field(t, offs.TCS_FLAGS, '<Q', offs.TCS_FLAGS_AEXNOTIFY)
277+
274278
set_tls_field(t, offs.SGX_COMMON_SELF, tls_area.addr + offs.PAGESIZE * t)
275279
set_tls_field(t, offs.SGX_COMMON_STACK_PROTECTOR_CANARY,
276280
offs.STACK_PROTECTOR_CANARY_DEFAULT)
@@ -473,13 +477,15 @@ def get_mrenclave_and_manifest(manifest_path, libpal, verbose=False):
473477
attr = {
474478
'enclave_size': parse_size(manifest_sgx['enclave_size']),
475479
'edmm_enable': manifest_sgx.get('edmm_enable', False),
480+
'enable_aex_notify': manifest_sgx.get('experimental_enable_aex_notify', False),
476481
'max_threads': manifest_sgx['max_threads'],
477482
}
478483

479484
if verbose:
480485
print('Attributes (required for enclave measurement):')
481486
print(f' size: {attr["enclave_size"]:#x}')
482487
print(f' edmm: {attr["edmm_enable"]}')
488+
print(f' aex-notify: {attr["enable_aex_notify"]}')
483489
print(f' max_threads: {attr["max_threads"]}')
484490

485491
print('SGX remote attestation:')

0 commit comments

Comments
 (0)