1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static const stress_help_t help[] = {
28 	{ NULL,	"landlock N",	  "start N workers stressing landlock file operations" },
29 	{ NULL,	"landlock-ops N", "stop after N landlock bogo operations" },
30 	{ NULL,	NULL,		  NULL }
31 };
32 #define SHIM_LANDLOCK_CREATE_RULESET_VERSION	(1U << 0)
33 
34 #define SHIM_LANDLOCK_ACCESS_FS_EXECUTE		(1ULL << 0)
35 #define SHIM_LANDLOCK_ACCESS_FS_WRITE_FILE	(1ULL << 1)
36 #define SHIM_LANDLOCK_ACCESS_FS_READ_FILE	(1ULL << 2)
37 #define SHIM_LANDLOCK_ACCESS_FS_READ_DIR	(1ULL << 3)
38 #define SHIM_LANDLOCK_ACCESS_FS_REMOVE_DIR	(1ULL << 4)
39 #define SHIM_LANDLOCK_ACCESS_FS_REMOVE_FILE	(1ULL << 5)
40 #define SHIM_LANDLOCK_ACCESS_FS_MAKE_CHAR	(1ULL << 6)
41 #define SHIM_LANDLOCK_ACCESS_FS_MAKE_DIR	(1ULL << 7)
42 #define SHIM_LANDLOCK_ACCESS_FS_MAKE_REG	(1ULL << 8)
43 #define SHIM_LANDLOCK_ACCESS_FS_MAKE_SOCK	(1ULL << 9)
44 #define SHIM_LANDLOCK_ACCESS_FS_MAKE_FIFO	(1ULL << 10)
45 #define SHIM_LANDLOCK_ACCESS_FS_MAKE_BLOCK	(1ULL << 11)
46 #define SHIM_LANDLOCK_ACCESS_FS_MAKE_SYM	(1ULL << 12)
47 
48 #if defined(HAVE_LINUX_LANDLOCK_H) &&		\
49     defined(HAVE_LANDLOCK_RULE_TYPE) &&		\
50     defined(HAVE_LANDLOCK_RULESET_ATTR) &&	\
51     defined(__NR_landlock_create_ruleset) &&	\
52     defined(__NR_landlock_restrict_self) &&	\
53     defined(__NR_landlock_add_rule)
54 
55 typedef int (*stress_landlock_func)(const stress_args_t *args, void *ctxt);
56 
shim_landlock_create_ruleset(struct landlock_ruleset_attr * attr,size_t size,uint32_t flags)57 static int shim_landlock_create_ruleset(
58 	struct landlock_ruleset_attr *attr,
59 	size_t size,
60 	uint32_t flags)
61 {
62 #if defined(__NR_landlock_create_ruleset)
63 	return (int)syscall(__NR_landlock_create_ruleset, attr, size, flags);
64 #else
65 	(void)attr;
66 	(void)size;
67 	(void)flags;
68 
69 	errno = ENOSYS;
70 	return -1;
71 #endif
72 }
73 
shim_landlock_restrict_self(const int fd,const uint32_t flags)74 static int shim_landlock_restrict_self(const int fd, const uint32_t flags)
75 {
76 #if defined(__NR_landlock_restrict_self)
77 	return (int)syscall(__NR_landlock_restrict_self, fd, flags);
78 #else
79 	(void)fd;
80 	(void)flags;
81 
82 	errno = ENOSYS;
83 	return -1;
84 #endif
85 }
86 
shim_landlock_add_rule(const int fd,const enum landlock_rule_type type,const void * const rule_attr,const uint32_t flags)87 static int shim_landlock_add_rule(
88 	const int fd,
89 	const enum landlock_rule_type type,
90 	const void *const rule_attr,
91 	const uint32_t flags)
92 {
93 #if defined(__NR_landlock_add_rule)
94 	return (int)syscall(__NR_landlock_add_rule, fd, type, rule_attr, flags);
95 #else
96 	(void)fd;
97 	(void)type;
98 	(void)rule_attr;
99 	(void)flags;
100 
101 	errno = ENOSYS;
102 	return -1;
103 #endif
104 }
105 
stress_landlock_supported(const char * name)106 static int stress_landlock_supported(const char *name)
107 {
108 	int ruleset_fd;
109 	struct landlock_ruleset_attr ruleset_attr;
110 
111 	(void)memset(&ruleset_attr, 0, sizeof(ruleset_attr));
112 	ruleset_attr.handled_access_fs = SHIM_LANDLOCK_ACCESS_FS_READ_FILE;
113 
114 	ruleset_fd = shim_landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
115 	if (ruleset_fd < 0) {
116 		if (errno == ENOSYS) {
117 			pr_inf_skip("%s: stressor will be skipped, landlock_create_ruleset system call"
118 				"is not supported\n", name);
119 		} else {
120 			pr_inf_skip("%s: stressor will be skipped, perhaps "
121 				"lsm=landlock is not enabled\n", name);
122 		}
123 		return -1;
124 	}
125 
126 	(void)close(ruleset_fd);
127 	return 0;
128 }
129 
stress_landlock_flag(const stress_args_t * args,void * ctxt)130 static int stress_landlock_flag(const stress_args_t *args, void *ctxt)
131 {
132 	uint32_t flag = *(uint32_t *)ctxt;
133 	const char *path = stress_get_temp_path();
134 	char filename[PATH_MAX];
135 
136 	int ruleset_fd, fd, ret, rc = EXIT_SUCCESS;
137 	const pid_t pid = getpid();
138 	struct landlock_ruleset_attr ruleset_attr;
139 	struct landlock_path_beneath_attr path_beneath = {
140 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
141 				  LANDLOCK_ACCESS_FS_READ_DIR,
142 	};
143 	struct landlock_path_beneath_attr bad_path_beneath = {
144 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
145 				  LANDLOCK_ACCESS_FS_READ_DIR,
146 	};
147 
148 	if (!path)
149 		return 0;
150 
151 	(void)memset(&ruleset_attr, 0, sizeof(ruleset_attr));
152 	/* Exercise illegal ruleset sizes, EINVAL */
153 	ret = shim_landlock_create_ruleset(&ruleset_attr, 0, 0);
154 	(void)ret;
155 	/* Exercise illegal ruleset sizes, E2BIG */
156 	ret = shim_landlock_create_ruleset(&ruleset_attr, 4096, 0);
157 	(void)ret;
158 	/* Exercise fetch of ruleset API version, ignore return */
159 	ret = shim_landlock_create_ruleset(NULL, 0, SHIM_LANDLOCK_CREATE_RULESET_VERSION);
160 	(void)ret;
161 
162 	(void)memset(&ruleset_attr, 0, sizeof(ruleset_attr));
163 	ruleset_attr.handled_access_fs = flag;
164 
165 	ruleset_fd = shim_landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
166 	if (ruleset_fd < 0)
167 		return 0;
168 
169 	/* Exercise illegal parent_fd */
170 	path_beneath.parent_fd = -1;
171 	ret = shim_landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
172 		&path_beneath, 0);
173 	(void)ret;
174 
175 	path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);
176 	if (path_beneath.parent_fd < 0)
177 		goto close_ruleset;
178 
179 	/* Exercise illegal fd */
180 	ret = shim_landlock_add_rule(-1, LANDLOCK_RULE_PATH_BENEATH,
181 		&path_beneath, 0);
182 	(void)ret;
183 
184 	/* Exercise illegal flags */
185 	ret = shim_landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
186 		&path_beneath, ~0);
187 	(void)ret;
188 	/* Exercise illegal rule type */
189 	ret = shim_landlock_add_rule(ruleset_fd, ~0,
190 		&path_beneath, 0);
191 	(void)ret;
192 
193 	ret = shim_landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
194 		&path_beneath, 0);
195 	if (ret < 0)
196 		goto close_parent;
197 
198 	/* Exercise illegal parent_fd */
199 	bad_path_beneath.parent_fd = ruleset_fd;
200 	ret = shim_landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
201 		&bad_path_beneath, 0);
202 	(void)ret;
203 
204 	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
205 	if (ret < 0)
206 		goto close_parent;
207 
208 	ret = shim_landlock_restrict_self(ruleset_fd, 0);
209 	if (ret < 0)
210 		goto close_parent;
211 
212 	/*
213 	 *  Got a valid landlocked restricted child process,
214 	 *  so now sanity check it on some test files
215 	 */
216 	(void)snprintf(filename, sizeof(filename), "%s/landlock-%d", path, (int)pid);
217 
218 	fd = open(path, O_PATH | O_CLOEXEC);
219 	if (fd > -1)
220 		(void)close(fd);
221 
222 	fd = open(filename, O_CREAT | O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR);
223 	if (fd > -1) {
224 		pr_fail("%s: failed to landlock writable file %s\n",
225 			args->name, filename);
226 		(void)unlink(filename);
227 		(void)close(fd);
228 		rc = EXIT_FAILURE;
229 		goto close_parent;
230 	}
231 	if ((fd < 0) && (errno != EACCES)) {
232 		pr_fail("%s: landlocked file create should have returned "
233 			"errno=%d (%s), got errno=%d (%s) instead\n",
234 			args->name,
235 			EACCES, strerror(EACCES),
236 			errno, strerror(errno));
237 		rc = EXIT_FAILURE;
238 		goto close_parent;
239 	}
240 
241 close_parent:
242 	(void)close(path_beneath.parent_fd);
243 close_ruleset:
244 	(void)close(ruleset_fd);
245 
246 	return rc;
247 }
248 
stress_landlock_test(const stress_args_t * args,stress_landlock_func func,void * ctxt,int * failures)249 static void stress_landlock_test(
250 	const stress_args_t *args,
251 	stress_landlock_func func,
252 	void *ctxt,
253 	int *failures)
254 {
255 	int status;
256 	pid_t pid;
257 
258 again:
259 	pid = fork();
260 	if (pid < 0) {
261 		if (stress_redo_fork(errno))
262 			goto again;
263 		return;
264 	} else if (pid == 0) {
265 		_exit(func(args, ctxt));
266 	} else {
267 		if (shim_waitpid(pid, &status, 0) < 0) {
268 			if (errno != EINTR) {
269 				pr_err("%s: waitpid errno=%d (%s)\n",
270 					args->name, errno, strerror(errno));
271 			} else {
272 				/* Probably an SIGARLM, force reap */
273 				(void)kill(pid, SIGKILL);
274 				(void)shim_waitpid(pid, &status, 0);
275 				return;
276 			}
277 		}
278 		if (WIFEXITED(status)) {
279 			int rc = WEXITSTATUS(status);
280 
281 			if (rc != EXIT_SUCCESS)
282 				(*failures)++;
283 			return;
284 		}
285 	}
286 }
287 
288 /*
289  *  stress_landlock()
290  *	stress landlock API
291  */
stress_landlock(const stress_args_t * args)292 static int stress_landlock(const stress_args_t *args)
293 {
294 	static const int landlock_access_flags[] = {
295 		SHIM_LANDLOCK_ACCESS_FS_EXECUTE,
296 		SHIM_LANDLOCK_ACCESS_FS_WRITE_FILE,
297 		SHIM_LANDLOCK_ACCESS_FS_READ_FILE,
298 		SHIM_LANDLOCK_ACCESS_FS_READ_DIR,
299 		SHIM_LANDLOCK_ACCESS_FS_REMOVE_DIR,
300 		SHIM_LANDLOCK_ACCESS_FS_REMOVE_FILE,
301 		SHIM_LANDLOCK_ACCESS_FS_MAKE_CHAR,
302 		SHIM_LANDLOCK_ACCESS_FS_MAKE_DIR,
303 		SHIM_LANDLOCK_ACCESS_FS_MAKE_REG,
304 		SHIM_LANDLOCK_ACCESS_FS_MAKE_SOCK,
305 		SHIM_LANDLOCK_ACCESS_FS_MAKE_FIFO,
306 		SHIM_LANDLOCK_ACCESS_FS_MAKE_BLOCK,
307 		SHIM_LANDLOCK_ACCESS_FS_MAKE_SYM,
308 		0,
309 	};
310 	int failures = 0;
311 
312 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
313 
314 	do {
315 		size_t i;
316 		uint32_t flags = 0;
317 
318 		/* Exercise with a mix of valid and invalid flags */
319 		for (i = 0; i < SIZEOF_ARRAY(landlock_access_flags); i++) {
320 			uint32_t flag = landlock_access_flags[i];
321 
322 			flags |= flag;
323 			stress_landlock_test(args, stress_landlock_flag, &flag, &failures);
324 			if (failures >= 5)
325 				goto err;
326 		}
327 		stress_landlock_test(args, stress_landlock_flag, &flags, &failures);
328 		if (failures >= 5)
329 			goto err;
330 		flags = ~flags;
331 		stress_landlock_test(args, stress_landlock_flag, &flags, &failures);
332 		if (failures >= 5)
333 			goto err;
334 
335 		inc_counter(args);
336 	} while (keep_stressing(args));
337 
338 err:
339 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
340 
341 	return EXIT_SUCCESS;
342 }
343 
344 stressor_info_t stress_landlock_info = {
345 	.stressor = stress_landlock,
346 	.class = CLASS_OS,
347 	.supported = stress_landlock_supported,
348 	.help = help
349 };
350 #else
351 stressor_info_t stress_landlock_info = {
352 	.stressor = stress_not_implemented,
353 	.class = CLASS_OS,
354 	.help = help
355 };
356 #endif
357