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