openat2_test.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Author: Aleksa Sarai <cyphar@cyphar.com>
  4. * Copyright (C) 2018-2019 SUSE LLC.
  5. */
  6. #define _GNU_SOURCE
  7. #define __SANE_USERSPACE_TYPES__ // Use ll64
  8. #include <fcntl.h>
  9. #include <sched.h>
  10. #include <sys/stat.h>
  11. #include <sys/types.h>
  12. #include <sys/mount.h>
  13. #include <stdlib.h>
  14. #include <stdbool.h>
  15. #include <string.h>
  16. #include "kselftest.h"
  17. #include "helpers.h"
  18. /*
  19. * O_LARGEFILE is set to 0 by glibc.
  20. * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
  21. */
  22. #undef O_LARGEFILE
  23. #ifdef __aarch64__
  24. #define O_LARGEFILE 0x20000
  25. #else
  26. #define O_LARGEFILE 0x8000
  27. #endif
  28. struct open_how_ext {
  29. struct open_how inner;
  30. uint32_t extra1;
  31. char pad1[128];
  32. uint32_t extra2;
  33. char pad2[128];
  34. uint32_t extra3;
  35. };
  36. struct struct_test {
  37. const char *name;
  38. struct open_how_ext arg;
  39. size_t size;
  40. int err;
  41. };
  42. #define NUM_OPENAT2_STRUCT_TESTS 7
  43. #define NUM_OPENAT2_STRUCT_VARIATIONS 13
  44. void test_openat2_struct(void)
  45. {
  46. int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
  47. struct struct_test tests[] = {
  48. /* Normal struct. */
  49. { .name = "normal struct",
  50. .arg.inner.flags = O_RDONLY,
  51. .size = sizeof(struct open_how) },
  52. /* Bigger struct, with zeroed out end. */
  53. { .name = "bigger struct (zeroed out)",
  54. .arg.inner.flags = O_RDONLY,
  55. .size = sizeof(struct open_how_ext) },
  56. /* TODO: Once expanded, check zero-padding. */
  57. /* Smaller than version-0 struct. */
  58. { .name = "zero-sized 'struct'",
  59. .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL },
  60. { .name = "smaller-than-v0 struct",
  61. .arg.inner.flags = O_RDONLY,
  62. .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL },
  63. /* Bigger struct, with non-zero trailing bytes. */
  64. { .name = "bigger struct (non-zero data in first 'future field')",
  65. .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef,
  66. .size = sizeof(struct open_how_ext), .err = -E2BIG },
  67. { .name = "bigger struct (non-zero data in middle of 'future fields')",
  68. .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe,
  69. .size = sizeof(struct open_how_ext), .err = -E2BIG },
  70. { .name = "bigger struct (non-zero data at end of 'future fields')",
  71. .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea,
  72. .size = sizeof(struct open_how_ext), .err = -E2BIG },
  73. };
  74. BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS);
  75. BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS);
  76. for (int i = 0; i < ARRAY_LEN(tests); i++) {
  77. struct struct_test *test = &tests[i];
  78. struct open_how_ext how_ext = test->arg;
  79. for (int j = 0; j < ARRAY_LEN(misalignments); j++) {
  80. int fd, misalign = misalignments[j];
  81. char *fdpath = NULL;
  82. bool failed;
  83. void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
  84. void *copy = NULL, *how_copy = &how_ext;
  85. if (!openat2_supported) {
  86. ksft_print_msg("openat2(2) unsupported\n");
  87. resultfn = ksft_test_result_skip;
  88. goto skip;
  89. }
  90. if (misalign) {
  91. /*
  92. * Explicitly misalign the structure copying it with the given
  93. * (mis)alignment offset. The other data is set to be non-zero to
  94. * make sure that non-zero bytes outside the struct aren't checked
  95. *
  96. * This is effectively to check that is_zeroed_user() works.
  97. */
  98. copy = malloc(misalign + sizeof(how_ext));
  99. how_copy = copy + misalign;
  100. memset(copy, 0xff, misalign);
  101. memcpy(how_copy, &how_ext, sizeof(how_ext));
  102. }
  103. fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size);
  104. if (test->err >= 0)
  105. failed = (fd < 0);
  106. else
  107. failed = (fd != test->err);
  108. if (fd >= 0) {
  109. fdpath = fdreadlink(fd);
  110. close(fd);
  111. }
  112. if (failed) {
  113. resultfn = ksft_test_result_fail;
  114. ksft_print_msg("openat2 unexpectedly returned ");
  115. if (fdpath)
  116. ksft_print_msg("%d['%s']\n", fd, fdpath);
  117. else
  118. ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
  119. }
  120. skip:
  121. if (test->err >= 0)
  122. resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
  123. test->name, misalign);
  124. else
  125. resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
  126. test->name, misalign, test->err,
  127. strerror(-test->err));
  128. free(copy);
  129. free(fdpath);
  130. fflush(stdout);
  131. }
  132. }
  133. }
  134. struct flag_test {
  135. const char *name;
  136. struct open_how how;
  137. int err;
  138. };
  139. #define NUM_OPENAT2_FLAG_TESTS 25
  140. void test_openat2_flags(void)
  141. {
  142. struct flag_test tests[] = {
  143. /* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
  144. { .name = "incompatible flags (O_TMPFILE | O_PATH)",
  145. .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL },
  146. { .name = "incompatible flags (O_TMPFILE | O_CREAT)",
  147. .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL },
  148. /* O_PATH only permits certain other flags to be set ... */
  149. { .name = "compatible flags (O_PATH | O_CLOEXEC)",
  150. .how.flags = O_PATH | O_CLOEXEC },
  151. { .name = "compatible flags (O_PATH | O_DIRECTORY)",
  152. .how.flags = O_PATH | O_DIRECTORY },
  153. { .name = "compatible flags (O_PATH | O_NOFOLLOW)",
  154. .how.flags = O_PATH | O_NOFOLLOW },
  155. /* ... and others are absolutely not permitted. */
  156. { .name = "incompatible flags (O_PATH | O_RDWR)",
  157. .how.flags = O_PATH | O_RDWR, .err = -EINVAL },
  158. { .name = "incompatible flags (O_PATH | O_CREAT)",
  159. .how.flags = O_PATH | O_CREAT, .err = -EINVAL },
  160. { .name = "incompatible flags (O_PATH | O_EXCL)",
  161. .how.flags = O_PATH | O_EXCL, .err = -EINVAL },
  162. { .name = "incompatible flags (O_PATH | O_NOCTTY)",
  163. .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL },
  164. { .name = "incompatible flags (O_PATH | O_DIRECT)",
  165. .how.flags = O_PATH | O_DIRECT, .err = -EINVAL },
  166. { .name = "incompatible flags (O_PATH | O_LARGEFILE)",
  167. .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL },
  168. /* ->mode must only be set with O_{CREAT,TMPFILE}. */
  169. { .name = "non-zero how.mode and O_RDONLY",
  170. .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL },
  171. { .name = "non-zero how.mode and O_PATH",
  172. .how.flags = O_PATH, .how.mode = 0600, .err = -EINVAL },
  173. { .name = "valid how.mode and O_CREAT",
  174. .how.flags = O_CREAT, .how.mode = 0600 },
  175. { .name = "valid how.mode and O_TMPFILE",
  176. .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
  177. /* ->mode must only contain 0777 bits. */
  178. { .name = "invalid how.mode and O_CREAT",
  179. .how.flags = O_CREAT,
  180. .how.mode = 0xFFFF, .err = -EINVAL },
  181. { .name = "invalid (very large) how.mode and O_CREAT",
  182. .how.flags = O_CREAT,
  183. .how.mode = 0xC000000000000000ULL, .err = -EINVAL },
  184. { .name = "invalid how.mode and O_TMPFILE",
  185. .how.flags = O_TMPFILE | O_RDWR,
  186. .how.mode = 0x1337, .err = -EINVAL },
  187. { .name = "invalid (very large) how.mode and O_TMPFILE",
  188. .how.flags = O_TMPFILE | O_RDWR,
  189. .how.mode = 0x0000A00000000000ULL, .err = -EINVAL },
  190. /* ->resolve flags must not conflict. */
  191. { .name = "incompatible resolve flags (BENEATH | IN_ROOT)",
  192. .how.flags = O_RDONLY,
  193. .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
  194. .err = -EINVAL },
  195. /* ->resolve must only contain RESOLVE_* flags. */
  196. { .name = "invalid how.resolve and O_RDONLY",
  197. .how.flags = O_RDONLY,
  198. .how.resolve = 0x1337, .err = -EINVAL },
  199. { .name = "invalid how.resolve and O_CREAT",
  200. .how.flags = O_CREAT,
  201. .how.resolve = 0x1337, .err = -EINVAL },
  202. { .name = "invalid how.resolve and O_TMPFILE",
  203. .how.flags = O_TMPFILE | O_RDWR,
  204. .how.resolve = 0x1337, .err = -EINVAL },
  205. { .name = "invalid how.resolve and O_PATH",
  206. .how.flags = O_PATH,
  207. .how.resolve = 0x1337, .err = -EINVAL },
  208. /* currently unknown upper 32 bit rejected. */
  209. { .name = "currently unknown bit (1 << 63)",
  210. .how.flags = O_RDONLY | (1ULL << 63),
  211. .how.resolve = 0, .err = -EINVAL },
  212. };
  213. BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
  214. for (int i = 0; i < ARRAY_LEN(tests); i++) {
  215. int fd, fdflags = -1;
  216. char *path, *fdpath = NULL;
  217. bool failed = false;
  218. struct flag_test *test = &tests[i];
  219. void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
  220. if (!openat2_supported) {
  221. ksft_print_msg("openat2(2) unsupported\n");
  222. resultfn = ksft_test_result_skip;
  223. goto skip;
  224. }
  225. path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
  226. unlink(path);
  227. fd = sys_openat2(AT_FDCWD, path, &test->how);
  228. if (fd < 0 && fd == -EOPNOTSUPP) {
  229. /*
  230. * Skip the testcase if it failed because not supported
  231. * by FS. (e.g. a valid O_TMPFILE combination on NFS)
  232. */
  233. ksft_test_result_skip("openat2 with %s fails with %d (%s)\n",
  234. test->name, fd, strerror(-fd));
  235. goto next;
  236. }
  237. if (test->err >= 0)
  238. failed = (fd < 0);
  239. else
  240. failed = (fd != test->err);
  241. if (fd >= 0) {
  242. int otherflags;
  243. fdpath = fdreadlink(fd);
  244. fdflags = fcntl(fd, F_GETFL);
  245. otherflags = fcntl(fd, F_GETFD);
  246. close(fd);
  247. E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
  248. E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
  249. /* O_CLOEXEC isn't shown in F_GETFL. */
  250. if (otherflags & FD_CLOEXEC)
  251. fdflags |= O_CLOEXEC;
  252. /* O_CREAT is hidden from F_GETFL. */
  253. if (test->how.flags & O_CREAT)
  254. fdflags |= O_CREAT;
  255. if (!(test->how.flags & O_LARGEFILE))
  256. fdflags &= ~O_LARGEFILE;
  257. failed |= (fdflags != test->how.flags);
  258. }
  259. if (failed) {
  260. resultfn = ksft_test_result_fail;
  261. ksft_print_msg("openat2 unexpectedly returned ");
  262. if (fdpath)
  263. ksft_print_msg("%d['%s'] with %X (!= %llX)\n",
  264. fd, fdpath, fdflags,
  265. test->how.flags);
  266. else
  267. ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
  268. }
  269. skip:
  270. if (test->err >= 0)
  271. resultfn("openat2 with %s succeeds\n", test->name);
  272. else
  273. resultfn("openat2 with %s fails with %d (%s)\n",
  274. test->name, test->err, strerror(-test->err));
  275. next:
  276. free(fdpath);
  277. fflush(stdout);
  278. }
  279. }
  280. #define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
  281. NUM_OPENAT2_FLAG_TESTS)
  282. int main(int argc, char **argv)
  283. {
  284. ksft_print_header();
  285. ksft_set_plan(NUM_TESTS);
  286. test_openat2_struct();
  287. test_openat2_flags();
  288. if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
  289. ksft_exit_fail();
  290. else
  291. ksft_exit_pass();
  292. }