Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/* Copyright(c) 2016-20 Intel Corporation. */
3
4#include <cpuid.h>
5#include <elf.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdint.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14#include <sys/ioctl.h>
15#include <sys/mman.h>
16#include <sys/stat.h>
17#include <sys/time.h>
18#include <sys/types.h>
19#include <sys/auxv.h>
20#include "defines.h"
21#include "../kselftest_harness.h"
22#include "main.h"
23
24static const uint64_t MAGIC = 0x1122334455667788ULL;
25static const uint64_t MAGIC2 = 0x8877665544332211ULL;
26vdso_sgx_enter_enclave_t vdso_sgx_enter_enclave;
27
28struct vdso_symtab {
29 Elf64_Sym *elf_symtab;
30 const char *elf_symstrtab;
31 Elf64_Word *elf_hashtab;
32};
33
34static Elf64_Dyn *vdso_get_dyntab(void *addr)
35{
36 Elf64_Ehdr *ehdr = addr;
37 Elf64_Phdr *phdrtab = addr + ehdr->e_phoff;
38 int i;
39
40 for (i = 0; i < ehdr->e_phnum; i++)
41 if (phdrtab[i].p_type == PT_DYNAMIC)
42 return addr + phdrtab[i].p_offset;
43
44 return NULL;
45}
46
47static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag)
48{
49 int i;
50
51 for (i = 0; dyntab[i].d_tag != DT_NULL; i++)
52 if (dyntab[i].d_tag == tag)
53 return addr + dyntab[i].d_un.d_ptr;
54
55 return NULL;
56}
57
58static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab)
59{
60 Elf64_Dyn *dyntab = vdso_get_dyntab(addr);
61
62 symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB);
63 if (!symtab->elf_symtab)
64 return false;
65
66 symtab->elf_symstrtab = vdso_get_dyn(addr, dyntab, DT_STRTAB);
67 if (!symtab->elf_symstrtab)
68 return false;
69
70 symtab->elf_hashtab = vdso_get_dyn(addr, dyntab, DT_HASH);
71 if (!symtab->elf_hashtab)
72 return false;
73
74 return true;
75}
76
77static unsigned long elf_sym_hash(const char *name)
78{
79 unsigned long h = 0, high;
80
81 while (*name) {
82 h = (h << 4) + *name++;
83 high = h & 0xf0000000;
84
85 if (high)
86 h ^= high >> 24;
87
88 h &= ~high;
89 }
90
91 return h;
92}
93
94static Elf64_Sym *vdso_symtab_get(struct vdso_symtab *symtab, const char *name)
95{
96 Elf64_Word bucketnum = symtab->elf_hashtab[0];
97 Elf64_Word *buckettab = &symtab->elf_hashtab[2];
98 Elf64_Word *chaintab = &symtab->elf_hashtab[2 + bucketnum];
99 Elf64_Sym *sym;
100 Elf64_Word i;
101
102 for (i = buckettab[elf_sym_hash(name) % bucketnum]; i != STN_UNDEF;
103 i = chaintab[i]) {
104 sym = &symtab->elf_symtab[i];
105 if (!strcmp(name, &symtab->elf_symstrtab[sym->st_name]))
106 return sym;
107 }
108
109 return NULL;
110}
111
112/*
113 * Return the offset in the enclave where the data segment can be found.
114 * The first RW segment loaded is the TCS, skip that to get info on the
115 * data segment.
116 */
117static off_t encl_get_data_offset(struct encl *encl)
118{
119 int i;
120
121 for (i = 1; i < encl->nr_segments; i++) {
122 struct encl_segment *seg = &encl->segment_tbl[i];
123
124 if (seg->prot == (PROT_READ | PROT_WRITE))
125 return seg->offset;
126 }
127
128 return -1;
129}
130
131FIXTURE(enclave) {
132 struct encl encl;
133 struct sgx_enclave_run run;
134};
135
136static bool setup_test_encl(unsigned long heap_size, struct encl *encl,
137 struct __test_metadata *_metadata)
138{
139 Elf64_Sym *sgx_enter_enclave_sym = NULL;
140 struct vdso_symtab symtab;
141 struct encl_segment *seg;
142 char maps_line[256];
143 FILE *maps_file;
144 unsigned int i;
145 void *addr;
146
147 if (!encl_load("test_encl.elf", encl, heap_size)) {
148 encl_delete(encl);
149 TH_LOG("Failed to load the test enclave.\n");
150 }
151
152 if (!encl_measure(encl))
153 goto err;
154
155 if (!encl_build(encl))
156 goto err;
157
158 /*
159 * An enclave consumer only must do this.
160 */
161 for (i = 0; i < encl->nr_segments; i++) {
162 struct encl_segment *seg = &encl->segment_tbl[i];
163
164 addr = mmap((void *)encl->encl_base + seg->offset, seg->size,
165 seg->prot, MAP_SHARED | MAP_FIXED, encl->fd, 0);
166 EXPECT_NE(addr, MAP_FAILED);
167 if (addr == MAP_FAILED)
168 goto err;
169 }
170
171 /* Get vDSO base address */
172 addr = (void *)getauxval(AT_SYSINFO_EHDR);
173 if (!addr)
174 goto err;
175
176 if (!vdso_get_symtab(addr, &symtab))
177 goto err;
178
179 sgx_enter_enclave_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave");
180 if (!sgx_enter_enclave_sym)
181 goto err;
182
183 vdso_sgx_enter_enclave = addr + sgx_enter_enclave_sym->st_value;
184
185 return true;
186
187err:
188 encl_delete(encl);
189
190 for (i = 0; i < encl->nr_segments; i++) {
191 seg = &encl->segment_tbl[i];
192
193 TH_LOG("0x%016lx 0x%016lx 0x%02x", seg->offset, seg->size, seg->prot);
194 }
195
196 maps_file = fopen("/proc/self/maps", "r");
197 if (maps_file != NULL) {
198 while (fgets(maps_line, sizeof(maps_line), maps_file) != NULL) {
199 maps_line[strlen(maps_line) - 1] = '\0';
200
201 if (strstr(maps_line, "/dev/sgx_enclave"))
202 TH_LOG("%s", maps_line);
203 }
204
205 fclose(maps_file);
206 }
207
208 TH_LOG("Failed to initialize the test enclave.\n");
209
210 return false;
211}
212
213FIXTURE_SETUP(enclave)
214{
215}
216
217FIXTURE_TEARDOWN(enclave)
218{
219 encl_delete(&self->encl);
220}
221
222#define ENCL_CALL(op, run, clobbered) \
223 ({ \
224 int ret; \
225 if ((clobbered)) \
226 ret = vdso_sgx_enter_enclave((unsigned long)(op), 0, 0, \
227 EENTER, 0, 0, (run)); \
228 else \
229 ret = sgx_enter_enclave((void *)(op), NULL, 0, EENTER, NULL, NULL, \
230 (run)); \
231 ret; \
232 })
233
234#define EXPECT_EEXIT(run) \
235 do { \
236 EXPECT_EQ((run)->function, EEXIT); \
237 if ((run)->function != EEXIT) \
238 TH_LOG("0x%02x 0x%02x 0x%016llx", (run)->exception_vector, \
239 (run)->exception_error_code, (run)->exception_addr); \
240 } while (0)
241
242TEST_F(enclave, unclobbered_vdso)
243{
244 struct encl_op_get_from_buf get_op;
245 struct encl_op_put_to_buf put_op;
246
247 ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
248
249 memset(&self->run, 0, sizeof(self->run));
250 self->run.tcs = self->encl.encl_base;
251
252 put_op.header.type = ENCL_OP_PUT_TO_BUFFER;
253 put_op.value = MAGIC;
254
255 EXPECT_EQ(ENCL_CALL(&put_op, &self->run, false), 0);
256
257 EXPECT_EEXIT(&self->run);
258 EXPECT_EQ(self->run.user_data, 0);
259
260 get_op.header.type = ENCL_OP_GET_FROM_BUFFER;
261 get_op.value = 0;
262
263 EXPECT_EQ(ENCL_CALL(&get_op, &self->run, false), 0);
264
265 EXPECT_EQ(get_op.value, MAGIC);
266 EXPECT_EEXIT(&self->run);
267 EXPECT_EQ(self->run.user_data, 0);
268}
269
270/*
271 * A section metric is concatenated in a way that @low bits 12-31 define the
272 * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the
273 * metric.
274 */
275static unsigned long sgx_calc_section_metric(unsigned int low,
276 unsigned int high)
277{
278 return (low & GENMASK_ULL(31, 12)) +
279 ((high & GENMASK_ULL(19, 0)) << 32);
280}
281
282/*
283 * Sum total available physical SGX memory across all EPC sections
284 *
285 * Return: total available physical SGX memory available on system
286 */
287static unsigned long get_total_epc_mem(void)
288{
289 unsigned int eax, ebx, ecx, edx;
290 unsigned long total_size = 0;
291 unsigned int type;
292 int section = 0;
293
294 while (true) {
295 __cpuid_count(SGX_CPUID, section + SGX_CPUID_EPC, eax, ebx, ecx, edx);
296
297 type = eax & SGX_CPUID_EPC_MASK;
298 if (type == SGX_CPUID_EPC_INVALID)
299 break;
300
301 if (type != SGX_CPUID_EPC_SECTION)
302 break;
303
304 total_size += sgx_calc_section_metric(ecx, edx);
305
306 section++;
307 }
308
309 return total_size;
310}
311
312TEST_F(enclave, unclobbered_vdso_oversubscribed)
313{
314 struct encl_op_get_from_buf get_op;
315 struct encl_op_put_to_buf put_op;
316 unsigned long total_mem;
317
318 total_mem = get_total_epc_mem();
319 ASSERT_NE(total_mem, 0);
320 ASSERT_TRUE(setup_test_encl(total_mem, &self->encl, _metadata));
321
322 memset(&self->run, 0, sizeof(self->run));
323 self->run.tcs = self->encl.encl_base;
324
325 put_op.header.type = ENCL_OP_PUT_TO_BUFFER;
326 put_op.value = MAGIC;
327
328 EXPECT_EQ(ENCL_CALL(&put_op, &self->run, false), 0);
329
330 EXPECT_EEXIT(&self->run);
331 EXPECT_EQ(self->run.user_data, 0);
332
333 get_op.header.type = ENCL_OP_GET_FROM_BUFFER;
334 get_op.value = 0;
335
336 EXPECT_EQ(ENCL_CALL(&get_op, &self->run, false), 0);
337
338 EXPECT_EQ(get_op.value, MAGIC);
339 EXPECT_EEXIT(&self->run);
340 EXPECT_EQ(self->run.user_data, 0);
341
342}
343
344TEST_F(enclave, clobbered_vdso)
345{
346 struct encl_op_get_from_buf get_op;
347 struct encl_op_put_to_buf put_op;
348
349 ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
350
351 memset(&self->run, 0, sizeof(self->run));
352 self->run.tcs = self->encl.encl_base;
353
354 put_op.header.type = ENCL_OP_PUT_TO_BUFFER;
355 put_op.value = MAGIC;
356
357 EXPECT_EQ(ENCL_CALL(&put_op, &self->run, true), 0);
358
359 EXPECT_EEXIT(&self->run);
360 EXPECT_EQ(self->run.user_data, 0);
361
362 get_op.header.type = ENCL_OP_GET_FROM_BUFFER;
363 get_op.value = 0;
364
365 EXPECT_EQ(ENCL_CALL(&get_op, &self->run, true), 0);
366
367 EXPECT_EQ(get_op.value, MAGIC);
368 EXPECT_EEXIT(&self->run);
369 EXPECT_EQ(self->run.user_data, 0);
370}
371
372static int test_handler(long rdi, long rsi, long rdx, long ursp, long r8, long r9,
373 struct sgx_enclave_run *run)
374{
375 run->user_data = 0;
376
377 return 0;
378}
379
380TEST_F(enclave, clobbered_vdso_and_user_function)
381{
382 struct encl_op_get_from_buf get_op;
383 struct encl_op_put_to_buf put_op;
384
385 ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
386
387 memset(&self->run, 0, sizeof(self->run));
388 self->run.tcs = self->encl.encl_base;
389
390 self->run.user_handler = (__u64)test_handler;
391 self->run.user_data = 0xdeadbeef;
392
393 put_op.header.type = ENCL_OP_PUT_TO_BUFFER;
394 put_op.value = MAGIC;
395
396 EXPECT_EQ(ENCL_CALL(&put_op, &self->run, true), 0);
397
398 EXPECT_EEXIT(&self->run);
399 EXPECT_EQ(self->run.user_data, 0);
400
401 get_op.header.type = ENCL_OP_GET_FROM_BUFFER;
402 get_op.value = 0;
403
404 EXPECT_EQ(ENCL_CALL(&get_op, &self->run, true), 0);
405
406 EXPECT_EQ(get_op.value, MAGIC);
407 EXPECT_EEXIT(&self->run);
408 EXPECT_EQ(self->run.user_data, 0);
409}
410
411/*
412 * Sanity check that it is possible to enter either of the two hardcoded TCS
413 */
414TEST_F(enclave, tcs_entry)
415{
416 struct encl_op_header op;
417
418 ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
419
420 memset(&self->run, 0, sizeof(self->run));
421 self->run.tcs = self->encl.encl_base;
422
423 op.type = ENCL_OP_NOP;
424
425 EXPECT_EQ(ENCL_CALL(&op, &self->run, true), 0);
426
427 EXPECT_EEXIT(&self->run);
428 EXPECT_EQ(self->run.exception_vector, 0);
429 EXPECT_EQ(self->run.exception_error_code, 0);
430 EXPECT_EQ(self->run.exception_addr, 0);
431
432 /* Move to the next TCS. */
433 self->run.tcs = self->encl.encl_base + PAGE_SIZE;
434
435 EXPECT_EQ(ENCL_CALL(&op, &self->run, true), 0);
436
437 EXPECT_EEXIT(&self->run);
438 EXPECT_EQ(self->run.exception_vector, 0);
439 EXPECT_EQ(self->run.exception_error_code, 0);
440 EXPECT_EQ(self->run.exception_addr, 0);
441}
442
443/*
444 * Second page of .data segment is used to test changing PTE permissions.
445 * This spans the local encl_buffer within the test enclave.
446 *
447 * 1) Start with a sanity check: a value is written to the target page within
448 * the enclave and read back to ensure target page can be written to.
449 * 2) Change PTE permissions (RW -> RO) of target page within enclave.
450 * 3) Repeat (1) - this time expecting a regular #PF communicated via the
451 * vDSO.
452 * 4) Change PTE permissions of target page within enclave back to be RW.
453 * 5) Repeat (1) by resuming enclave, now expected to be possible to write to
454 * and read from target page within enclave.
455 */
456TEST_F(enclave, pte_permissions)
457{
458 struct encl_op_get_from_addr get_addr_op;
459 struct encl_op_put_to_addr put_addr_op;
460 unsigned long data_start;
461 int ret;
462
463 ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, _metadata));
464
465 memset(&self->run, 0, sizeof(self->run));
466 self->run.tcs = self->encl.encl_base;
467
468 data_start = self->encl.encl_base +
469 encl_get_data_offset(&self->encl) +
470 PAGE_SIZE;
471
472 /*
473 * Sanity check to ensure it is possible to write to page that will
474 * have its permissions manipulated.
475 */
476
477 /* Write MAGIC to page */
478 put_addr_op.value = MAGIC;
479 put_addr_op.addr = data_start;
480 put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
481
482 EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
483
484 EXPECT_EEXIT(&self->run);
485 EXPECT_EQ(self->run.exception_vector, 0);
486 EXPECT_EQ(self->run.exception_error_code, 0);
487 EXPECT_EQ(self->run.exception_addr, 0);
488
489 /*
490 * Read memory that was just written to, confirming that it is the
491 * value previously written (MAGIC).
492 */
493 get_addr_op.value = 0;
494 get_addr_op.addr = data_start;
495 get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
496
497 EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
498
499 EXPECT_EQ(get_addr_op.value, MAGIC);
500 EXPECT_EEXIT(&self->run);
501 EXPECT_EQ(self->run.exception_vector, 0);
502 EXPECT_EQ(self->run.exception_error_code, 0);
503 EXPECT_EQ(self->run.exception_addr, 0);
504
505 /* Change PTE permissions of target page within the enclave */
506 ret = mprotect((void *)data_start, PAGE_SIZE, PROT_READ);
507 if (ret)
508 perror("mprotect");
509
510 /*
511 * PTE permissions of target page changed to read-only, EPCM
512 * permissions unchanged (EPCM permissions are RW), attempt to
513 * write to the page, expecting a regular #PF.
514 */
515
516 put_addr_op.value = MAGIC2;
517
518 EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
519
520 EXPECT_EQ(self->run.exception_vector, 14);
521 EXPECT_EQ(self->run.exception_error_code, 0x7);
522 EXPECT_EQ(self->run.exception_addr, data_start);
523
524 self->run.exception_vector = 0;
525 self->run.exception_error_code = 0;
526 self->run.exception_addr = 0;
527
528 /*
529 * Change PTE permissions back to enable enclave to write to the
530 * target page and resume enclave - do not expect any exceptions this
531 * time.
532 */
533 ret = mprotect((void *)data_start, PAGE_SIZE, PROT_READ | PROT_WRITE);
534 if (ret)
535 perror("mprotect");
536
537 EXPECT_EQ(vdso_sgx_enter_enclave((unsigned long)&put_addr_op, 0,
538 0, ERESUME, 0, 0, &self->run),
539 0);
540
541 EXPECT_EEXIT(&self->run);
542 EXPECT_EQ(self->run.exception_vector, 0);
543 EXPECT_EQ(self->run.exception_error_code, 0);
544 EXPECT_EQ(self->run.exception_addr, 0);
545
546 get_addr_op.value = 0;
547
548 EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
549
550 EXPECT_EQ(get_addr_op.value, MAGIC2);
551 EXPECT_EEXIT(&self->run);
552 EXPECT_EQ(self->run.exception_vector, 0);
553 EXPECT_EQ(self->run.exception_error_code, 0);
554 EXPECT_EQ(self->run.exception_addr, 0);
555}
556
557TEST_HARNESS_MAIN