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,	"efivar N",	"start N workers that read EFI variables" },
29 	{ NULL,	"efivar-ops N",	"stop after N EFI variable bogo read operations" },
30 	{ NULL,	NULL,		NULL }
31 };
32 
33 #if defined(__linux__)
34 
35 typedef struct {
36 	uint16_t	varname[512];
37 	uint8_t		guid[16];
38 	uint64_t	datalen;
39 	uint8_t		data[1024];
40 	uint64_t	status;
41 	uint32_t	attributes;
42 } __attribute__((packed)) stress_efi_var_t;
43 
44 static const char vars[] = "/sys/firmware/efi/vars";
45 static const char efi_vars[] = "/sys/firmware/efi/efivars";
46 static struct dirent **efi_dentries;
47 static bool *efi_ignore;
48 static int dir_count;
49 
50 static const char * const efi_sysfs_names[] = {
51 	"attributes",
52 	"data",
53 	"guid",
54 	"size"
55 };
56 
57 /*
58  *  efi_var_ignore()
59  *	check for filenames that are not efi vars
60  */
efi_var_ignore(char * d_name)61 static inline bool efi_var_ignore(char *d_name)
62 {
63 	if (*d_name == '.')
64 		return true;
65 	if (strcmp(d_name, "del_var") == 0)
66 		return true;
67 	if (strcmp(d_name, "new_var") == 0)
68 		return true;
69 	if (strstr(d_name, "MokListRT"))
70 		return true;
71 	return false;
72 }
73 
74 /*
75  *  guid_to_str()
76  *	turn efi GUID to a string
77  */
guid_to_str(const uint8_t * guid,char * guid_str,size_t guid_len)78 static inline void guid_to_str(const uint8_t *guid, char *guid_str, size_t guid_len)
79 {
80 	if (!guid_str)
81 		return;
82 
83 	if (guid_len > 36)
84 		(void)snprintf(guid_str, guid_len,
85 			"%02x%02x%02x%02x-%02x%02x-%02x%02x-"
86 			"%02x%02x-%02x%02x%02x%02x%02x%02x",
87 			guid[3], guid[2], guid[1], guid[0], guid[5], guid[4], guid[7], guid[6],
88 		guid[8], guid[9], guid[10], guid[11], guid[12], guid[13], guid[14], guid[15]);
89 	else
90 		*guid_str = '\0';
91 }
92 
93 /*
94  *  efi_get_varname()
95  *	fetch the UEFI variable name in terms of a 8 bit C string
96  */
efi_get_varname(char * dst,const size_t len,const stress_efi_var_t * var)97 static inline void efi_get_varname(char *dst, const size_t len, const stress_efi_var_t *var)
98 {
99 	register size_t i = len;
100 
101 	/*
102 	 * gcc-9 -Waddress-of-packed-member workaround, urgh, we know
103 	 * this is always going to be aligned correctly, but gcc-9 whines
104 	 * so this hack works around it for now.
105 	 */
106 	const uint8_t *src8 = (const uint8_t *)var->varname;
107 	const uint16_t *src = (const uint16_t *)src8;
108 
109 	while ((*src) && (i > 1)) {
110 		*dst++ = (char)(*(src++) & 0xff);
111 		i--;
112 	}
113 	*dst = '\0';
114 }
115 
116 /*
117  *  efi_lseek_read()
118  *	perform a lseek and a 1 char read on fd, silently ignore errors
119  */
efi_lseek_read(const int fd,const off_t offset,const int whence)120 static void efi_lseek_read(const int fd, const off_t offset, const int whence)
121 {
122 	off_t offret;
123 
124 	offret = lseek(fd, offset, whence);
125 	if (offret != (off_t)-1) {
126 		char data[1];
127 		ssize_t n;
128 
129 		n = read(fd, data, sizeof(data));
130 		(void)n;
131 	}
132 }
133 
134 /*
135  *  efi_get_data()
136  *	read data from a raw efi sysfs entry
137  */
efi_get_data(const stress_args_t * args,const char * varname,const char * field,void * buf,size_t buf_len)138 static int efi_get_data(
139 	const stress_args_t *args,
140 	const char *varname,
141 	const char *field,
142 	void *buf,
143 	size_t buf_len)
144 {
145 	int fd, rc = -1;
146 	ssize_t n;
147 	char filename[PATH_MAX];
148 	struct stat statbuf;
149 	off_t offset;
150 
151 	(void)snprintf(filename, sizeof(filename),
152 		"%s/%s/%s", vars, varname, field);
153 	if ((fd = open(filename, O_RDONLY)) < 0)
154 		return rc;
155 
156 	if (fstat(fd, &statbuf) < 0) {
157 		pr_err("%s: failed to stat %s, errno=%d (%s)\n",
158 			args->name, filename, errno, strerror(errno));
159 		goto err_vars;
160 	}
161 
162 	(void)memset(buf, 0, buf_len);
163 
164 	n = read(fd, buf, buf_len);
165 	if ((n < 0) && (errno != EIO) && (errno != EAGAIN) && (errno != EINTR)) {
166 		pr_err("%s: failed to read %s, errno=%d (%s)\n",
167 			args->name, filename, errno, strerror(errno));
168 		goto err_vars;
169 	}
170 
171 	rc = 0;
172 
173 	/*
174 	 *  And exercise the interface for some extra kernel
175 	 *  test coverage
176 	 */
177 	offset = (n > 0) ? stress_mwc32() % n : 0;
178 	efi_lseek_read(fd, offset, SEEK_SET);
179 
180 	offset = (n > 0) ? stress_mwc32() % n : 0;
181 	efi_lseek_read(fd, offset, SEEK_END);
182 
183 	efi_lseek_read(fd, 0, SEEK_SET);
184 	efi_lseek_read(fd, offset, SEEK_CUR);
185 
186 	/*
187 	 *  exercise mmap
188 	 */
189 	{
190 		const size_t len = (n > 0) ? (size_t)n : args->page_size;
191 		void *ptr;
192 
193 		ptr = mmap(NULL, len, PROT_READ | PROT_WRITE,
194 			MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
195 		if (ptr != MAP_FAILED) {
196 			stress_madvise_random(ptr, len);
197 			(void)munmap(ptr, len);
198 		}
199 	}
200 
201 #if defined(FIGETBSZ)
202 	{
203 		int ret, isz;
204 
205 		ret = ioctl(fd, FIGETBSZ, &isz);
206 		(void)ret;
207 	}
208 #endif
209 #if defined(FIONREAD)
210 	{
211 		int ret, isz;
212 
213 		ret = ioctl(fd, FIONREAD, &isz);
214 		(void)ret;
215 	}
216 #endif
217 
218 err_vars:
219 	(void)close(fd);
220 
221 	return rc;
222 }
223 
224 /*
225  *  efi_get_variable()
226  *	fetch a UEFI variable given its name.
227  */
efi_get_variable(const stress_args_t * args,const char * varname,stress_efi_var_t * var)228 static int efi_get_variable(const stress_args_t *args, const char *varname, stress_efi_var_t *var)
229 {
230 #if defined(FS_IOC_GETFLAGS) &&	\
231     defined(FS_IOC_SETFLAGS)
232 	int flags;
233 #endif
234 	int fd, ret, rc = 0;
235 	size_t i;
236 	ssize_t n;
237 	char filename[PATH_MAX];
238 	static char data[4096];
239 	struct stat statbuf;
240 
241 	if ((!varname) || (!var))
242 		return -1;
243 
244 	if (efi_get_data(args, varname, "raw_var", var, sizeof(*var)) < 0)
245 		rc = -1;
246 
247 	/* Exercise reading the efi sysfs files */
248 	for (i = 0; i < SIZEOF_ARRAY(efi_sysfs_names); i++) {
249 		(void)efi_get_data(args, varname, efi_sysfs_names[i], data, sizeof(data));
250 	}
251 
252 	(void)stress_mk_filename(filename, sizeof(filename), efi_vars, varname);
253 	if ((fd = open(filename, O_RDONLY)) < 0)
254 		return -1;
255 
256 	ret = fstat(fd, &statbuf);
257 	if (ret < 0) {
258 		pr_err("%s: failed to stat %s, errno=%d (%s)\n",
259 			args->name, filename, errno, strerror(errno));
260 		rc = -1;
261 		goto err_efi_vars;
262 	}
263 
264 	n = read(fd, data, sizeof(data));
265 	if ((n < 0) && (errno != EIO) && (errno != EAGAIN) && (errno != EINTR)) {
266 		pr_err("%s: failed to read %s, errno=%d (%s)\n",
267 			args->name, filename, errno, strerror(errno));
268 		rc = -1;
269 		goto err_efi_vars;
270 	}
271 
272 #if defined(FS_IOC_GETFLAGS) &&	\
273     defined(FS_IOC_SETFLAGS)
274 	ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
275 	if (ret < 0) {
276 		pr_err("%s: ioctl FS_IOC_GETFLAGS on %s failed, errno=%d (%s)\n",
277 			args->name, filename, errno, strerror(errno));
278 		rc = -1;
279 		goto err_efi_vars;
280 	}
281 
282 	ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
283 	if (ret < 0) {
284 		pr_err("%s: ioctl FS_IOC_SETFLAGS on %s failed, errno=%d (%s)\n",
285 			args->name, filename, errno, strerror(errno));
286 		rc = -1;
287 	}
288 #endif
289 
290 err_efi_vars:
291 	(void)close(fd);
292 
293 	return rc;
294 }
295 
296 /*
297  *  efi_vars_get()
298  *	read EFI variables
299  */
efi_vars_get(const stress_args_t * args)300 static int efi_vars_get(const stress_args_t *args)
301 {
302 	int i;
303 
304 	for (i = 0; i < dir_count; i++) {
305 		stress_efi_var_t var;
306 		char *d_name = efi_dentries[i]->d_name;
307 		int ret;
308 
309 		if (efi_ignore[i])
310 			continue;
311 
312 		if (efi_var_ignore(d_name)) {
313 			efi_ignore[i] = true;
314 			continue;
315 		}
316 
317 		ret = efi_get_variable(args, d_name, &var);
318 		if (ret < 0) {
319 			efi_ignore[i] = true;
320 			continue;
321 		}
322 
323 		if (var.attributes) {
324 			char varname[513];
325 			char guid_str[37];
326 
327 			efi_get_varname(varname, sizeof(varname), &var);
328 			guid_to_str(var.guid, guid_str, sizeof(guid_str));
329 
330 			(void)guid_str;
331 		} else {
332 			efi_ignore[i] = true;
333 		}
334 		inc_counter(args);
335 	}
336 
337 	return 0;
338 }
339 
340 /*
341  *  stress_efivar_supported()
342  *      check if we can run this as root
343  */
stress_efivar_supported(const char * name)344 static int stress_efivar_supported(const char *name)
345 {
346 	DIR *dir;
347 
348 	if (!stress_check_capability(SHIM_CAP_SYS_ADMIN)) {
349 		pr_inf_skip("%s stressor will be skipped, "
350 			"need to be running with CAP_SYS_ADMIN "
351 			"rights for this stressor\n", name);
352 		return -1;
353 	}
354 
355 	dir = opendir(efi_vars);
356 	if (!dir) {
357 		pr_inf_skip("%s stressor will be skipped, "
358 			"need to have access to EFI vars in %s\n",
359 			name, vars);
360 		return -1;
361 	}
362 	(void)closedir(dir);
363 
364 	return 0;
365 }
366 
367 /*
368  *  stress_efivar()
369  *	stress that exercises the efi variables
370  */
stress_efivar(const stress_args_t * args)371 static int stress_efivar(const stress_args_t *args)
372 {
373 	pid_t pid;
374 	size_t sz;
375 
376 	efi_dentries = NULL;
377 	dir_count = scandir(vars, &efi_dentries, NULL, alphasort);
378 	if (!efi_dentries || (dir_count <= 0)) {
379 		pr_inf("%s: cannot read EFI vars in %s\n", args->name, vars);
380 		return EXIT_SUCCESS;
381 	}
382 
383 	sz = (((size_t)dir_count * sizeof(*efi_ignore)) + args->page_size) & (args->page_size - 1);
384 	efi_ignore = mmap(NULL, sz, PROT_READ | PROT_WRITE,
385 			MAP_ANONYMOUS | MAP_SHARED, -1, 0);
386 	if (efi_ignore == MAP_FAILED) {
387 		pr_err("%s: cannot mmap shared memory: %d (%s))\n",
388 			args->name, errno, strerror(errno));
389 		return EXIT_NO_RESOURCE;
390 	}
391 
392 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
393 again:
394 	pid = fork();
395 	if (pid < 0) {
396 		if (stress_redo_fork(errno))
397 			goto again;
398 		if (!keep_stressing(args))
399 			goto finish;
400 		pr_err("%s: fork failed: errno=%d (%s)\n",
401 			args->name, errno, strerror(errno));
402 	} else if (pid > 0) {
403 		int status, ret;
404 
405 		(void)setpgid(pid, g_pgrp);
406 		/* Parent, wait for child */
407 		ret = shim_waitpid(pid, &status, 0);
408 		if (ret < 0) {
409 			if (errno != EINTR)
410 				pr_dbg("%s: waitpid(): errno=%d (%s)\n",
411 					args->name, errno, strerror(errno));
412 			(void)kill(pid, SIGTERM);
413 			(void)kill(pid, SIGKILL);
414 			(void)shim_waitpid(pid, &status, 0);
415 		} else if (WIFSIGNALED(status)) {
416 			pr_dbg("%s: child died: %s (instance %d)\n",
417 				args->name, stress_strsignal(WTERMSIG(status)),
418 				args->instance);
419 			return EXIT_FAILURE;
420 		}
421 	} else if (pid == 0) {
422 		(void)setpgid(0, g_pgrp);
423 		stress_parent_died_alarm();
424 		stress_set_oom_adjustment(args->name, true);
425 		(void)sched_settings_apply(true);
426 
427 		do {
428 			efi_vars_get(args);
429 		} while (keep_stressing(args));
430 		_exit(0);
431 	}
432 
433 finish:
434 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
435 
436 	(void)munmap(efi_ignore, sz);
437 	stress_dirent_list_free(efi_dentries, dir_count);
438 
439 	return EXIT_SUCCESS;
440 }
441 
442 stressor_info_t stress_efivar_info = {
443 	.stressor = stress_efivar,
444 	.supported = stress_efivar_supported,
445 	.class = CLASS_OS,
446 	.help = help
447 };
448 #else
stress_efivar_supported(const char * name)449 static int stress_efivar_supported(const char *name)
450 {
451 	pr_inf_skip("%s stressor will be skipped, "
452 		"it is not implemented on this platform\n", name);
453 
454 	return -1;
455 }
456 stressor_info_t stress_efivar_info = {
457 	.stressor = stress_not_implemented,
458 	.supported = stress_efivar_supported,
459 	.class = CLASS_OS,
460 	.help = help
461 };
462 #endif
463