1 // SPDX-License-Identifier: GPL-2.0 2 3 #define _GNU_SOURCE 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <inttypes.h> 7 #include <limits.h> 8 #include <linux/types.h> 9 #include <sched.h> 10 #include <signal.h> 11 #include <stdbool.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <syscall.h> 16 #include <sys/ioctl.h> 17 #include <sys/mount.h> 18 #include <sys/prctl.h> 19 #include <sys/wait.h> 20 #include <unistd.h> 21 22 #include "pidfd.h" 23 #include "../kselftest.h" 24 25 #ifndef PIDFS_IOCTL_MAGIC 26 #define PIDFS_IOCTL_MAGIC 0xFF 27 #endif 28 29 #ifndef PIDFD_GET_INFO 30 #define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info) 31 #define PIDFD_INFO_CGROUPID (1UL << 0) 32 33 struct pidfd_info { 34 __u64 request_mask; 35 __u64 cgroupid; 36 __u32 pid; 37 __u32 tgid; 38 __u32 ppid; 39 __u32 ruid; 40 __u32 rgid; 41 __u32 euid; 42 __u32 egid; 43 __u32 suid; 44 __u32 sgid; 45 __u32 fsuid; 46 __u32 fsgid; 47 __u32 spare0[1]; 48 }; 49 #endif 50 51 static int safe_int(const char *numstr, int *converted) 52 { 53 char *err = NULL; 54 long sli; 55 56 errno = 0; 57 sli = strtol(numstr, &err, 0); 58 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) 59 return -ERANGE; 60 61 if (errno != 0 && sli == 0) 62 return -EINVAL; 63 64 if (err == numstr || *err != '\0') 65 return -EINVAL; 66 67 if (sli > INT_MAX || sli < INT_MIN) 68 return -ERANGE; 69 70 *converted = (int)sli; 71 return 0; 72 } 73 74 static int char_left_gc(const char *buffer, size_t len) 75 { 76 size_t i; 77 78 for (i = 0; i < len; i++) { 79 if (buffer[i] == ' ' || 80 buffer[i] == '\t') 81 continue; 82 83 return i; 84 } 85 86 return 0; 87 } 88 89 static int char_right_gc(const char *buffer, size_t len) 90 { 91 int i; 92 93 for (i = len - 1; i >= 0; i--) { 94 if (buffer[i] == ' ' || 95 buffer[i] == '\t' || 96 buffer[i] == '\n' || 97 buffer[i] == '\0') 98 continue; 99 100 return i + 1; 101 } 102 103 return 0; 104 } 105 106 static char *trim_whitespace_in_place(char *buffer) 107 { 108 buffer += char_left_gc(buffer, strlen(buffer)); 109 buffer[char_right_gc(buffer, strlen(buffer))] = '\0'; 110 return buffer; 111 } 112 113 static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen) 114 { 115 int ret; 116 char path[512]; 117 FILE *f; 118 size_t n = 0; 119 pid_t result = -1; 120 char *line = NULL; 121 122 snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", pidfd); 123 124 f = fopen(path, "re"); 125 if (!f) 126 return -1; 127 128 while (getline(&line, &n, f) != -1) { 129 char *numstr; 130 131 if (strncmp(line, key, keylen)) 132 continue; 133 134 numstr = trim_whitespace_in_place(line + 4); 135 ret = safe_int(numstr, &result); 136 if (ret < 0) 137 goto out; 138 139 break; 140 } 141 142 out: 143 free(line); 144 fclose(f); 145 return result; 146 } 147 148 int main(int argc, char **argv) 149 { 150 struct pidfd_info info = { 151 .request_mask = PIDFD_INFO_CGROUPID, 152 }; 153 int pidfd = -1, ret = 1; 154 pid_t pid; 155 156 ksft_set_plan(4); 157 158 pidfd = sys_pidfd_open(-1, 0); 159 if (pidfd >= 0) { 160 ksft_print_msg( 161 "%s - succeeded to open pidfd for invalid pid -1\n", 162 strerror(errno)); 163 goto on_error; 164 } 165 ksft_test_result_pass("do not allow invalid pid test: passed\n"); 166 167 pidfd = sys_pidfd_open(getpid(), 1); 168 if (pidfd >= 0) { 169 ksft_print_msg( 170 "%s - succeeded to open pidfd with invalid flag value specified\n", 171 strerror(errno)); 172 goto on_error; 173 } 174 ksft_test_result_pass("do not allow invalid flag test: passed\n"); 175 176 pidfd = sys_pidfd_open(getpid(), 0); 177 if (pidfd < 0) { 178 ksft_print_msg("%s - failed to open pidfd\n", strerror(errno)); 179 goto on_error; 180 } 181 ksft_test_result_pass("open a new pidfd test: passed\n"); 182 183 pid = get_pid_from_fdinfo_file(pidfd, "Pid:", sizeof("Pid:") - 1); 184 ksft_print_msg("pidfd %d refers to process with pid %d\n", pidfd, pid); 185 186 if (ioctl(pidfd, PIDFD_GET_INFO, &info) < 0) { 187 ksft_print_msg("%s - failed to get info from pidfd\n", strerror(errno)); 188 goto on_error; 189 } 190 if (info.pid != pid) { 191 ksft_print_msg("pid from fdinfo file %d does not match pid from ioctl %d\n", 192 pid, info.pid); 193 goto on_error; 194 } 195 if (info.ppid != getppid()) { 196 ksft_print_msg("ppid %d does not match ppid from ioctl %d\n", 197 pid, info.pid); 198 goto on_error; 199 } 200 if (info.ruid != getuid()) { 201 ksft_print_msg("uid %d does not match uid from ioctl %d\n", 202 getuid(), info.ruid); 203 goto on_error; 204 } 205 if (info.rgid != getgid()) { 206 ksft_print_msg("gid %d does not match gid from ioctl %d\n", 207 getgid(), info.rgid); 208 goto on_error; 209 } 210 if (info.euid != geteuid()) { 211 ksft_print_msg("euid %d does not match euid from ioctl %d\n", 212 geteuid(), info.euid); 213 goto on_error; 214 } 215 if (info.egid != getegid()) { 216 ksft_print_msg("egid %d does not match egid from ioctl %d\n", 217 getegid(), info.egid); 218 goto on_error; 219 } 220 if (info.suid != geteuid()) { 221 ksft_print_msg("suid %d does not match suid from ioctl %d\n", 222 geteuid(), info.suid); 223 goto on_error; 224 } 225 if (info.sgid != getegid()) { 226 ksft_print_msg("sgid %d does not match sgid from ioctl %d\n", 227 getegid(), info.sgid); 228 goto on_error; 229 } 230 if ((info.request_mask & PIDFD_INFO_CGROUPID) && info.cgroupid == 0) { 231 ksft_print_msg("cgroupid should not be 0 when PIDFD_INFO_CGROUPID is set\n"); 232 goto on_error; 233 } 234 ksft_test_result_pass("get info from pidfd test: passed\n"); 235 236 ret = 0; 237 238 on_error: 239 if (pidfd >= 0) 240 close(pidfd); 241 242 if (ret) 243 ksft_exit_fail(); 244 ksft_exit_pass(); 245 } 246