xref: /aosp_15_r20/external/ltp/testcases/kernel/fs/proc/proc01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 /*
2  * proc01.c - Tests Linux /proc file reading.
3  *
4  * Copyright (C) 2001 Stephane Fillod <[email protected]>
5  * Copyright (c) 2008, 2009  Red Hat, Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of version 2 of the GNU General Public License as
9  * published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it would be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  *
15  * Further, this software is distributed without any warranty that it is
16  * free of the rightful claim of any third person regarding infringement
17  * or the like.  Any license provided herein, whether implied or
18  * otherwise, applies only to this software file.  Patent licenses, if
19  * any, provided herein do not apply to combinations of this program with
20  * other software, or any other product whatsoever.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  *
26  */
27 
28 #include "config.h"
29 
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <limits.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <fnmatch.h>
41 
42 #ifdef HAVE_LIBSELINUX_DEVEL
43 #include <selinux/selinux.h>
44 #endif
45 
46 #include "test.h"
47 
48 #define MAX_BUFF_SIZE 65536
49 #define MAX_FUNC_NAME 256
50 
51 char *TCID = "proc01";
52 int TST_TOTAL = 1;
53 
54 static int opt_verbose;
55 static int opt_procpath;
56 static char *opt_procpathstr;
57 static int opt_buffsize;
58 static int opt_readirq;
59 static char *opt_buffsizestr;
60 static int opt_maxmbytes;
61 static char *opt_maxmbytesstr;
62 
63 static char *procpath = "/proc";
64 static const char selfpath[] = "/proc/self";
65 size_t buffsize = 1024;
66 static unsigned long long maxbytes;
67 
68 unsigned long long total_read;
69 unsigned int total_obj;
70 
71 struct mapping {
72 	char func[MAX_FUNC_NAME];
73 	char file[PATH_MAX];
74 	int err;
75 };
76 
77 /* Those are known failures for 2.6.18 baremetal kernel and Xen dom0
78    kernel on i686, x86_64, ia64, ppc64 and s390x. In addition, It looks
79    like if SELinux is disabled, the test may still fail on some other
80    entries. */
81 static const struct mapping known_issues[] = {
82 	{"open", "/proc/acpi/event", EBUSY},
83 	{"open", "/proc/sal/cpe/data", EBUSY},
84 	{"open", "/proc/sal/cmc/data", EBUSY},
85 	{"open", "/proc/sal/init/data", EBUSY},
86 	{"open", "/proc/sal/mca/data", EBUSY},
87 	{"open", "/proc/fs/nfsd/pool_stats", ENODEV},
88 	{"read", "/proc/fs/nfsd/clients/*/ctl", EINVAL},
89 	{"read", "/proc/acpi/event", EAGAIN},
90 	{"read", "/proc/kmsg", EAGAIN},
91 	{"read", "/proc/sal/cpe/event", EAGAIN},
92 	{"read", "/proc/sal/cmc/event", EAGAIN},
93 	{"read", "/proc/sal/init/event", EAGAIN},
94 	{"read", "/proc/sal/mca/event", EAGAIN},
95 	{"read", "/proc/xen/privcmd", EIO},
96 	{"read", "/proc/xen/privcmd", EINVAL},
97 	{"read", "/proc/self/mem", EIO},
98 	{"read", "/proc/self/task/[0-9]*/mem", EIO},
99 	{"read", "/proc/self/attr/*", EINVAL},
100 	{"read", "/proc/self/attr/selinux/*", EINVAL},
101 	{"read", "/proc/self/attr/smack/*", EINVAL},
102 	{"read", "/proc/self/attr/apparmor/*", EINVAL},
103 	{"read", "/proc/self/task/[0-9]*/attr/*", EINVAL},
104 	{"read", "/proc/self/task/[0-9]*/attr/smack/*", EINVAL},
105 	{"read", "/proc/self/task/[0-9]*/attr/selinux/*", EINVAL},
106 	{"read", "/proc/self/task/[0-9]*/attr/apparmor/*", EINVAL},
107 	{"read", "/proc/self/ns/*", EINVAL},
108 	{"read", "/proc/self/task/[0-9]*/ns/*", EINVAL},
109 	{"read", "/proc/ppc64/rtas/error_log", EINVAL},
110 	{"read", "/proc/powerpc/rtas/error_log", EINVAL},
111 	{"read", "/proc/fs/nfsd/unlock_filesystem", EINVAL},
112 	{"read", "/proc/fs/nfsd/unlock_ip", EINVAL},
113 	{"read", "/proc/fs/nfsd/filehandle", EINVAL},
114 	{"read", "/proc/fs/nfsd/.getfs", EINVAL},
115 	{"read", "/proc/fs/nfsd/.getfd", EINVAL},
116 	{"read", "/proc/self/net/rpc/use-gss-proxy", EAGAIN},
117 	{"read", "/proc/sys/net/ipv6/conf/*/stable_secret", EIO},
118 	{"read", "/proc/sys/vm/nr_hugepages", EOPNOTSUPP},
119 	{"read", "/proc/sys/vm/nr_overcommit_hugepages", EOPNOTSUPP},
120 	{"read", "/proc/sys/vm/nr_hugepages_mempolicy", EOPNOTSUPP},
121 	/* These are added for making sure LTP runs on some of the devices that have non upstream
122 	 * proc files. See following commit for more details
123 	 *    d59bfa01c ("proc file fixup: This fixes some random qualcomm proc files that act odd")
124 	 */
125 	{"read", "/proc/debug/fwdump", EINVAL},
126 	{"read", "/proc/cid/athdiagpfs", EIO},
127 	{"read", "/proc/pressure/*", EOPNOTSUPP},
128 	{"", "", 0}
129 };
130 
131 /*
132  * If a particular LSM is enabled, it is expected that some entries can
133  * be read successfully. Otherwise, those entries will retrun some
134  * failures listed above. Here to add any LSM specific entries.
135  */
136 
137 /*
138  * Test macro to indicate that SELinux libraries and headers are
139  * installed.
140  */
141 #ifdef HAVE_LIBSELINUX_DEVEL
142 static const char lsm_should_work[][PATH_MAX] = {
143 	"/proc/self/attr/*",
144 	"/proc/self/attr/selinux/*",
145 	"/proc/self/task/[0-9]*/attr/*",
146 	"/proc/self/task/[0-9]*/attr/selinux/*",
147 	""
148 };
149 #endif
150 
151 /* Known files that does not honor O_NONBLOCK, so they will hang
152    the test while being read. */
153 static const char error_nonblock[][PATH_MAX] = {
154 	"/proc/xen/xenbus",
155 	""
156 };
157 
158 /*
159  * Verify expected failures, and then let the test to continue.
160  *
161  * Return 0 when a problem errno is found.
162  * Return 1 when a known issue is found.
163  *
164  */
found_errno(const char * syscall,const char * obj,int tmperr)165 static int found_errno(const char *syscall, const char *obj, int tmperr)
166 {
167 	int i;
168 
169 	/* Should not see any error for certain entries if a LSM is enabled. */
170 #ifdef HAVE_LIBSELINUX_DEVEL
171 	if (is_selinux_enabled()) {
172 		for (i = 0; lsm_should_work[i][0] != '\0'; i++) {
173 			if (!strcmp(obj, lsm_should_work[i]) ||
174 			    !fnmatch(lsm_should_work[i], obj, FNM_PATHNAME)) {
175 				return 0;
176 			}
177 		}
178 	}
179 #endif
180 	for (i = 0; known_issues[i].err != 0; i++) {
181 		if (tmperr == known_issues[i].err &&
182 		    (!strcmp(obj, known_issues[i].file) ||
183 		     !fnmatch(known_issues[i].file, obj, FNM_PATHNAME)) &&
184 		    !strcmp(syscall, known_issues[i].func)) {
185 			/* Using strcmp / fnmatch could have messed up the
186 			 * errno value. */
187 			errno = tmperr;
188 			tst_resm(TINFO | TERRNO, "%s: known issue", obj);
189 			return 1;
190 		}
191 	}
192 	return 0;
193 }
194 
cleanup(void)195 static void cleanup(void)
196 {
197 	tst_rmdir();
198 }
199 
setup(void)200 static void setup(void)
201 {
202 	tst_sig(FORK, DEF_HANDLER, cleanup);
203 	TEST_PAUSE;
204 	tst_tmpdir();
205 }
206 
help(void)207 void help(void)
208 {
209 	printf("  -b x    read byte count\n");
210 	printf("  -m x    max megabytes to read from single file\n");
211 	printf("  -q      read .../irq/... entries\n");
212 	printf("  -r x    proc pathname\n");
213 	printf("  -v      verbose mode\n");
214 }
215 
216 /*
217  * add the -m option whose parameter is the
218  * pages that should be mapped.
219  */
220 static option_t options[] = {
221 	{"b:", &opt_buffsize, &opt_buffsizestr},
222 	{"m:", &opt_maxmbytes, &opt_maxmbytesstr},
223 	{"q", &opt_readirq, NULL},
224 	{"r:", &opt_procpath, &opt_procpathstr},
225 	{"v", &opt_verbose, NULL},
226 	{NULL, NULL, NULL}
227 };
228 
229 /*
230  * NB: this function is recursive
231  * returns 0 if no error encountered, otherwise number of errors (objs)
232  *
233  * REM: Funny enough, while developing this function (actually replacing
234  *	streamed fopen by standard open), I hit a real /proc bug.
235  *	On a 2.2.13-SuSE kernel, "cat /proc/tty/driver/serial" would fail
236  *	with EFAULT, while "cat /proc/tty/driver/serial > somefile" wouldn't.
237  *	Okay, this might be due to a slight serial misconfiguration, but still.
238  *	Analysis with strace showed up the difference was on the count size
239  *	of read (1024 bytes vs 4096 bytes). So I tested further..
240  *	read count of 512 bytes adds /proc/tty/drivers to the list
241  *	of broken proc files, while 64 bytes reads removes
242  *	/proc/tty/driver/serial from the list. Interesting, isn't it?
243  *	Now, there's a -b option to this test, so you can try your luck. --SF
244  *
245  * It's more fun to run this test it as root, as all the files will be accessible!
246  * (however, be careful, there might be some bufferoverflow holes..)
247  * reading proc files might be also a good kernel latency killer.
248  */
readproc(const char * obj)249 static long readproc(const char *obj)
250 {
251 	DIR *dir = NULL;	/* pointer to a directory */
252 	struct dirent *dir_ent;	/* pointer to directory entries */
253 	char dirobj[PATH_MAX];	/* object inside directory to modify */
254 	struct stat statbuf;	/* used to hold stat information */
255 	int fd, tmperr, i;
256 	ssize_t nread;
257 	static char buf[MAX_BUFF_SIZE];	/* static kills reentrancy, but we don't care about the contents */
258 	unsigned long long file_total_read = 0;
259 
260 	/* Determine the file type */
261 	if (lstat(obj, &statbuf) < 0) {
262 
263 		/* permission denied is not considered as error */
264 		if (errno != EACCES) {
265 			tst_resm(TFAIL | TERRNO, "%s: lstat", obj);
266 			return 1;
267 		}
268 		return 0;
269 
270 	}
271 
272 	/* Prevent loops, but read /proc/self. */
273 	if (S_ISLNK(statbuf.st_mode) && strcmp(obj, selfpath))
274 		return 0;
275 
276 	total_obj++;
277 
278 	/* Take appropriate action, depending on the file type */
279 	if (S_ISDIR(statbuf.st_mode) || !strcmp(obj, selfpath)) {
280 
281 		/* object is a directory */
282 
283 		/*
284 		 * Skip over the /proc/irq directory, unless the user
285 		 * requested that we read the directory because it could
286 		 * map to a broken driver which effectively `hangs' the
287 		 * test.
288 		 */
289 		if (!opt_readirq && !strcmp("/proc/irq", obj)) {
290 			return 0;
291 			/* Open the directory to get access to what is in it */
292 		} else if ((dir = opendir(obj)) == NULL) {
293 			if (errno != EACCES) {
294 				tst_resm(TFAIL | TERRNO, "%s: opendir", obj);
295 				return 1;
296 			}
297 			return 0;
298 		} else {
299 
300 			long ret_val = 0;
301 
302 			/* Loop through the entries in the directory */
303 			for (dir_ent = (struct dirent *)readdir(dir);
304 			     dir_ent != NULL;
305 			     dir_ent = (struct dirent *)readdir(dir)) {
306 
307 				/* Ignore ".", "..", "kcore", and
308 				 * "/proc/<pid>" (unless this is our
309 				 * starting point as directed by the
310 				 * user).
311 				 */
312 				if (strcmp(dir_ent->d_name, ".") &&
313 				    strcmp(dir_ent->d_name, "..") &&
314 				    strcmp(dir_ent->d_name, "kcore") &&
315 				    (fnmatch("[0-9]*", dir_ent->d_name,
316 					     FNM_PATHNAME) ||
317 				     strcmp(obj, procpath))) {
318 
319 					if (opt_verbose) {
320 						fprintf(stderr, "%s\n",
321 							dir_ent->d_name);
322 					}
323 
324 					/* Recursively call this routine to test the
325 					 * current entry */
326 					snprintf(dirobj, PATH_MAX,
327 						 "%s/%s", obj, dir_ent->d_name);
328 					ret_val += readproc(dirobj);
329 
330 				}
331 
332 			}
333 
334 			/* Close the directory */
335 			if (dir)
336 				(void)closedir(dir);
337 
338 			return ret_val;
339 
340 		}
341 
342 	} else {		/* if it's not a dir, read it! */
343 
344 		if (!S_ISREG(statbuf.st_mode))
345 			return 0;
346 
347 #ifdef DEBUG
348 		fprintf(stderr, "%s", obj);
349 #endif
350 
351 		/* is O_NONBLOCK enough to escape from FIFO's ? */
352 		fd = open(obj, O_RDONLY | O_NONBLOCK);
353 		if (fd < 0) {
354 			tmperr = errno;
355 
356 			if (!found_errno("open", obj, tmperr)) {
357 
358 				errno = tmperr;
359 
360 				if (errno != EACCES) {
361 					tst_resm(TFAIL | TERRNO,
362 						 "%s: open failed", obj);
363 					return 1;
364 				}
365 
366 			}
367 			return 0;
368 
369 		}
370 
371 		/* Skip write-only files. */
372 		if ((statbuf.st_mode & S_IRUSR) == 0 &&
373 		    (statbuf.st_mode & S_IWUSR) != 0) {
374 			tst_resm(TINFO, "%s: is write-only.", obj);
375 			(void)close(fd);
376 			return 0;
377 		}
378 
379 		/* Skip files does not honor O_NONBLOCK. */
380 		for (i = 0; error_nonblock[i][0] != '\0'; i++) {
381 			if (!strcmp(obj, error_nonblock[i])) {
382 				tst_resm(TINFO, "%s: does not honor "
383 					 "O_NONBLOCK", obj);
384 				(void)close(fd);
385 				return 0;
386 			}
387 		}
388 
389 		file_total_read = 0;
390 		do {
391 
392 			nread = read(fd, buf, buffsize);
393 
394 			if (nread < 0) {
395 
396 				tmperr = errno;
397 				(void)close(fd);
398 
399 				/* ignore no perm (not root) and no
400 				 * process (terminated) errors */
401 				if (!found_errno("read", obj, tmperr)) {
402 
403 					errno = tmperr;
404 
405 					if (errno != EACCES && errno != ESRCH) {
406 						tst_resm(TFAIL | TERRNO,
407 							 "read failed: "
408 							 "%s", obj);
409 						return 1;
410 					}
411 					return 0;
412 
413 				}
414 
415 			} else
416 				file_total_read += nread;
417 
418 			if (opt_verbose) {
419 #ifdef DEBUG
420 				fprintf(stderr, "%ld", nread);
421 #endif
422 				fprintf(stderr, ".");
423 			}
424 
425 			if ((maxbytes > 0) && (file_total_read > maxbytes)) {
426 				tst_resm(TINFO, "%s: reached maxmbytes (-m)",
427 					 obj);
428 				break;
429 			}
430 		} while (0 < nread);
431 		total_read += file_total_read;
432 
433 		if (opt_verbose)
434 			fprintf(stderr, "\n");
435 
436 		if (0 <= fd)
437 			(void)close(fd);
438 
439 	}
440 
441 	/* It's better to assume success by default rather than failure. */
442 	return 0;
443 
444 }
445 
main(int argc,char * argv[])446 int main(int argc, char *argv[])
447 {
448 	int lc;
449 
450 	tst_parse_opts(argc, argv, options, help);
451 
452 	if (opt_buffsize) {
453 		size_t bs;
454 		bs = atoi(opt_buffsizestr);
455 		if (bs <= MAX_BUFF_SIZE)
456 			buffsize = bs;
457 		else
458 			tst_brkm(TBROK, cleanup,
459 				 "Invalid arg for -b (max: %u): %s",
460 				 MAX_BUFF_SIZE, opt_buffsizestr);
461 	}
462 	if (opt_maxmbytes)
463 		maxbytes = atoi(opt_maxmbytesstr) * 1024 * 1024;
464 
465 	if (opt_procpath)
466 		procpath = opt_procpathstr;
467 
468 	setup();
469 
470 	for (lc = 0; TEST_LOOPING(lc); lc++) {
471 		tst_count = 0;
472 
473 		TEST(readproc(procpath));
474 
475 		if (TEST_RETURN != 0) {
476 			tst_resm(TFAIL, "readproc() failed with %ld errors.",
477 				 TEST_RETURN);
478 		} else {
479 			tst_resm(TPASS, "readproc() completed successfully, "
480 				 "total read: %llu bytes, %u objs", total_read,
481 				 total_obj);
482 		}
483 	}
484 
485 	cleanup();
486 	tst_exit();
487 }
488