0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #define _GNU_SOURCE
0017 #define __EXPORTED_HEADERS__
0018
0019 #include <errno.h>
0020 #include <inttypes.h>
0021 #include <limits.h>
0022 #include <linux/falloc.h>
0023 #include <fcntl.h>
0024 #include <linux/memfd.h>
0025 #include <sched.h>
0026 #include <stdio.h>
0027 #include <stdlib.h>
0028 #include <signal.h>
0029 #include <string.h>
0030 #include <sys/mman.h>
0031 #include <sys/stat.h>
0032 #include <sys/syscall.h>
0033 #include <sys/wait.h>
0034 #include <unistd.h>
0035
0036 #include "common.h"
0037
0038 #define MFD_DEF_SIZE 8192
0039 #define STACK_SIZE 65536
0040
0041 static size_t mfd_def_size = MFD_DEF_SIZE;
0042
0043 static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags)
0044 {
0045 int r, fd;
0046
0047 fd = sys_memfd_create(name, flags);
0048 if (fd < 0) {
0049 printf("memfd_create(\"%s\", %u) failed: %m\n",
0050 name, flags);
0051 abort();
0052 }
0053
0054 r = ftruncate(fd, sz);
0055 if (r < 0) {
0056 printf("ftruncate(%llu) failed: %m\n", (unsigned long long)sz);
0057 abort();
0058 }
0059
0060 return fd;
0061 }
0062
0063 static __u64 mfd_assert_get_seals(int fd)
0064 {
0065 long r;
0066
0067 r = fcntl(fd, F_GET_SEALS);
0068 if (r < 0) {
0069 printf("GET_SEALS(%d) failed: %m\n", fd);
0070 abort();
0071 }
0072
0073 return r;
0074 }
0075
0076 static void mfd_assert_has_seals(int fd, __u64 seals)
0077 {
0078 __u64 s;
0079
0080 s = mfd_assert_get_seals(fd);
0081 if (s != seals) {
0082 printf("%llu != %llu = GET_SEALS(%d)\n",
0083 (unsigned long long)seals, (unsigned long long)s, fd);
0084 abort();
0085 }
0086 }
0087
0088 static void mfd_assert_add_seals(int fd, __u64 seals)
0089 {
0090 long r;
0091 __u64 s;
0092
0093 s = mfd_assert_get_seals(fd);
0094 r = fcntl(fd, F_ADD_SEALS, seals);
0095 if (r < 0) {
0096 printf("ADD_SEALS(%d, %llu -> %llu) failed: %m\n",
0097 fd, (unsigned long long)s, (unsigned long long)seals);
0098 abort();
0099 }
0100 }
0101
0102 static int mfd_busy_add_seals(int fd, __u64 seals)
0103 {
0104 long r;
0105 __u64 s;
0106
0107 r = fcntl(fd, F_GET_SEALS);
0108 if (r < 0)
0109 s = 0;
0110 else
0111 s = r;
0112
0113 r = fcntl(fd, F_ADD_SEALS, seals);
0114 if (r < 0 && errno != EBUSY) {
0115 printf("ADD_SEALS(%d, %llu -> %llu) didn't fail as expected with EBUSY: %m\n",
0116 fd, (unsigned long long)s, (unsigned long long)seals);
0117 abort();
0118 }
0119
0120 return r;
0121 }
0122
0123 static void *mfd_assert_mmap_shared(int fd)
0124 {
0125 void *p;
0126
0127 p = mmap(NULL,
0128 mfd_def_size,
0129 PROT_READ | PROT_WRITE,
0130 MAP_SHARED,
0131 fd,
0132 0);
0133 if (p == MAP_FAILED) {
0134 printf("mmap() failed: %m\n");
0135 abort();
0136 }
0137
0138 return p;
0139 }
0140
0141 static void *mfd_assert_mmap_private(int fd)
0142 {
0143 void *p;
0144
0145 p = mmap(NULL,
0146 mfd_def_size,
0147 PROT_READ | PROT_WRITE,
0148 MAP_PRIVATE,
0149 fd,
0150 0);
0151 if (p == MAP_FAILED) {
0152 printf("mmap() failed: %m\n");
0153 abort();
0154 }
0155
0156 return p;
0157 }
0158
0159 static int global_mfd = -1;
0160 static void *global_p = NULL;
0161
0162 static int sealing_thread_fn(void *arg)
0163 {
0164 int sig, r;
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176 usleep(200000);
0177
0178
0179 munmap(global_p, mfd_def_size);
0180
0181
0182
0183
0184
0185 r = mfd_busy_add_seals(global_mfd, F_SEAL_WRITE);
0186 if (r >= 0) {
0187 printf("HURRAY! This kernel fixed GUP races!\n");
0188 } else {
0189
0190 sleep(1);
0191
0192
0193 mfd_assert_add_seals(global_mfd, F_SEAL_WRITE);
0194 }
0195
0196 return 0;
0197 }
0198
0199 static pid_t spawn_sealing_thread(void)
0200 {
0201 uint8_t *stack;
0202 pid_t pid;
0203
0204 stack = malloc(STACK_SIZE);
0205 if (!stack) {
0206 printf("malloc(STACK_SIZE) failed: %m\n");
0207 abort();
0208 }
0209
0210 pid = clone(sealing_thread_fn,
0211 stack + STACK_SIZE,
0212 SIGCHLD | CLONE_FILES | CLONE_FS | CLONE_VM,
0213 NULL);
0214 if (pid < 0) {
0215 printf("clone() failed: %m\n");
0216 abort();
0217 }
0218
0219 return pid;
0220 }
0221
0222 static void join_sealing_thread(pid_t pid)
0223 {
0224 waitpid(pid, NULL, 0);
0225 }
0226
0227 int main(int argc, char **argv)
0228 {
0229 char *zero;
0230 int fd, mfd, r;
0231 void *p;
0232 int was_sealed;
0233 pid_t pid;
0234
0235 if (argc < 2) {
0236 printf("error: please pass path to file in fuse_mnt mount-point\n");
0237 abort();
0238 }
0239
0240 if (argc >= 3) {
0241 if (!strcmp(argv[2], "hugetlbfs")) {
0242 unsigned long hpage_size = default_huge_page_size();
0243
0244 if (!hpage_size) {
0245 printf("Unable to determine huge page size\n");
0246 abort();
0247 }
0248
0249 hugetlbfs_test = 1;
0250 mfd_def_size = hpage_size * 2;
0251 } else {
0252 printf("Unknown option: %s\n", argv[2]);
0253 abort();
0254 }
0255 }
0256
0257 zero = calloc(sizeof(*zero), mfd_def_size);
0258
0259
0260 printf("opening: %s\n", argv[1]);
0261 fd = open(argv[1], O_RDONLY | O_CLOEXEC);
0262 if (fd < 0) {
0263 printf("cannot open(\"%s\"): %m\n", argv[1]);
0264 abort();
0265 }
0266
0267
0268 mfd = mfd_assert_new("kern_memfd_fuse",
0269 mfd_def_size,
0270 MFD_CLOEXEC | MFD_ALLOW_SEALING);
0271
0272
0273 p = mfd_assert_mmap_shared(mfd);
0274
0275
0276
0277 global_mfd = mfd;
0278 global_p = p;
0279 pid = spawn_sealing_thread();
0280
0281
0282
0283
0284
0285
0286
0287
0288 r = read(fd, p, mfd_def_size);
0289 if (r < 0) {
0290 printf("read() failed: %m\n");
0291 abort();
0292 } else if (!r) {
0293 printf("unexpected EOF on read()\n");
0294 abort();
0295 }
0296
0297 was_sealed = mfd_assert_get_seals(mfd) & F_SEAL_WRITE;
0298
0299
0300
0301 join_sealing_thread(pid);
0302 mfd_assert_has_seals(mfd, F_SEAL_WRITE);
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314 p = mfd_assert_mmap_private(mfd);
0315 if (was_sealed && memcmp(p, zero, mfd_def_size)) {
0316 printf("memfd sealed during read() but data not discarded\n");
0317 abort();
0318 } else if (!was_sealed && !memcmp(p, zero, mfd_def_size)) {
0319 printf("memfd sealed after read() but data discarded\n");
0320 abort();
0321 }
0322
0323 close(mfd);
0324 close(fd);
0325
0326 printf("fuse: DONE\n");
0327 free(zero);
0328
0329 return 0;
0330 }