1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved. 4f08c3bdfSopenharmony_ci * Copyright (c) 2021 CTERA Networks. All Rights Reserved. 5f08c3bdfSopenharmony_ci * 6f08c3bdfSopenharmony_ci * User ns support by: Xiao Yang <yangx.jy@cn.fujitsu.com> 7f08c3bdfSopenharmony_ci * Forked from getxattr05.c by Amir Goldstein <amir73il@gmail.com> 8f08c3bdfSopenharmony_ci */ 9f08c3bdfSopenharmony_ci 10f08c3bdfSopenharmony_ci/*\ 11f08c3bdfSopenharmony_ci * [Description] 12f08c3bdfSopenharmony_ci * Check that fanotify groups and marks limits are enforced correctly. 13f08c3bdfSopenharmony_ci * If user ns is supported, verify that global limit and per user ns 14f08c3bdfSopenharmony_ci * limits are both enforced. 15f08c3bdfSopenharmony_ci * Otherwise, we only check that global groups limit is enforced. 16f08c3bdfSopenharmony_ci */ 17f08c3bdfSopenharmony_ci 18f08c3bdfSopenharmony_ci#define _GNU_SOURCE 19f08c3bdfSopenharmony_ci#include "config.h" 20f08c3bdfSopenharmony_ci#include <errno.h> 21f08c3bdfSopenharmony_ci#include <unistd.h> 22f08c3bdfSopenharmony_ci#include <sys/types.h> 23f08c3bdfSopenharmony_ci#include <stdlib.h> 24f08c3bdfSopenharmony_ci 25f08c3bdfSopenharmony_ci#include "tst_test.h" 26f08c3bdfSopenharmony_ci#include "lapi/sched.h" 27f08c3bdfSopenharmony_ci 28f08c3bdfSopenharmony_ci#ifdef HAVE_SYS_FANOTIFY_H 29f08c3bdfSopenharmony_ci#include "fanotify.h" 30f08c3bdfSopenharmony_ci 31f08c3bdfSopenharmony_ci#define MOUNT_PATH "fs_mnt" 32f08c3bdfSopenharmony_ci#define TEST_FILE MOUNT_PATH "/testfile" 33f08c3bdfSopenharmony_ci#define SELF_USERNS "/proc/self/ns/user" 34f08c3bdfSopenharmony_ci#define MAX_USERNS "/proc/sys/user/max_user_namespaces" 35f08c3bdfSopenharmony_ci#define UID_MAP "/proc/self/uid_map" 36f08c3bdfSopenharmony_ci 37f08c3bdfSopenharmony_ci#define GLOBAL_MAX_GROUPS "/proc/sys/fs/fanotify/max_user_groups" 38f08c3bdfSopenharmony_ci#define GLOBAL_MAX_MARKS "/proc/sys/fs/fanotify/max_user_marks" 39f08c3bdfSopenharmony_ci#define USERNS_MAX_GROUPS "/proc/sys/user/max_fanotify_groups" 40f08c3bdfSopenharmony_ci#define USERNS_MAX_MARKS "/proc/sys/user/max_fanotify_marks" 41f08c3bdfSopenharmony_ci 42f08c3bdfSopenharmony_ci/* 43f08c3bdfSopenharmony_ci * In older kernels those limits were fixed in kernel. 44f08c3bdfSopenharmony_ci * The fanotify_init() man page documents the max groups limit is 128, but the 45f08c3bdfSopenharmony_ci * implementation actually allows one extra group. 46f08c3bdfSopenharmony_ci */ 47f08c3bdfSopenharmony_ci#define DEFAULT_MAX_GROUPS 129 48f08c3bdfSopenharmony_ci#define DEFAULT_MAX_MARKS 8192 49f08c3bdfSopenharmony_ci 50f08c3bdfSopenharmony_cistatic int orig_max_userns = -1; 51f08c3bdfSopenharmony_cistatic int user_ns_supported = 1; 52f08c3bdfSopenharmony_cistatic int max_groups = DEFAULT_MAX_GROUPS; 53f08c3bdfSopenharmony_cistatic int max_marks = DEFAULT_MAX_MARKS; 54f08c3bdfSopenharmony_ci 55f08c3bdfSopenharmony_cistatic struct tcase { 56f08c3bdfSopenharmony_ci const char *tname; 57f08c3bdfSopenharmony_ci unsigned int init_flags; 58f08c3bdfSopenharmony_ci /* 0: without userns, 1: with userns */ 59f08c3bdfSopenharmony_ci int set_userns; 60f08c3bdfSopenharmony_ci /* 0: don't map root UID in userns, 1: map root UID in userns */ 61f08c3bdfSopenharmony_ci int map_root; 62f08c3bdfSopenharmony_ci /* 0: unlimited groups in userns */ 63f08c3bdfSopenharmony_ci int max_user_groups; 64f08c3bdfSopenharmony_ci /* 0: unlimited marks in userns */ 65f08c3bdfSopenharmony_ci int max_user_marks; 66f08c3bdfSopenharmony_ci} tcases[] = { 67f08c3bdfSopenharmony_ci { 68f08c3bdfSopenharmony_ci "Global groups limit in init user ns", 69f08c3bdfSopenharmony_ci FAN_CLASS_NOTIF, 70f08c3bdfSopenharmony_ci 0, 0, 0, 0, 71f08c3bdfSopenharmony_ci }, 72f08c3bdfSopenharmony_ci { 73f08c3bdfSopenharmony_ci "Global groups limit in privileged user ns", 74f08c3bdfSopenharmony_ci FANOTIFY_REQUIRED_USER_INIT_FLAGS, 75f08c3bdfSopenharmony_ci 1, 1, 0, 0, 76f08c3bdfSopenharmony_ci }, 77f08c3bdfSopenharmony_ci { 78f08c3bdfSopenharmony_ci "Local groups limit in unprivileged user ns", 79f08c3bdfSopenharmony_ci FANOTIFY_REQUIRED_USER_INIT_FLAGS, 80f08c3bdfSopenharmony_ci 1, 0, 10, 0, 81f08c3bdfSopenharmony_ci }, 82f08c3bdfSopenharmony_ci { 83f08c3bdfSopenharmony_ci "Local marks limit in unprivileged user ns", 84f08c3bdfSopenharmony_ci FANOTIFY_REQUIRED_USER_INIT_FLAGS, 85f08c3bdfSopenharmony_ci 1, 0, 0, 10, 86f08c3bdfSopenharmony_ci }, 87f08c3bdfSopenharmony_ci}; 88f08c3bdfSopenharmony_ci 89f08c3bdfSopenharmony_ci/* Verify that groups and marks cannot be created beyond limit */ 90f08c3bdfSopenharmony_cistatic void verify_user_limits(unsigned int init_flags, int groups, int marks) 91f08c3bdfSopenharmony_ci{ 92f08c3bdfSopenharmony_ci int i, fd = 0, ret = 0; 93f08c3bdfSopenharmony_ci 94f08c3bdfSopenharmony_ci for (i = 0; i <= groups; i++) { 95f08c3bdfSopenharmony_ci fd = fanotify_init(init_flags, O_RDONLY); 96f08c3bdfSopenharmony_ci /* 97f08c3bdfSopenharmony_ci * Don't bother closing fd's, the child process will exit 98f08c3bdfSopenharmony_ci * and all fd's will be closed. 99f08c3bdfSopenharmony_ci */ 100f08c3bdfSopenharmony_ci if (fd < 0) 101f08c3bdfSopenharmony_ci break; 102f08c3bdfSopenharmony_ci 103f08c3bdfSopenharmony_ci ret = fanotify_mark(fd, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, 104f08c3bdfSopenharmony_ci TEST_FILE); 105f08c3bdfSopenharmony_ci if (ret < 0) 106f08c3bdfSopenharmony_ci break; 107f08c3bdfSopenharmony_ci 108f08c3bdfSopenharmony_ci } 109f08c3bdfSopenharmony_ci if (fd > 0 && i > groups) { 110f08c3bdfSopenharmony_ci tst_res(TFAIL, 111f08c3bdfSopenharmony_ci "Created %d groups and marks - " 112f08c3bdfSopenharmony_ci "groups limit (%d) exceeded", i, groups); 113f08c3bdfSopenharmony_ci } else if (!ret && i > marks) { 114f08c3bdfSopenharmony_ci tst_res(TFAIL, 115f08c3bdfSopenharmony_ci "Created %d groups and marks - " 116f08c3bdfSopenharmony_ci "marks limit (%d) exceeded", i, marks); 117f08c3bdfSopenharmony_ci } else if (ret < 0 && errno == ENOSPC && marks < groups) { 118f08c3bdfSopenharmony_ci /* 119f08c3bdfSopenharmony_ci * ENOSPC is to be returned to the calling process when 120f08c3bdfSopenharmony_ci * fanotify marks limit is reached. 121f08c3bdfSopenharmony_ci */ 122f08c3bdfSopenharmony_ci tst_res(TPASS, 123f08c3bdfSopenharmony_ci "Created %d marks - " 124f08c3bdfSopenharmony_ci "below marks limit (%d)", i, marks); 125f08c3bdfSopenharmony_ci } else if (fd < 0 && errno == EMFILE) { 126f08c3bdfSopenharmony_ci /* 127f08c3bdfSopenharmony_ci * EMFILE is to be returned to the calling process when 128f08c3bdfSopenharmony_ci * fanotify groups limit is reached. 129f08c3bdfSopenharmony_ci */ 130f08c3bdfSopenharmony_ci tst_res(TPASS, 131f08c3bdfSopenharmony_ci "Created %d groups - " 132f08c3bdfSopenharmony_ci "below groups limit (%d)", i, groups); 133f08c3bdfSopenharmony_ci } else if (errno == EPERM) { 134f08c3bdfSopenharmony_ci tst_res(TCONF, 135f08c3bdfSopenharmony_ci "unprivileged fanotify not supported by kernel?"); 136f08c3bdfSopenharmony_ci } else if (fd < 0) { 137f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, 138f08c3bdfSopenharmony_ci "fd=%d, fanotify_init(%x, O_RDONLY) failed", 139f08c3bdfSopenharmony_ci fd, init_flags); 140f08c3bdfSopenharmony_ci } else if (ret < 0) { 141f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, 142f08c3bdfSopenharmony_ci "ret=%d, fanotify_mark(%d, FAN_MARK_ADD, FAN_OPEN, " 143f08c3bdfSopenharmony_ci "AT_FDCWD, '" TEST_FILE "') failed", ret, fd); 144f08c3bdfSopenharmony_ci } 145f08c3bdfSopenharmony_ci} 146f08c3bdfSopenharmony_ci 147f08c3bdfSopenharmony_cistatic void do_unshare(int map_root) 148f08c3bdfSopenharmony_ci{ 149f08c3bdfSopenharmony_ci int res; 150f08c3bdfSopenharmony_ci 151f08c3bdfSopenharmony_ci /* unshare() should support CLONE_NEWUSER flag since Linux 3.8 */ 152f08c3bdfSopenharmony_ci res = unshare(CLONE_NEWUSER); 153f08c3bdfSopenharmony_ci if (res == -1) 154f08c3bdfSopenharmony_ci tst_brk(TFAIL | TERRNO, "unshare(CLONE_NEWUSER) failed"); 155f08c3bdfSopenharmony_ci 156f08c3bdfSopenharmony_ci if (map_root) { 157f08c3bdfSopenharmony_ci /* 158f08c3bdfSopenharmony_ci * uid_map file should exist since Linux 3.8 because 159f08c3bdfSopenharmony_ci * it is available on Linux 3.5 160f08c3bdfSopenharmony_ci */ 161f08c3bdfSopenharmony_ci SAFE_ACCESS(UID_MAP, F_OK); 162f08c3bdfSopenharmony_ci 163f08c3bdfSopenharmony_ci SAFE_FILE_PRINTF(UID_MAP, "%d %d %d", 0, 0, 1); 164f08c3bdfSopenharmony_ci } 165f08c3bdfSopenharmony_ci} 166f08c3bdfSopenharmony_ci 167f08c3bdfSopenharmony_cistatic void test_fanotify(unsigned int n) 168f08c3bdfSopenharmony_ci{ 169f08c3bdfSopenharmony_ci struct tcase *tc = &tcases[n]; 170f08c3bdfSopenharmony_ci int groups = max_groups; 171f08c3bdfSopenharmony_ci int marks = max_marks; 172f08c3bdfSopenharmony_ci pid_t pid; 173f08c3bdfSopenharmony_ci 174f08c3bdfSopenharmony_ci tst_res(TINFO, "Test #%d: %s", n, tc->tname); 175f08c3bdfSopenharmony_ci 176f08c3bdfSopenharmony_ci if (tc->set_userns && !user_ns_supported) { 177f08c3bdfSopenharmony_ci tst_res(TCONF, "fanotify inside user namespace is not supported"); 178f08c3bdfSopenharmony_ci return; 179f08c3bdfSopenharmony_ci } 180f08c3bdfSopenharmony_ci 181f08c3bdfSopenharmony_ci pid = SAFE_FORK(); 182f08c3bdfSopenharmony_ci if (!pid) { 183f08c3bdfSopenharmony_ci if (tc->set_userns) { 184f08c3bdfSopenharmony_ci do_unshare(tc->map_root); 185f08c3bdfSopenharmony_ci /* Not changing global limits, only per userns limits */ 186f08c3bdfSopenharmony_ci if (tc->max_user_groups && tc->max_user_groups < groups) { 187f08c3bdfSopenharmony_ci /* Further limit user ns groups */ 188f08c3bdfSopenharmony_ci marks = groups = tc->max_user_groups; 189f08c3bdfSopenharmony_ci SAFE_FILE_PRINTF(USERNS_MAX_GROUPS, "%d", groups); 190f08c3bdfSopenharmony_ci } 191f08c3bdfSopenharmony_ci if (tc->max_user_marks && tc->max_user_marks < marks) { 192f08c3bdfSopenharmony_ci /* Further limit user ns marks */ 193f08c3bdfSopenharmony_ci marks = tc->max_user_marks; 194f08c3bdfSopenharmony_ci SAFE_FILE_PRINTF(USERNS_MAX_MARKS, "%d", marks); 195f08c3bdfSopenharmony_ci } 196f08c3bdfSopenharmony_ci } 197f08c3bdfSopenharmony_ci verify_user_limits(tc->init_flags, groups, marks); 198f08c3bdfSopenharmony_ci exit(0); 199f08c3bdfSopenharmony_ci } 200f08c3bdfSopenharmony_ci 201f08c3bdfSopenharmony_ci tst_reap_children(); 202f08c3bdfSopenharmony_ci} 203f08c3bdfSopenharmony_ci 204f08c3bdfSopenharmony_cistatic void setup_rlimit(unsigned int max_files) 205f08c3bdfSopenharmony_ci{ 206f08c3bdfSopenharmony_ci struct rlimit rlim; 207f08c3bdfSopenharmony_ci 208f08c3bdfSopenharmony_ci SAFE_GETRLIMIT(RLIMIT_NOFILE, &rlim); 209f08c3bdfSopenharmony_ci rlim.rlim_cur = max_files; 210f08c3bdfSopenharmony_ci SAFE_SETRLIMIT(RLIMIT_NOFILE, &rlim); 211f08c3bdfSopenharmony_ci} 212f08c3bdfSopenharmony_ci 213f08c3bdfSopenharmony_cistatic void setup(void) 214f08c3bdfSopenharmony_ci{ 215f08c3bdfSopenharmony_ci SAFE_TOUCH(TEST_FILE, 0666, NULL); 216f08c3bdfSopenharmony_ci /* Check for kernel fanotify support */ 217f08c3bdfSopenharmony_ci REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_FILE); 218f08c3bdfSopenharmony_ci 219f08c3bdfSopenharmony_ci /* 220f08c3bdfSopenharmony_ci * The default value of max_user_namespaces is set to 0 on some distros, 221f08c3bdfSopenharmony_ci * We need to change the default value to call unshare(). 222f08c3bdfSopenharmony_ci */ 223f08c3bdfSopenharmony_ci if (access(SELF_USERNS, F_OK) != 0) { 224f08c3bdfSopenharmony_ci user_ns_supported = 0; 225f08c3bdfSopenharmony_ci } else if (!access(MAX_USERNS, F_OK)) { 226f08c3bdfSopenharmony_ci SAFE_FILE_SCANF(MAX_USERNS, "%d", &orig_max_userns); 227f08c3bdfSopenharmony_ci SAFE_FILE_PRINTF(MAX_USERNS, "%d", 10); 228f08c3bdfSopenharmony_ci } 229f08c3bdfSopenharmony_ci 230f08c3bdfSopenharmony_ci /* 231f08c3bdfSopenharmony_ci * In older kernels those limits were fixed in kernel and fanotify is 232f08c3bdfSopenharmony_ci * not permitted inside user ns. 233f08c3bdfSopenharmony_ci */ 234f08c3bdfSopenharmony_ci if (access(GLOBAL_MAX_GROUPS, F_OK) && errno == ENOENT) { 235f08c3bdfSopenharmony_ci user_ns_supported = 0; 236f08c3bdfSopenharmony_ci } else { 237f08c3bdfSopenharmony_ci SAFE_FILE_SCANF(GLOBAL_MAX_GROUPS, "%d", &max_groups); 238f08c3bdfSopenharmony_ci SAFE_FILE_SCANF(GLOBAL_MAX_MARKS, "%d", &max_marks); 239f08c3bdfSopenharmony_ci } 240f08c3bdfSopenharmony_ci tst_res(TINFO, "max_fanotify_groups=%d max_fanotify_marks=%d", 241f08c3bdfSopenharmony_ci max_groups, max_marks); 242f08c3bdfSopenharmony_ci 243f08c3bdfSopenharmony_ci /* Make sure we are not limited by nr of open files */ 244f08c3bdfSopenharmony_ci setup_rlimit(max_groups * 2); 245f08c3bdfSopenharmony_ci} 246f08c3bdfSopenharmony_ci 247f08c3bdfSopenharmony_cistatic void cleanup(void) 248f08c3bdfSopenharmony_ci{ 249f08c3bdfSopenharmony_ci if (orig_max_userns != -1) 250f08c3bdfSopenharmony_ci SAFE_FILE_PRINTF(MAX_USERNS, "%d", orig_max_userns); 251f08c3bdfSopenharmony_ci} 252f08c3bdfSopenharmony_ci 253f08c3bdfSopenharmony_cistatic struct tst_test test = { 254f08c3bdfSopenharmony_ci .test = test_fanotify, 255f08c3bdfSopenharmony_ci .tcnt = ARRAY_SIZE(tcases), 256f08c3bdfSopenharmony_ci .setup = setup, 257f08c3bdfSopenharmony_ci .cleanup = cleanup, 258f08c3bdfSopenharmony_ci .needs_root = 1, 259f08c3bdfSopenharmony_ci .forks_child = 1, 260f08c3bdfSopenharmony_ci .mount_device = 1, 261f08c3bdfSopenharmony_ci .mntpoint = MOUNT_PATH, 262f08c3bdfSopenharmony_ci}; 263f08c3bdfSopenharmony_ci#else 264f08c3bdfSopenharmony_ci TST_TEST_TCONF("system doesn't have required fanotify support"); 265f08c3bdfSopenharmony_ci#endif 266