Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/sgx: Test complete changing of page type flow

Support for changing an enclave page's type enables an initialized
enclave to be expanded with support for more threads by changing the
type of a regular enclave page to that of a Thread Control Structure
(TCS). Additionally, being able to change a TCS or regular enclave
page's type to be trimmed (SGX_PAGE_TYPE_TRIM) initiates the removal
of the page from the enclave.

Test changing page type to TCS as well as page removal flows
in two phases: In the first phase support for a new thread is
dynamically added to an initialized enclave and in the second phase
the pages associated with the new thread are removed from the enclave.
As an additional sanity check after the second phase the page used as
a TCS page during the first phase is added back as a regular page and
ensured that it can be written to (which is not possible if it was a
TCS page).

Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lkml.kernel.org/r/d05b48b00338683a94dcaef9f478540fc3d6d5f9.1652137848.git.reinette.chatre@intel.com

authored by

Reinette Chatre and committed by
Dave Hansen
33c5aac3 b564982f

+385
+41
tools/testing/selftests/sgx/load.c
··· 130 130 return true; 131 131 } 132 132 133 + /* 134 + * Parse the enclave code's symbol table to locate and return address of 135 + * the provided symbol 136 + */ 137 + uint64_t encl_get_entry(struct encl *encl, const char *symbol) 138 + { 139 + Elf64_Shdr *sections; 140 + Elf64_Sym *symtab; 141 + Elf64_Ehdr *ehdr; 142 + char *sym_names; 143 + int num_sym; 144 + int i; 145 + 146 + ehdr = encl->bin; 147 + sections = encl->bin + ehdr->e_shoff; 148 + 149 + for (i = 0; i < ehdr->e_shnum; i++) { 150 + if (sections[i].sh_type == SHT_SYMTAB) { 151 + symtab = (Elf64_Sym *)((char *)encl->bin + sections[i].sh_offset); 152 + num_sym = sections[i].sh_size / sections[i].sh_entsize; 153 + break; 154 + } 155 + } 156 + 157 + for (i = 0; i < ehdr->e_shnum; i++) { 158 + if (sections[i].sh_type == SHT_STRTAB) { 159 + sym_names = (char *)encl->bin + sections[i].sh_offset; 160 + break; 161 + } 162 + } 163 + 164 + for (i = 0; i < num_sym; i++) { 165 + Elf64_Sym *sym = &symtab[i]; 166 + 167 + if (!strcmp(symbol, sym_names + sym->st_name)) 168 + return (uint64_t)sym->st_value; 169 + } 170 + 171 + return 0; 172 + } 173 + 133 174 bool encl_load(const char *path, struct encl *encl, unsigned long heap_size) 134 175 { 135 176 const char device_path[] = "/dev/sgx_enclave";
+343
tools/testing/selftests/sgx/main.c
··· 1090 1090 munmap(addr, PAGE_SIZE); 1091 1091 } 1092 1092 1093 + /* 1094 + * SGX2 page type modification test in two phases: 1095 + * Phase 1: 1096 + * Create a new TCS, consisting out of three new pages (stack page with regular 1097 + * page type, SSA page with regular page type, and TCS page with TCS page 1098 + * type) in an initialized enclave and run a simple workload within it. 1099 + * Phase 2: 1100 + * Remove the three pages added in phase 1, add a new regular page at the 1101 + * same address that previously hosted the TCS page and verify that it can 1102 + * be modified. 1103 + */ 1104 + TEST_F(enclave, tcs_create) 1105 + { 1106 + struct encl_op_init_tcs_page init_tcs_page_op; 1107 + struct sgx_enclave_remove_pages remove_ioc; 1108 + struct encl_op_get_from_addr get_addr_op; 1109 + struct sgx_enclave_modify_types modt_ioc; 1110 + struct encl_op_put_to_addr put_addr_op; 1111 + struct encl_op_get_from_buf get_buf_op; 1112 + struct encl_op_put_to_buf put_buf_op; 1113 + void *addr, *tcs, *stack_end, *ssa; 1114 + struct encl_op_eaccept eaccept_op; 1115 + size_t total_size = 0; 1116 + uint64_t val_64; 1117 + int errno_save; 1118 + int ret, i; 1119 + 1120 + ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl, 1121 + _metadata)); 1122 + 1123 + memset(&self->run, 0, sizeof(self->run)); 1124 + self->run.tcs = self->encl.encl_base; 1125 + 1126 + /* 1127 + * Hardware (SGX2) and kernel support is needed for this test. Start 1128 + * with check that test has a chance of succeeding. 1129 + */ 1130 + memset(&modt_ioc, 0, sizeof(modt_ioc)); 1131 + ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc); 1132 + 1133 + if (ret == -1) { 1134 + if (errno == ENOTTY) 1135 + SKIP(return, 1136 + "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()"); 1137 + else if (errno == ENODEV) 1138 + SKIP(return, "System does not support SGX2"); 1139 + } 1140 + 1141 + /* 1142 + * Invalid parameters were provided during sanity check, 1143 + * expect command to fail. 1144 + */ 1145 + EXPECT_EQ(ret, -1); 1146 + 1147 + /* 1148 + * Add three regular pages via EAUG: one will be the TCS stack, one 1149 + * will be the TCS SSA, and one will be the new TCS. The stack and 1150 + * SSA will remain as regular pages, the TCS page will need its 1151 + * type changed after populated with needed data. 1152 + */ 1153 + for (i = 0; i < self->encl.nr_segments; i++) { 1154 + struct encl_segment *seg = &self->encl.segment_tbl[i]; 1155 + 1156 + total_size += seg->size; 1157 + } 1158 + 1159 + /* 1160 + * Actual enclave size is expected to be larger than the loaded 1161 + * test enclave since enclave size must be a power of 2 in bytes while 1162 + * test_encl does not consume it all. 1163 + */ 1164 + EXPECT_LT(total_size + 3 * PAGE_SIZE, self->encl.encl_size); 1165 + 1166 + /* 1167 + * mmap() three pages at end of existing enclave to be used for the 1168 + * three new pages. 1169 + */ 1170 + addr = mmap((void *)self->encl.encl_base + total_size, 3 * PAGE_SIZE, 1171 + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, 1172 + self->encl.fd, 0); 1173 + EXPECT_NE(addr, MAP_FAILED); 1174 + 1175 + self->run.exception_vector = 0; 1176 + self->run.exception_error_code = 0; 1177 + self->run.exception_addr = 0; 1178 + 1179 + stack_end = (void *)self->encl.encl_base + total_size; 1180 + tcs = (void *)self->encl.encl_base + total_size + PAGE_SIZE; 1181 + ssa = (void *)self->encl.encl_base + total_size + 2 * PAGE_SIZE; 1182 + 1183 + /* 1184 + * Run EACCEPT on each new page to trigger the 1185 + * EACCEPT->(#PF)->EAUG->EACCEPT(again without a #PF) flow. 1186 + */ 1187 + 1188 + eaccept_op.epc_addr = (unsigned long)stack_end; 1189 + eaccept_op.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_REG | SGX_SECINFO_PENDING; 1190 + eaccept_op.ret = 0; 1191 + eaccept_op.header.type = ENCL_OP_EACCEPT; 1192 + 1193 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1194 + 1195 + if (self->run.exception_vector == 14 && 1196 + self->run.exception_error_code == 4 && 1197 + self->run.exception_addr == (unsigned long)stack_end) { 1198 + munmap(addr, 3 * PAGE_SIZE); 1199 + SKIP(return, "Kernel does not support adding pages to initialized enclave"); 1200 + } 1201 + 1202 + EXPECT_EEXIT(&self->run); 1203 + EXPECT_EQ(self->run.exception_vector, 0); 1204 + EXPECT_EQ(self->run.exception_error_code, 0); 1205 + EXPECT_EQ(self->run.exception_addr, 0); 1206 + EXPECT_EQ(eaccept_op.ret, 0); 1207 + 1208 + eaccept_op.epc_addr = (unsigned long)ssa; 1209 + 1210 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1211 + 1212 + EXPECT_EEXIT(&self->run); 1213 + EXPECT_EQ(self->run.exception_vector, 0); 1214 + EXPECT_EQ(self->run.exception_error_code, 0); 1215 + EXPECT_EQ(self->run.exception_addr, 0); 1216 + EXPECT_EQ(eaccept_op.ret, 0); 1217 + 1218 + eaccept_op.epc_addr = (unsigned long)tcs; 1219 + 1220 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1221 + 1222 + EXPECT_EEXIT(&self->run); 1223 + EXPECT_EQ(self->run.exception_vector, 0); 1224 + EXPECT_EQ(self->run.exception_error_code, 0); 1225 + EXPECT_EQ(self->run.exception_addr, 0); 1226 + EXPECT_EQ(eaccept_op.ret, 0); 1227 + 1228 + /* 1229 + * Three new pages added to enclave. Now populate the TCS page with 1230 + * needed data. This should be done from within enclave. Provide 1231 + * the function that will do the actual data population with needed 1232 + * data. 1233 + */ 1234 + 1235 + /* 1236 + * New TCS will use the "encl_dyn_entry" entrypoint that expects 1237 + * stack to begin in page before TCS page. 1238 + */ 1239 + val_64 = encl_get_entry(&self->encl, "encl_dyn_entry"); 1240 + EXPECT_NE(val_64, 0); 1241 + 1242 + init_tcs_page_op.tcs_page = (unsigned long)tcs; 1243 + init_tcs_page_op.ssa = (unsigned long)total_size + 2 * PAGE_SIZE; 1244 + init_tcs_page_op.entry = val_64; 1245 + init_tcs_page_op.header.type = ENCL_OP_INIT_TCS_PAGE; 1246 + 1247 + EXPECT_EQ(ENCL_CALL(&init_tcs_page_op, &self->run, true), 0); 1248 + 1249 + EXPECT_EEXIT(&self->run); 1250 + EXPECT_EQ(self->run.exception_vector, 0); 1251 + EXPECT_EQ(self->run.exception_error_code, 0); 1252 + EXPECT_EQ(self->run.exception_addr, 0); 1253 + 1254 + /* Change TCS page type to TCS. */ 1255 + memset(&modt_ioc, 0, sizeof(modt_ioc)); 1256 + 1257 + modt_ioc.offset = total_size + PAGE_SIZE; 1258 + modt_ioc.length = PAGE_SIZE; 1259 + modt_ioc.page_type = SGX_PAGE_TYPE_TCS; 1260 + 1261 + ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc); 1262 + errno_save = ret == -1 ? errno : 0; 1263 + 1264 + EXPECT_EQ(ret, 0); 1265 + EXPECT_EQ(errno_save, 0); 1266 + EXPECT_EQ(modt_ioc.result, 0); 1267 + EXPECT_EQ(modt_ioc.count, 4096); 1268 + 1269 + /* EACCEPT new TCS page from enclave. */ 1270 + eaccept_op.epc_addr = (unsigned long)tcs; 1271 + eaccept_op.flags = SGX_SECINFO_TCS | SGX_SECINFO_MODIFIED; 1272 + eaccept_op.ret = 0; 1273 + eaccept_op.header.type = ENCL_OP_EACCEPT; 1274 + 1275 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1276 + 1277 + EXPECT_EEXIT(&self->run); 1278 + EXPECT_EQ(self->run.exception_vector, 0); 1279 + EXPECT_EQ(self->run.exception_error_code, 0); 1280 + EXPECT_EQ(self->run.exception_addr, 0); 1281 + EXPECT_EQ(eaccept_op.ret, 0); 1282 + 1283 + /* Run workload from new TCS. */ 1284 + self->run.tcs = (unsigned long)tcs; 1285 + 1286 + /* 1287 + * Simple workload to write to data buffer and read value back. 1288 + */ 1289 + put_buf_op.header.type = ENCL_OP_PUT_TO_BUFFER; 1290 + put_buf_op.value = MAGIC; 1291 + 1292 + EXPECT_EQ(ENCL_CALL(&put_buf_op, &self->run, true), 0); 1293 + 1294 + EXPECT_EEXIT(&self->run); 1295 + EXPECT_EQ(self->run.exception_vector, 0); 1296 + EXPECT_EQ(self->run.exception_error_code, 0); 1297 + EXPECT_EQ(self->run.exception_addr, 0); 1298 + 1299 + get_buf_op.header.type = ENCL_OP_GET_FROM_BUFFER; 1300 + get_buf_op.value = 0; 1301 + 1302 + EXPECT_EQ(ENCL_CALL(&get_buf_op, &self->run, true), 0); 1303 + 1304 + EXPECT_EQ(get_buf_op.value, MAGIC); 1305 + EXPECT_EEXIT(&self->run); 1306 + EXPECT_EQ(self->run.exception_vector, 0); 1307 + EXPECT_EQ(self->run.exception_error_code, 0); 1308 + EXPECT_EQ(self->run.exception_addr, 0); 1309 + 1310 + /* 1311 + * Phase 2 of test: 1312 + * Remove pages associated with new TCS, create a regular page 1313 + * where TCS page used to be and verify it can be used as a regular 1314 + * page. 1315 + */ 1316 + 1317 + /* Start page removal by requesting change of page type to PT_TRIM. */ 1318 + memset(&modt_ioc, 0, sizeof(modt_ioc)); 1319 + 1320 + modt_ioc.offset = total_size; 1321 + modt_ioc.length = 3 * PAGE_SIZE; 1322 + modt_ioc.page_type = SGX_PAGE_TYPE_TRIM; 1323 + 1324 + ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc); 1325 + errno_save = ret == -1 ? errno : 0; 1326 + 1327 + EXPECT_EQ(ret, 0); 1328 + EXPECT_EQ(errno_save, 0); 1329 + EXPECT_EQ(modt_ioc.result, 0); 1330 + EXPECT_EQ(modt_ioc.count, 3 * PAGE_SIZE); 1331 + 1332 + /* 1333 + * Enter enclave via TCS #1 and approve page removal by sending 1334 + * EACCEPT for each of three removed pages. 1335 + */ 1336 + self->run.tcs = self->encl.encl_base; 1337 + 1338 + eaccept_op.epc_addr = (unsigned long)stack_end; 1339 + eaccept_op.flags = SGX_SECINFO_TRIM | SGX_SECINFO_MODIFIED; 1340 + eaccept_op.ret = 0; 1341 + eaccept_op.header.type = ENCL_OP_EACCEPT; 1342 + 1343 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1344 + 1345 + EXPECT_EEXIT(&self->run); 1346 + EXPECT_EQ(self->run.exception_vector, 0); 1347 + EXPECT_EQ(self->run.exception_error_code, 0); 1348 + EXPECT_EQ(self->run.exception_addr, 0); 1349 + EXPECT_EQ(eaccept_op.ret, 0); 1350 + 1351 + eaccept_op.epc_addr = (unsigned long)tcs; 1352 + eaccept_op.ret = 0; 1353 + 1354 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1355 + 1356 + EXPECT_EEXIT(&self->run); 1357 + EXPECT_EQ(self->run.exception_vector, 0); 1358 + EXPECT_EQ(self->run.exception_error_code, 0); 1359 + EXPECT_EQ(self->run.exception_addr, 0); 1360 + EXPECT_EQ(eaccept_op.ret, 0); 1361 + 1362 + eaccept_op.epc_addr = (unsigned long)ssa; 1363 + eaccept_op.ret = 0; 1364 + 1365 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1366 + 1367 + EXPECT_EEXIT(&self->run); 1368 + EXPECT_EQ(self->run.exception_vector, 0); 1369 + EXPECT_EQ(self->run.exception_error_code, 0); 1370 + EXPECT_EQ(self->run.exception_addr, 0); 1371 + EXPECT_EQ(eaccept_op.ret, 0); 1372 + 1373 + /* Send final ioctl() to complete page removal. */ 1374 + memset(&remove_ioc, 0, sizeof(remove_ioc)); 1375 + 1376 + remove_ioc.offset = total_size; 1377 + remove_ioc.length = 3 * PAGE_SIZE; 1378 + 1379 + ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_REMOVE_PAGES, &remove_ioc); 1380 + errno_save = ret == -1 ? errno : 0; 1381 + 1382 + EXPECT_EQ(ret, 0); 1383 + EXPECT_EQ(errno_save, 0); 1384 + EXPECT_EQ(remove_ioc.count, 3 * PAGE_SIZE); 1385 + 1386 + /* 1387 + * Enter enclave via TCS #1 and access location where TCS #3 was to 1388 + * trigger dynamic add of regular page at that location. 1389 + */ 1390 + eaccept_op.epc_addr = (unsigned long)tcs; 1391 + eaccept_op.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_REG | SGX_SECINFO_PENDING; 1392 + eaccept_op.ret = 0; 1393 + eaccept_op.header.type = ENCL_OP_EACCEPT; 1394 + 1395 + EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0); 1396 + 1397 + EXPECT_EEXIT(&self->run); 1398 + EXPECT_EQ(self->run.exception_vector, 0); 1399 + EXPECT_EQ(self->run.exception_error_code, 0); 1400 + EXPECT_EQ(self->run.exception_addr, 0); 1401 + EXPECT_EQ(eaccept_op.ret, 0); 1402 + 1403 + /* 1404 + * New page should be accessible from within enclave - write to it. 1405 + */ 1406 + put_addr_op.value = MAGIC; 1407 + put_addr_op.addr = (unsigned long)tcs; 1408 + put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS; 1409 + 1410 + EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0); 1411 + 1412 + EXPECT_EEXIT(&self->run); 1413 + EXPECT_EQ(self->run.exception_vector, 0); 1414 + EXPECT_EQ(self->run.exception_error_code, 0); 1415 + EXPECT_EQ(self->run.exception_addr, 0); 1416 + 1417 + /* 1418 + * Read memory from newly added page that was just written to, 1419 + * confirming that data previously written (MAGIC) is present. 1420 + */ 1421 + get_addr_op.value = 0; 1422 + get_addr_op.addr = (unsigned long)tcs; 1423 + get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS; 1424 + 1425 + EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0); 1426 + 1427 + EXPECT_EQ(get_addr_op.value, MAGIC); 1428 + EXPECT_EEXIT(&self->run); 1429 + EXPECT_EQ(self->run.exception_vector, 0); 1430 + EXPECT_EQ(self->run.exception_error_code, 0); 1431 + EXPECT_EQ(self->run.exception_addr, 0); 1432 + 1433 + munmap(addr, 3 * PAGE_SIZE); 1434 + } 1435 + 1093 1436 TEST_HARNESS_MAIN
+1
tools/testing/selftests/sgx/main.h
··· 38 38 bool encl_load(const char *path, struct encl *encl, unsigned long heap_size); 39 39 bool encl_measure(struct encl *encl); 40 40 bool encl_build(struct encl *encl); 41 + uint64_t encl_get_entry(struct encl *encl, const char *symbol); 41 42 42 43 int sgx_enter_enclave(void *rdi, void *rsi, long rdx, u32 function, void *r8, void *r9, 43 44 struct sgx_enclave_run *run);