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