sgx_ipfs.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /*
  2. * Copyright (C) 2022 Intel Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #if WASM_ENABLE_SGX_IPFS != 0
  6. #include "ssp_config.h"
  7. #include "bh_platform.h"
  8. #include "sgx_ipfs.h"
  9. #include <errno.h>
  10. #include "sgx_tprotected_fs.h"
  11. #define SGX_ERROR_FILE_LOWEST_ERROR_ID SGX_ERROR_FILE_BAD_STATUS
  12. #define SGX_ERROR_FILE_HIGHEST_ERROR_ID SGX_ERROR_FILE_CLOSE_FAILED
  13. // Internal buffer filled with zeroes and used when extending the size of
  14. // protected files.
  15. #define ZEROES_PADDING_LENGTH 32 * 1024
  16. char zeroes_padding[ZEROES_PADDING_LENGTH] = { 0 };
  17. // The mapping between file descriptors and IPFS file pointers.
  18. static HashMap *ipfs_file_list;
  19. // Converts an SGX error code to a POSIX error code.
  20. static __wasi_errno_t
  21. convert_sgx_errno(int error)
  22. {
  23. if (error >= SGX_ERROR_FILE_LOWEST_ERROR_ID
  24. && error <= SGX_ERROR_FILE_HIGHEST_ERROR_ID) {
  25. switch (error) {
  26. /* The file is in bad status */
  27. case SGX_ERROR_FILE_BAD_STATUS:
  28. return ENOTRECOVERABLE;
  29. /* The Key ID field is all zeros, can't re-generate the encryption
  30. * key */
  31. case SGX_ERROR_FILE_NO_KEY_ID:
  32. return EKEYREJECTED;
  33. /* The current file name is different then the original file name
  34. * (not allowed, substitution attack) */
  35. case SGX_ERROR_FILE_NAME_MISMATCH:
  36. return EIO;
  37. /* The file is not an SGX file */
  38. case SGX_ERROR_FILE_NOT_SGX_FILE:
  39. return EEXIST;
  40. /* A recovery file can't be opened, so flush operation can't
  41. * continue (only used when no EXXX is returned) */
  42. case SGX_ERROR_FILE_CANT_OPEN_RECOVERY_FILE:
  43. return EIO;
  44. /* A recovery file can't be written, so flush operation can't
  45. * continue (only used when no EXXX is returned) */
  46. case SGX_ERROR_FILE_CANT_WRITE_RECOVERY_FILE:
  47. return EIO;
  48. /* When openeing the file, recovery is needed, but the recovery
  49. * process failed */
  50. case SGX_ERROR_FILE_RECOVERY_NEEDED:
  51. return EIO;
  52. /* fflush operation (to disk) failed (only used when no EXXX is
  53. * returned) */
  54. case SGX_ERROR_FILE_FLUSH_FAILED:
  55. return EIO;
  56. /* fclose operation (to disk) failed (only used when no EXXX is
  57. * returned) */
  58. case SGX_ERROR_FILE_CLOSE_FAILED:
  59. return EIO;
  60. }
  61. }
  62. return error;
  63. }
  64. static void *
  65. fd2file(int fd)
  66. {
  67. return bh_hash_map_find(ipfs_file_list, (void *)(intptr_t)fd);
  68. }
  69. static void
  70. ipfs_file_destroy(void *sgx_file)
  71. {
  72. sgx_fclose(sgx_file);
  73. }
  74. // Writes a given number of zeroes in file at the current offset.
  75. // The return value is zero if successful; otherwise non-zero.
  76. static int
  77. ipfs_write_zeroes(void *sgx_file, size_t len)
  78. {
  79. int min_count;
  80. while (len > 0) {
  81. min_count = len < ZEROES_PADDING_LENGTH ? len : ZEROES_PADDING_LENGTH;
  82. if (sgx_fwrite(zeroes_padding, 1, min_count, sgx_file) == 0) {
  83. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  84. return -1;
  85. }
  86. len -= min_count;
  87. }
  88. return 0;
  89. }
  90. int
  91. ipfs_init()
  92. {
  93. ipfs_file_list =
  94. bh_hash_map_create(32, true, (HashFunc)fd_hash, (KeyEqualFunc)fd_equal,
  95. NULL, (ValueDestroyFunc)ipfs_file_destroy);
  96. return ipfs_file_list != NULL ? BHT_OK : BHT_ERROR;
  97. }
  98. void
  99. ipfs_destroy()
  100. {
  101. bh_hash_map_destroy(ipfs_file_list);
  102. }
  103. int
  104. ipfs_posix_fallocate(int fd, off_t offset, size_t len)
  105. {
  106. void *sgx_file = fd2file(fd);
  107. if (!sgx_file) {
  108. return EBADF;
  109. }
  110. // The wrapper for fseek takes care of extending the file if sought beyond
  111. // the end
  112. if (ipfs_lseek(fd, offset + len, SEEK_SET) == -1) {
  113. return errno;
  114. }
  115. // Make sure the file is allocated by flushing it
  116. if (sgx_fflush(sgx_file) != 0) {
  117. return errno;
  118. }
  119. return 0;
  120. }
  121. size_t
  122. ipfs_read(int fd, const struct iovec *iov, int iovcnt, bool has_offset,
  123. off_t offset)
  124. {
  125. int i;
  126. off_t original_offset = 0;
  127. void *sgx_file = fd2file(fd);
  128. size_t read_result, number_of_read_bytes = 0;
  129. if (!sgx_file) {
  130. errno = EBADF;
  131. return -1;
  132. }
  133. if (has_offset) {
  134. // Save the current offset, to restore it after the read operation
  135. original_offset = (off_t)sgx_ftell(sgx_file);
  136. if (original_offset == -1) {
  137. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  138. return -1;
  139. }
  140. // Move to the desired location
  141. if (sgx_fseek(sgx_file, offset, SEEK_SET) == -1) {
  142. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  143. return -1;
  144. }
  145. }
  146. // For each element in the vector
  147. for (i = 0; i < iovcnt; i++) {
  148. if (iov[i].iov_len == 0)
  149. continue;
  150. read_result = sgx_fread(iov[i].iov_base, 1, iov[i].iov_len, sgx_file);
  151. number_of_read_bytes += read_result;
  152. if (read_result != iov[i].iov_len) {
  153. if (!sgx_feof(sgx_file)) {
  154. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  155. return -1;
  156. }
  157. }
  158. }
  159. if (has_offset) {
  160. // Restore the position of the cursor
  161. if (sgx_fseek(sgx_file, original_offset, SEEK_SET) == -1) {
  162. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  163. return -1;
  164. }
  165. }
  166. return number_of_read_bytes;
  167. }
  168. size_t
  169. ipfs_write(int fd, const struct iovec *iov, int iovcnt, bool has_offset,
  170. off_t offset)
  171. {
  172. int i;
  173. off_t original_offset = 0;
  174. void *sgx_file = fd2file(fd);
  175. size_t write_result, number_of_written_bytes = 0;
  176. if (!sgx_file) {
  177. errno = EBADF;
  178. return -1;
  179. }
  180. if (has_offset) {
  181. // Save the current offset, to restore it after the read operation
  182. original_offset = (off_t)sgx_ftell(sgx_file);
  183. if (original_offset == -1) {
  184. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  185. return -1;
  186. }
  187. // Move to the desired location
  188. if (sgx_fseek(sgx_file, offset, SEEK_SET) == -1) {
  189. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  190. return -1;
  191. }
  192. }
  193. // For each element in the vector
  194. for (i = 0; i < iovcnt; i++) {
  195. if (iov[i].iov_len == 0)
  196. continue;
  197. write_result = sgx_fwrite(iov[i].iov_base, 1, iov[i].iov_len, sgx_file);
  198. number_of_written_bytes += write_result;
  199. if (write_result != iov[i].iov_len) {
  200. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  201. return -1;
  202. }
  203. }
  204. if (has_offset) {
  205. // Restore the position of the cursor
  206. if (sgx_fseek(sgx_file, original_offset, SEEK_SET) == -1) {
  207. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  208. return -1;
  209. }
  210. }
  211. return number_of_written_bytes;
  212. }
  213. int
  214. ipfs_close(int fd)
  215. {
  216. void *sgx_file;
  217. if (!bh_hash_map_remove(ipfs_file_list, (void *)(intptr_t)fd, NULL,
  218. &sgx_file)) {
  219. errno = EBADF;
  220. return -1;
  221. }
  222. if (sgx_fclose(sgx_file)) {
  223. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  224. return -1;
  225. }
  226. return 0;
  227. }
  228. void *
  229. ipfs_fopen(int fd, int flags)
  230. {
  231. // Mapping back the mode
  232. const char *mode;
  233. bool must_create = (flags & O_CREAT) != 0;
  234. bool must_truncate = (flags & O_TRUNC) != 0;
  235. bool must_append = (flags & O_APPEND) != 0;
  236. bool read_only = (flags & O_ACCMODE) == O_RDONLY;
  237. bool write_only = (flags & O_ACCMODE) == O_WRONLY;
  238. bool read_write = (flags & O_ACCMODE) == O_RDWR;
  239. // The mapping of the mode is similar to the table in the official
  240. // specifications:
  241. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html
  242. // Note that POSIX has obtained a file descriptor beforehand.
  243. // If opened with a destructive mode ("w" or "w+"), the truncate operation
  244. // already occurred and must not be repeated because this will invalidate
  245. // the file descriptor obtained by POSIX. Therefore, we do NOT map to the
  246. // modes that truncate the file ("w" and "w+"). Instead, we map to a
  247. // non-destructive mode ("r+").
  248. if (read_only)
  249. mode = "r";
  250. else if (write_only && must_create && must_truncate)
  251. // Rather than "w", we map to a non-destructive mode
  252. mode = "r+";
  253. else if (write_only && must_create && must_append)
  254. mode = "a";
  255. else if (read_write && must_create && must_append)
  256. mode = "a+";
  257. else if (read_write)
  258. // Rather than "w+", we map to a non-destructive mode
  259. mode = "r+";
  260. else
  261. mode = NULL;
  262. // Cannot map the requested access to the SGX IPFS
  263. if (mode == NULL) {
  264. errno = __WASI_ENOTCAPABLE;
  265. return NULL;
  266. }
  267. // Determine the symbolic link of the file descriptor, because IPFS does not
  268. // support opening a relative path to a file descriptor (i.e., openat).
  269. // Using the symbolic link in /proc/self allows to retrieve the same path as
  270. // opened by the initial openat and respects the chroot of WAMR.
  271. size_t ret;
  272. char symbolic_path[32];
  273. ret =
  274. snprintf(symbolic_path, sizeof(symbolic_path), "/proc/self/fd/%d", fd);
  275. if (ret >= sizeof(symbolic_path)) {
  276. errno = ENAMETOOLONG;
  277. return NULL;
  278. }
  279. // Resolve the symbolic link to real absolute path, because IPFS can only
  280. // open a file with a same file name it was initially created. Otherwise,
  281. // IPFS throws SGX_ERROR_FILE_NAME_MISMATCH.
  282. char real_path[PATH_MAX] = { 0 };
  283. ret = readlink(symbolic_path, real_path, PATH_MAX - 1);
  284. if (ret == -1)
  285. return NULL;
  286. // Opening the file using the real path
  287. void *sgx_file = sgx_fopen_auto_key(real_path, mode);
  288. if (sgx_file == NULL) {
  289. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  290. return NULL;
  291. }
  292. if (!bh_hash_map_insert(ipfs_file_list, (void *)(intptr_t)fd, sgx_file)) {
  293. errno = __WASI_ECANCELED;
  294. sgx_fclose(sgx_file);
  295. os_printf("An error occurred while inserting the IPFS file pointer in "
  296. "the map.");
  297. return NULL;
  298. }
  299. return sgx_file;
  300. }
  301. int
  302. ipfs_fflush(int fd)
  303. {
  304. void *sgx_file = fd2file(fd);
  305. if (!sgx_file) {
  306. errno = EBADF;
  307. return EOF;
  308. }
  309. int ret = sgx_fflush(sgx_file);
  310. if (ret == 1) {
  311. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  312. return EOF;
  313. }
  314. return ret;
  315. }
  316. off_t
  317. ipfs_lseek(int fd, off_t offset, int nwhence)
  318. {
  319. off_t cursor_current_location;
  320. void *sgx_file = fd2file(fd);
  321. if (!sgx_file) {
  322. errno = EBADF;
  323. return -1;
  324. }
  325. // Optimization: if the offset is 0 and the whence is SEEK_CUR,
  326. // this is equivalent of a call to ftell.
  327. if (offset == 0 && nwhence == SEEK_CUR) {
  328. cursor_current_location = (off_t)sgx_ftell(sgx_file);
  329. if (cursor_current_location == -1) {
  330. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  331. return -1;
  332. }
  333. return cursor_current_location;
  334. }
  335. int fseek_result = sgx_fseek(sgx_file, offset, nwhence);
  336. if (fseek_result == 0) {
  337. off_t new_offset = (off_t)sgx_ftell(sgx_file);
  338. if (new_offset == -1) {
  339. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  340. return -1;
  341. }
  342. return new_offset;
  343. }
  344. else {
  345. // In the case fseek returned an error
  346. int sgx_error = sgx_ferror(sgx_file);
  347. if (sgx_error != EINVAL) {
  348. errno = convert_sgx_errno(sgx_error);
  349. return -1;
  350. }
  351. // We must consider a difference in behavior of sgx_fseek and the POSIX
  352. // fseek. If the cursor is moved beyond the end of the file, sgx_fseek
  353. // returns an error, whereas POSIX fseek accepts the cursor move and
  354. // fill with zeroes the difference for the next write. This
  355. // implementation handle zeroes completion and moving the cursor forward
  356. // the end of the file, but does it now (during the fseek), which is
  357. // different compared to POSIX implementation, that writes zeroes on the
  358. // next write. This avoids the runtime to keep track of the cursor
  359. // manually.
  360. // Assume the error is raised because the cursor is moved beyond the end
  361. // of the file.
  362. // If the whence is the current cursor location, retrieve it
  363. if (nwhence == SEEK_CUR) {
  364. cursor_current_location = (off_t)sgx_ftell(sgx_file);
  365. }
  366. // Move the cursor at the end of the file
  367. if (sgx_fseek(sgx_file, 0, SEEK_END) == -1) {
  368. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  369. return -1;
  370. }
  371. // Compute the number of zeroes to append.
  372. int64_t number_of_zeroes;
  373. switch (nwhence) {
  374. case SEEK_SET:
  375. number_of_zeroes = offset - sgx_ftell(sgx_file);
  376. break;
  377. case SEEK_END:
  378. number_of_zeroes = offset;
  379. break;
  380. case SEEK_CUR:
  381. number_of_zeroes =
  382. cursor_current_location + offset - sgx_ftell(sgx_file);
  383. break;
  384. default:
  385. errno = EINVAL;
  386. return -1;
  387. }
  388. // Write the missing zeroes
  389. if (ipfs_write_zeroes(sgx_file, number_of_zeroes) != 0) {
  390. return -1;
  391. }
  392. // Move again at the end of the file
  393. if (sgx_fseek(sgx_file, 0, SEEK_END) == -1) {
  394. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  395. return -1;
  396. }
  397. return offset;
  398. }
  399. }
  400. // The official API does not provide a way to truncate files.
  401. // Only files extension is supported.
  402. int
  403. ipfs_ftruncate(int fd, off_t len)
  404. {
  405. void *sgx_file = fd2file(fd);
  406. if (!sgx_file) {
  407. errno = EBADF;
  408. return -1;
  409. }
  410. off_t original_offset = sgx_ftell(sgx_file);
  411. // Optimization path: if the length is smaller than the offset,
  412. // IPFS does not support truncate to a smaller size.
  413. if (len < original_offset) {
  414. os_printf(
  415. "SGX IPFS does not support truncate files to smaller sizes.\n");
  416. return __WASI_ECANCELED;
  417. }
  418. // Move to the end of the file to determine whether this is
  419. // a file extension or reduction.
  420. if (sgx_fseek(sgx_file, 0, SEEK_END) == -1) {
  421. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  422. return -1;
  423. }
  424. off_t file_size = sgx_ftell(sgx_file);
  425. // Reducing the file space is not supported by IPFS.
  426. if (len < file_size) {
  427. os_printf(
  428. "SGX IPFS does not support truncate files to smaller sizes.\n");
  429. return __WASI_ECANCELED;
  430. }
  431. // Increasing the size is equal to writing from the end of the file
  432. // with null bytes.
  433. if (ipfs_write_zeroes(sgx_file, len - file_size) != 0) {
  434. return -1;
  435. }
  436. // Restore the position of the cursor
  437. if (sgx_fseek(sgx_file, original_offset, SEEK_SET) == -1) {
  438. errno = convert_sgx_errno(sgx_ferror(sgx_file));
  439. return -1;
  440. }
  441. return 0;
  442. }
  443. #endif /* end of WASM_ENABLE_SGX_IPFS */