162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com> 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci// Test that /proc/$KERNEL_THREAD/fd/ is empty. 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#undef NDEBUG 1962306a36Sopenharmony_ci#include <sys/syscall.h> 2062306a36Sopenharmony_ci#include <assert.h> 2162306a36Sopenharmony_ci#include <dirent.h> 2262306a36Sopenharmony_ci#include <limits.h> 2362306a36Sopenharmony_ci#include <stdio.h> 2462306a36Sopenharmony_ci#include <string.h> 2562306a36Sopenharmony_ci#include <sys/types.h> 2662306a36Sopenharmony_ci#include <sys/stat.h> 2762306a36Sopenharmony_ci#include <fcntl.h> 2862306a36Sopenharmony_ci#include <unistd.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "proc.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define PF_KHTREAD 0x00200000 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Test for kernel threadness atomically with openat(). 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Return /proc/$PID/fd descriptor if process is kernel thread. 3862306a36Sopenharmony_ci * Return -1 if a process is userspace process. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic int kernel_thread_fd(unsigned int pid) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci unsigned int flags = 0; 4362306a36Sopenharmony_ci char buf[4096]; 4462306a36Sopenharmony_ci int dir_fd, fd; 4562306a36Sopenharmony_ci ssize_t rv; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "/proc/%u", pid); 4862306a36Sopenharmony_ci dir_fd = open(buf, O_RDONLY|O_DIRECTORY); 4962306a36Sopenharmony_ci if (dir_fd == -1) 5062306a36Sopenharmony_ci return -1; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* 5362306a36Sopenharmony_ci * Believe it or not, struct task_struct::flags is directly exposed 5462306a36Sopenharmony_ci * to userspace! 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci fd = openat(dir_fd, "stat", O_RDONLY); 5762306a36Sopenharmony_ci if (fd == -1) { 5862306a36Sopenharmony_ci close(dir_fd); 5962306a36Sopenharmony_ci return -1; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci rv = read(fd, buf, sizeof(buf)); 6262306a36Sopenharmony_ci close(fd); 6362306a36Sopenharmony_ci if (0 < rv && rv <= sizeof(buf)) { 6462306a36Sopenharmony_ci unsigned long long flags_ull; 6562306a36Sopenharmony_ci char *p, *end; 6662306a36Sopenharmony_ci int i; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci assert(buf[rv - 1] == '\n'); 6962306a36Sopenharmony_ci buf[rv - 1] = '\0'; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Search backwards: ->comm can contain whitespace and ')'. */ 7262306a36Sopenharmony_ci for (i = 0; i < 43; i++) { 7362306a36Sopenharmony_ci p = strrchr(buf, ' '); 7462306a36Sopenharmony_ci assert(p); 7562306a36Sopenharmony_ci *p = '\0'; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci p = strrchr(buf, ' '); 7962306a36Sopenharmony_ci assert(p); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci flags_ull = xstrtoull(p + 1, &end); 8262306a36Sopenharmony_ci assert(*end == '\0'); 8362306a36Sopenharmony_ci assert(flags_ull == (unsigned int)flags_ull); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci flags = flags_ull; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci fd = -1; 8962306a36Sopenharmony_ci if (flags & PF_KHTREAD) { 9062306a36Sopenharmony_ci fd = openat(dir_fd, "fd", O_RDONLY|O_DIRECTORY); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci close(dir_fd); 9362306a36Sopenharmony_ci return fd; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void test_readdir(int fd) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci DIR *d; 9962306a36Sopenharmony_ci struct dirent *de; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci d = fdopendir(fd); 10262306a36Sopenharmony_ci assert(d); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci de = xreaddir(d); 10562306a36Sopenharmony_ci assert(streq(de->d_name, ".")); 10662306a36Sopenharmony_ci assert(de->d_type == DT_DIR); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci de = xreaddir(d); 10962306a36Sopenharmony_ci assert(streq(de->d_name, "..")); 11062306a36Sopenharmony_ci assert(de->d_type == DT_DIR); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci de = xreaddir(d); 11362306a36Sopenharmony_ci assert(!de); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic inline int sys_statx(int dirfd, const char *pathname, int flags, 11762306a36Sopenharmony_ci unsigned int mask, void *stx) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return syscall(SYS_statx, dirfd, pathname, flags, mask, stx); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void test_lookup_fail(int fd, const char *pathname) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci char stx[256] __attribute__((aligned(8))); 12562306a36Sopenharmony_ci int rv; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci rv = sys_statx(fd, pathname, AT_SYMLINK_NOFOLLOW, 0, (void *)stx); 12862306a36Sopenharmony_ci assert(rv == -1 && errno == ENOENT); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void test_lookup(int fd) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci char buf[64]; 13462306a36Sopenharmony_ci unsigned int u; 13562306a36Sopenharmony_ci int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = INT_MIN; i < INT_MIN + 1024; i++) { 13862306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "%d", i); 13962306a36Sopenharmony_ci test_lookup_fail(fd, buf); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci for (i = -1024; i < 1024; i++) { 14262306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "%d", i); 14362306a36Sopenharmony_ci test_lookup_fail(fd, buf); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci for (u = INT_MAX - 1024; u < (unsigned int)INT_MAX + 1024; u++) { 14662306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "%u", u); 14762306a36Sopenharmony_ci test_lookup_fail(fd, buf); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci for (u = UINT_MAX - 1024; u != 0; u++) { 15062306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "%u", u); 15162306a36Sopenharmony_ci test_lookup_fail(fd, buf); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint main(void) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned int pid; 15862306a36Sopenharmony_ci int fd; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * In theory this will loop indefinitely if kernel threads are exiled 16262306a36Sopenharmony_ci * from /proc. 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * Start with kthreadd. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci pid = 2; 16762306a36Sopenharmony_ci while ((fd = kernel_thread_fd(pid)) == -1 && pid < 1024) { 16862306a36Sopenharmony_ci pid++; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci /* EACCES if run as non-root. */ 17162306a36Sopenharmony_ci if (pid >= 1024) 17262306a36Sopenharmony_ci return 1; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci test_readdir(fd); 17562306a36Sopenharmony_ci test_lookup(fd); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 179