1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved. 4 * 5 * Started by Matthew Bobrowski <mbobrowski@mbobrowski.org> 6 */ 7 8/*\ 9 * [Description] 10 * This set of tests is to ensure that the unprivileged listener feature of 11 * fanotify is functioning as expected. The objective of this test file is 12 * to generate a sequence of events and ensure that the returned events 13 * contain the limited values that an unprivileged listener is expected 14 * to receive. 15 */ 16 17#define _GNU_SOURCE 18#include "config.h" 19 20#include <pwd.h> 21#include <stdio.h> 22#include <errno.h> 23#include <stdlib.h> 24#include <sys/wait.h> 25 26#include "tst_test.h" 27 28#ifdef HAVE_SYS_FANOTIFY_H 29#include "fanotify.h" 30 31#define EVENT_MAX 1024 32#define EVENT_SIZE (sizeof(struct fanotify_event_metadata)) 33#define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE) 34#define EVENT_SET_MAX 48 35 36#define BUF_SIZE 256 37 38#define MOUNT_PATH "fs_mnt" 39#define TEST_FILE MOUNT_PATH "/testfile" 40 41static uid_t euid; 42static int fd_notify; 43static char buf[BUF_SIZE]; 44static struct fanotify_event_metadata event_buf[EVENT_BUF_LEN]; 45 46static struct test_case_t { 47 const char *name; 48 unsigned int fork; 49 unsigned int elevate; 50 unsigned int event_count; 51 unsigned long long event_set[EVENT_SET_MAX]; 52} test_cases[] = { 53 { 54 "unprivileged listener - events by self", 55 0, 56 0, 57 4, 58 { 59 FAN_OPEN, 60 FAN_ACCESS, 61 FAN_MODIFY, 62 FAN_CLOSE, 63 } 64 }, 65 { 66 "unprivileged lisneter - events by child", 67 1, 68 0, 69 4, 70 { 71 FAN_OPEN, 72 FAN_ACCESS, 73 FAN_MODIFY, 74 FAN_CLOSE, 75 } 76 }, 77 { 78 "unprivileged listener, privileged reader - events by self", 79 0, 80 1, 81 4, 82 { 83 FAN_OPEN, 84 FAN_ACCESS, 85 FAN_MODIFY, 86 FAN_CLOSE, 87 } 88 }, 89 { 90 "unprivileged lisneter, privileged reader - events by child", 91 1, 92 1, 93 4, 94 { 95 FAN_OPEN, 96 FAN_ACCESS, 97 FAN_MODIFY, 98 FAN_CLOSE, 99 } 100 }, 101}; 102 103static void generate_events(void) 104{ 105 int fd; 106 107 /* FAN_OPEN */ 108 fd = SAFE_OPEN(TEST_FILE, O_RDWR); 109 110 /* FAN_ACCESS */ 111 SAFE_READ(0, fd, buf, BUF_SIZE); 112 113 /* FAN_MODIFY */ 114 SAFE_WRITE(SAFE_WRITE_ALL, fd, TEST_FILE, 1); 115 116 /* FAN_CLOSE */ 117 SAFE_CLOSE(fd); 118} 119 120static void do_fork(void) 121{ 122 int status; 123 pid_t child; 124 125 child = SAFE_FORK(); 126 127 if (child == 0) { 128 SAFE_CLOSE(fd_notify); 129 generate_events(); 130 exit(0); 131 } 132 133 SAFE_WAITPID(child, &status, 0); 134 135 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 136 tst_brk(TBROK, "Child process terminated incorrectly. Aborting"); 137} 138 139static void test_fanotify(unsigned int n) 140{ 141 int len = 0; 142 pid_t pid = getpid(); 143 unsigned int test_number = 0; 144 struct fanotify_event_metadata *event; 145 struct test_case_t *tc = &test_cases[n]; 146 struct passwd *nobody; 147 148 tst_res(TINFO, "Test #%d %s", n, tc->name); 149 150 /* Relinquish privileged user */ 151 if (euid == 0) { 152 tst_res(TINFO, "Running as privileged user, revoking"); 153 nobody = SAFE_GETPWNAM("nobody"); 154 SAFE_SETEUID(nobody->pw_uid); 155 } 156 157 /* Initialize fanotify */ 158 fd_notify = fanotify_init(FANOTIFY_REQUIRED_USER_INIT_FLAGS, O_RDONLY); 159 160 if (fd_notify < 0) { 161 if (errno == EPERM || errno == EINVAL) { 162 tst_res(TCONF, 163 "unprivileged fanotify not supported by kernel?"); 164 return; 165 } 166 167 tst_brk(TBROK | TERRNO, 168 "fanotify_init(FAN_CLASS_NOTIF, O_RDONLY) failed"); 169 } 170 171 /* Place mark on object */ 172 if (fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_ALL_EVENTS, 173 AT_FDCWD, TEST_FILE) < 0) { 174 tst_brk(TBROK | TERRNO, 175 "fanotify_mark(%d, FAN_MARK_ADD, %d, " 176 "AT_FDCWD, %s) failed", 177 fd_notify, 178 FAN_ALL_EVENTS, 179 TEST_FILE); 180 } 181 182 /* Generate events in either child or listening process */ 183 if (tc->fork) 184 do_fork(); 185 else 186 generate_events(); 187 188 /* Restore privileges */ 189 if (euid == 0 && tc->elevate) { 190 tst_res(TINFO, "Restoring privileged user"); 191 SAFE_SETEUID(0); 192 } 193 194 /* Read events from queue */ 195 len = SAFE_READ(0, fd_notify, event_buf + len, EVENT_BUF_LEN - len); 196 197 event = event_buf; 198 199 /* Iterate over and validate events against expected result set */ 200 while (FAN_EVENT_OK(event, len) && test_number < tc->event_count) { 201 if (!(event->mask & tc->event_set[test_number])) { 202 tst_res(TFAIL, 203 "Received unexpected event mask: mask=%llx " 204 "pid=%u fd=%d", 205 (unsigned long long) event->mask, 206 (unsigned int) event->pid, 207 event->fd); 208 } else if ((!tc->fork && event->pid != pid) || 209 (tc->fork && event->pid != 0)) { 210 tst_res(TFAIL, 211 "Received unexpected pid in event: " 212 "mask=%llx pid=%u (expected %u) fd=%d", 213 (unsigned long long) event->mask, 214 (unsigned int) event->pid, 215 (tc->fork ? 0 : pid), 216 event->fd); 217 } else if (event->fd != FAN_NOFD) { 218 tst_res(TFAIL, 219 "Received unexpected file descriptor: " 220 "mask=%llx pid=%u fd=%d (expected %d)", 221 (unsigned long long) event->pid, 222 (unsigned int) event->pid, 223 event->fd, 224 FAN_NOFD); 225 SAFE_CLOSE(event->fd); 226 } else { 227 tst_res(TPASS, 228 "Received event: mask=%llx, pid=%u fd=%d", 229 (unsigned long long) event->mask, 230 (unsigned int) event->pid, 231 event->fd); 232 } 233 234 /* Non-permission events can be merged into a single event. */ 235 event->mask &= ~tc->event_set[test_number]; 236 237 if (event->mask == 0) 238 event = FAN_EVENT_NEXT(event, len); 239 test_number++; 240 } 241 242 /* 243 * Determine whether there is still unprocessed events remaining in the 244 * buffer. This is to cover the basis whereby the event processing loop 245 * terminates prematurely. In that case, we need to ensure that any 246 * event file descriptor that is open is closed so that the temporary 247 * filesystem can be unmounted. 248 */ 249 if (FAN_EVENT_OK(event, len)) { 250 tst_res(TFAIL, 251 "Event processing loop exited prematurely. Did NOT " 252 "finish processing events in buffer. Cleaning up."); 253 while (FAN_EVENT_OK(event, len)) { 254 if (event->fd != FAN_NOFD) 255 SAFE_CLOSE(event->fd); 256 event = FAN_EVENT_NEXT(event, len); 257 } 258 } 259 260 SAFE_CLOSE(fd_notify); 261} 262 263static void setup(void) 264{ 265 SAFE_FILE_PRINTF(TEST_FILE, "1"); 266 SAFE_CHMOD(TEST_FILE, 0666); 267 268 /* Check for kernel fanotify support */ 269 REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_FILE); 270 271 euid = geteuid(); 272} 273 274static void cleanup(void) 275{ 276 if (fd_notify > 0) 277 SAFE_CLOSE(fd_notify); 278} 279 280static struct tst_test test = { 281 .test = test_fanotify, 282 .tcnt = ARRAY_SIZE(test_cases), 283 .setup = setup, 284 .cleanup = cleanup, 285 .forks_child = 1, 286 .needs_root = 1, 287 .mount_device = 1, 288 .mntpoint = MOUNT_PATH, 289 .tags = (const struct tst_tag[]) { 290 {"linux-git", "a8b98c808eab"}, 291 {} 292 } 293}; 294 295#else 296 TST_TEST_TCONF("system doesn't have required fanotify support"); 297#endif 298