DF-4147 / fixtest.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | /* * DF-4147 fix functional test: the cross-process fallback path must now * actually copy between the SERVER's user memory and the CLIENT's user * memory (the old raw-copyin/copyout implementation wrote into the * server's own address space at the client's VA, or EFAULTed). * * flow: client read(8192) -> window too big for the fast path -> server * CUSE_IOCTL_WRITE_DATA{peer = client buffer VA} -> must land in the * CLIENT's buffer -> SYNC(15) -> client's read returns 15 bytes. */ #include <sys/types.h> #include <sys/param.h> #include <sys/ioccom.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/wait.h> #include <time.h> #include <errno.h> #include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define CUSE_ERR_NONE 0 enum { CUSE_CMD_NONE, CUSE_CMD_OPEN, CUSE_CMD_CLOSE, CUSE_CMD_READ, CUSE_CMD_WRITE, CUSE_CMD_IOCTL, CUSE_CMD_POLL, CUSE_CMD_SIGNAL, CUSE_CMD_SYNC, CUSE_CMD_MAX }; struct cuse_dev; struct cuse_data_chunk { uintptr_t local_ptr, peer_ptr; unsigned long length; }; struct cuse_command { struct cuse_dev *dev; unsigned long fflags; uintptr_t per_file_handle; uintptr_t data_pointer; unsigned long argument, command; }; struct cuse_create_dev { struct cuse_dev *dev; uid_t user_id; gid_t group_id; int permissions; char devname[80]; }; #define CUSE_IOCTL_GET_COMMAND _IOR('C', 0, struct cuse_command) #define CUSE_IOCTL_WRITE_DATA _IOW('C', 1, struct cuse_data_chunk) #define CUSE_IOCTL_SYNC_COMMAND _IOW('C', 3, int) #define CUSE_IOCTL_CREATE_DEV _IOW('C', 8, struct cuse_create_dev) int main(void) { struct cuse_create_dev cd; struct cuse_command cmd; struct cuse_data_chunk ck; char local[32] = "HELLO-FIX-WORKS"; int fd, status, rv = 15; pid_t client; setvbuf(stdout, NULL, _IONBF, 0); printf("[*] fixtest start\n"); fd = open("/dev/cuse", O_RDWR); if (fd < 0) { perror("open /dev/cuse"); return 1; } memset(&cd, 0, sizeof(cd)); snprintf(cd.devname, sizeof(cd.devname), "cf%lx", (unsigned long)time(NULL)); cd.permissions = 0666; if (ioctl(fd, CUSE_IOCTL_CREATE_DEV, &cd) != 0) { perror("CREATE_DEV"); return 1; } setenv("CUSE_FIXT_DEV", cd.devname, 1); client = fork(); if (client == 0) { char *big = malloc(8192); int c = -1, i; close(fd); /* drop inherited /dev/cuse */ setgid(65534); setuid(65534); #if 0 /* desync our address space from the server's */ void *m = mmap(NULL, 64*1024*1024, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); (void)m; #endif char path[128]; snprintf(path, sizeof(path), "/dev/%s", getenv("CUSE_FIXT_DEV")); for (i = 0; i < 500; i++) { c = open(path, O_RDWR); if (c >= 0) break; if (i == 499) printf("[-] client open %s failed: %s\n", path, strerror(errno)); usleep(1000); } printf("[*] client opened fd=%d\n", c); if (c < 0) _exit(9); memset(big, 0, 8192); printf("[*] client entering read(8192) buf=%p\n", big); ssize_t r = read(c, big, 8192); printf("[*] client read returned %zd\n", r); if (r == 15 && memcmp(big, "HELLO-FIX-WORKS", 15) == 0) { printf("[+] cross-process fallback copy WORKS " "(client got \"%15.15s\")\n", big); _exit(0); } printf("[-] read=%zd buf=\"%15.15s\" - copy did not reach " "the client\n", r, big); _exit(1); } /* complete the client's OPEN first, then wait for the real READ */ printf("[*] dev=%s waiting for commands\n", cd.devname); for (;;) { if (ioctl(fd, CUSE_IOCTL_GET_COMMAND, &cmd) != 0) { perror("GET_COMMAND"); return 1; } printf("[*] got command %lu\n", cmd.command); if (cmd.command == CUSE_CMD_READ) break; rv = CUSE_ERR_NONE; ioctl(fd, CUSE_IOCTL_SYNC_COMMAND, &rv); } rv = 15; printf("[+] READ command: data_pointer=%p len=%lu\n", (void *)cmd.data_pointer, cmd.argument); /* push data straight to the client's buffer via the fallback path */ ck.local_ptr = (uintptr_t)local; ck.peer_ptr = cmd.data_pointer; ck.length = 15; if (ioctl(fd, CUSE_IOCTL_WRITE_DATA, &ck) != 0) printf("[*] WRITE_DATA returned %d (%s)\n", errno, strerror(errno)); if (ioctl(fd, CUSE_IOCTL_SYNC_COMMAND, &rv) != 0) perror("SYNC"); /* drain the client's CLOSE so its exit doesn't block */ { struct cuse_command c2; int rv2 = CUSE_ERR_NONE; if (ioctl(fd, CUSE_IOCTL_GET_COMMAND, &c2) == 0) ioctl(fd, CUSE_IOCTL_SYNC_COMMAND, &rv2); } waitpid(client, &status, 0); close(fd); printf("[*] client exit status %d\n", WEXITSTATUS(status)); return (WEXITSTATUS(status) == 0) ? 0 : 1; } |