1 /*
2 * proc01.c - Tests Linux /proc file reading.
3 *
4 * Copyright (C) 2001 Stephane Fillod <f4cfe@free.fr>
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/smack/*", EINVAL},
101 {"read", "/proc/self/attr/apparmor/*", EINVAL},
102 {"read", "/proc/self/task/[0-9]*/attr/*", EINVAL},
103 {"read", "/proc/self/task/[0-9]*/attr/smack/*", EINVAL},
104 {"read", "/proc/self/task/[0-9]*/attr/apparmor/*", EINVAL},
105 {"read", "/proc/self/ns/*", EINVAL},
106 {"read", "/proc/self/task/[0-9]*/ns/*", EINVAL},
107 {"read", "/proc/ppc64/rtas/error_log", EINVAL},
108 {"read", "/proc/powerpc/rtas/error_log", EINVAL},
109 {"read", "/proc/fs/nfsd/unlock_filesystem", EINVAL},
110 {"read", "/proc/fs/nfsd/unlock_ip", EINVAL},
111 {"read", "/proc/fs/nfsd/filehandle", EINVAL},
112 {"read", "/proc/fs/nfsd/.getfs", EINVAL},
113 {"read", "/proc/fs/nfsd/.getfd", EINVAL},
114 {"read", "/proc/self/net/rpc/use-gss-proxy", EAGAIN},
115 {"read", "/proc/sys/net/ipv6/conf/*/stable_secret", EIO},
116 {"read", "/proc/sys/vm/nr_hugepages", EOPNOTSUPP},
117 {"read", "/proc/sys/vm/nr_overcommit_hugepages", EOPNOTSUPP},
118 {"read", "/proc/sys/vm/nr_hugepages_mempolicy", EOPNOTSUPP},
119 /* These are added for making sure LTP runs on some of the devices that have non upstream
120 * proc files. See following commit for more details
121 * d59bfa01c ("proc file fixup: This fixes some random qualcomm proc files that act odd")
122 */
123 {"read", "/proc/debug/fwdump", EINVAL},
124 {"read", "/proc/cid/athdiagpfs", EIO},
125 {"read", "/proc/pressure/*", EOPNOTSUPP},
126 {"", "", 0}
127 };
128
129 /*
130 * If a particular LSM is enabled, it is expected that some entries can
131 * be read successfully. Otherwise, those entries will retrun some
132 * failures listed above. Here to add any LSM specific entries.
133 */
134
135 /*
136 * Test macro to indicate that SELinux libraries and headers are
137 * installed.
138 */
139 #ifdef HAVE_LIBSELINUX_DEVEL
140 static const char lsm_should_work[][PATH_MAX] = {
141 "/proc/self/attr/*",
142 "/proc/self/task/[0-9]*/attr/*",
143 ""
144 };
145
146 /* Place holder for none of LSM is detected. */
147 #else
148 static const char lsm_should_work[][PATH_MAX] = {
149 ""
150 };
151 #endif
152
153 /* Known files that does not honor O_NONBLOCK, so they will hang
154 the test while being read. */
155 static const char error_nonblock[][PATH_MAX] = {
156 "/proc/xen/xenbus",
157 ""
158 };
159
160 /*
161 * Verify expected failures, and then let the test to continue.
162 *
163 * Return 0 when a problem errno is found.
164 * Return 1 when a known issue is found.
165 *
166 */
found_errno(const char * syscall,const char * obj,int tmperr)167 static int found_errno(const char *syscall, const char *obj, int tmperr)
168 {
169 int i;
170
171 /* Should not see any error for certain entries if a LSM is enabled. */
172 #ifdef HAVE_LIBSELINUX_DEVEL
173 if (is_selinux_enabled()) {
174 for (i = 0; lsm_should_work[i][0] != '\0'; i++) {
175 if (!strcmp(obj, lsm_should_work[i]) ||
176 !fnmatch(lsm_should_work[i], obj, FNM_PATHNAME)) {
177 return 0;
178 }
179 }
180 }
181 #endif
182 for (i = 0; known_issues[i].err != 0; i++) {
183 if (tmperr == known_issues[i].err &&
184 (!strcmp(obj, known_issues[i].file) ||
185 !fnmatch(known_issues[i].file, obj, FNM_PATHNAME)) &&
186 !strcmp(syscall, known_issues[i].func)) {
187 /* Using strcmp / fnmatch could have messed up the
188 * errno value. */
189 errno = tmperr;
190 tst_resm(TINFO | TERRNO, "%s: known issue", obj);
191 return 1;
192 }
193 }
194 return 0;
195 }
196
cleanup(void)197 static void cleanup(void)
198 {
199 tst_rmdir();
200 }
201
setup(void)202 static void setup(void)
203 {
204 tst_sig(FORK, DEF_HANDLER, cleanup);
205 TEST_PAUSE;
206 tst_tmpdir();
207 }
208
help(void)209 void help(void)
210 {
211 printf(" -b x read byte count\n");
212 printf(" -m x max megabytes to read from single file\n");
213 printf(" -q read .../irq/... entries\n");
214 printf(" -r x proc pathname\n");
215 printf(" -v verbose mode\n");
216 }
217
218 /*
219 * add the -m option whose parameter is the
220 * pages that should be mapped.
221 */
222 static option_t options[] = {
223 {"b:", &opt_buffsize, &opt_buffsizestr},
224 {"m:", &opt_maxmbytes, &opt_maxmbytesstr},
225 {"q", &opt_readirq, NULL},
226 {"r:", &opt_procpath, &opt_procpathstr},
227 {"v", &opt_verbose, NULL},
228 {NULL, NULL, NULL}
229 };
230
231 /*
232 * NB: this function is recursive
233 * returns 0 if no error encountered, otherwise number of errors (objs)
234 *
235 * REM: Funny enough, while developing this function (actually replacing
236 * streamed fopen by standard open), I hit a real /proc bug.
237 * On a 2.2.13-SuSE kernel, "cat /proc/tty/driver/serial" would fail
238 * with EFAULT, while "cat /proc/tty/driver/serial > somefile" wouldn't.
239 * Okay, this might be due to a slight serial misconfiguration, but still.
240 * Analysis with strace showed up the difference was on the count size
241 * of read (1024 bytes vs 4096 bytes). So I tested further..
242 * read count of 512 bytes adds /proc/tty/drivers to the list
243 * of broken proc files, while 64 bytes reads removes
244 * /proc/tty/driver/serial from the list. Interesting, isn't it?
245 * Now, there's a -b option to this test, so you can try your luck. --SF
246 *
247 * It's more fun to run this test it as root, as all the files will be accessible!
248 * (however, be careful, there might be some bufferoverflow holes..)
249 * reading proc files might be also a good kernel latency killer.
250 */
readproc(const char * obj)251 static long readproc(const char *obj)
252 {
253 DIR *dir = NULL; /* pointer to a directory */
254 struct dirent *dir_ent; /* pointer to directory entries */
255 char dirobj[PATH_MAX]; /* object inside directory to modify */
256 struct stat statbuf; /* used to hold stat information */
257 int fd, tmperr, i;
258 ssize_t nread;
259 static char buf[MAX_BUFF_SIZE]; /* static kills reentrancy, but we don't care about the contents */
260 unsigned long long file_total_read = 0;
261
262 /* Determine the file type */
263 if (lstat(obj, &statbuf) < 0) {
264
265 /* permission denied is not considered as error */
266 if (errno != EACCES) {
267 tst_resm(TFAIL | TERRNO, "%s: lstat", obj);
268 return 1;
269 }
270 return 0;
271
272 }
273
274 /* Prevent loops, but read /proc/self. */
275 if (S_ISLNK(statbuf.st_mode) && strcmp(obj, selfpath))
276 return 0;
277
278 total_obj++;
279
280 /* Take appropriate action, depending on the file type */
281 if (S_ISDIR(statbuf.st_mode) || !strcmp(obj, selfpath)) {
282
283 /* object is a directory */
284
285 /*
286 * Skip over the /proc/irq directory, unless the user
287 * requested that we read the directory because it could
288 * map to a broken driver which effectively `hangs' the
289 * test.
290 */
291 if (!opt_readirq && !strcmp("/proc/irq", obj)) {
292 return 0;
293 /* Open the directory to get access to what is in it */
294 } else if ((dir = opendir(obj)) == NULL) {
295 if (errno != EACCES) {
296 tst_resm(TFAIL | TERRNO, "%s: opendir", obj);
297 return 1;
298 }
299 return 0;
300 } else {
301
302 long ret_val = 0;
303
304 /* Loop through the entries in the directory */
305 for (dir_ent = (struct dirent *)readdir(dir);
306 dir_ent != NULL;
307 dir_ent = (struct dirent *)readdir(dir)) {
308
309 /* Ignore ".", "..", "kcore", and
310 * "/proc/<pid>" (unless this is our
311 * starting point as directed by the
312 * user).
313 */
314 if (strcmp(dir_ent->d_name, ".") &&
315 strcmp(dir_ent->d_name, "..") &&
316 strcmp(dir_ent->d_name, "kcore") &&
317 (fnmatch("[0-9]*", dir_ent->d_name,
318 FNM_PATHNAME) ||
319 strcmp(obj, procpath))) {
320
321 if (opt_verbose) {
322 fprintf(stderr, "%s\n",
323 dir_ent->d_name);
324 }
325
326 /* Recursively call this routine to test the
327 * current entry */
328 snprintf(dirobj, PATH_MAX,
329 "%s/%s", obj, dir_ent->d_name);
330 ret_val += readproc(dirobj);
331
332 }
333
334 }
335
336 /* Close the directory */
337 if (dir)
338 (void)closedir(dir);
339
340 return ret_val;
341
342 }
343
344 } else { /* if it's not a dir, read it! */
345
346 if (!S_ISREG(statbuf.st_mode))
347 return 0;
348
349 #ifdef DEBUG
350 fprintf(stderr, "%s", obj);
351 #endif
352
353 /* is O_NONBLOCK enough to escape from FIFO's ? */
354 fd = open(obj, O_RDONLY | O_NONBLOCK);
355 if (fd < 0) {
356 tmperr = errno;
357
358 if (!found_errno("open", obj, tmperr)) {
359
360 errno = tmperr;
361
362 if (errno != EACCES) {
363 tst_resm(TFAIL | TERRNO,
364 "%s: open failed", obj);
365 return 1;
366 }
367
368 }
369 return 0;
370
371 }
372
373 /* Skip write-only files. */
374 if ((statbuf.st_mode & S_IRUSR) == 0 &&
375 (statbuf.st_mode & S_IWUSR) != 0) {
376 tst_resm(TINFO, "%s: is write-only.", obj);
377 (void)close(fd);
378 return 0;
379 }
380
381 /* Skip files does not honor O_NONBLOCK. */
382 for (i = 0; error_nonblock[i][0] != '\0'; i++) {
383 if (!strcmp(obj, error_nonblock[i])) {
384 tst_resm(TINFO, "%s: does not honor "
385 "O_NONBLOCK", obj);
386 (void)close(fd);
387 return 0;
388 }
389 }
390
391 file_total_read = 0;
392 do {
393
394 nread = read(fd, buf, buffsize);
395
396 if (nread < 0) {
397
398 tmperr = errno;
399 (void)close(fd);
400
401 /* ignore no perm (not root) and no
402 * process (terminated) errors */
403 if (!found_errno("read", obj, tmperr)) {
404
405 errno = tmperr;
406
407 if (errno != EACCES && errno != ESRCH) {
408 tst_resm(TFAIL | TERRNO,
409 "read failed: "
410 "%s", obj);
411 return 1;
412 }
413 return 0;
414
415 }
416
417 } else
418 file_total_read += nread;
419
420 if (opt_verbose) {
421 #ifdef DEBUG
422 fprintf(stderr, "%ld", nread);
423 #endif
424 fprintf(stderr, ".");
425 }
426
427 if ((maxbytes > 0) && (file_total_read > maxbytes)) {
428 tst_resm(TINFO, "%s: reached maxmbytes (-m)",
429 obj);
430 break;
431 }
432 } while (0 < nread);
433 total_read += file_total_read;
434
435 if (opt_verbose)
436 fprintf(stderr, "\n");
437
438 if (0 <= fd)
439 (void)close(fd);
440
441 }
442
443 /* It's better to assume success by default rather than failure. */
444 return 0;
445
446 }
447
main(int argc,char * argv[])448 int main(int argc, char *argv[])
449 {
450 int lc;
451
452 tst_parse_opts(argc, argv, options, help);
453
454 if (opt_buffsize) {
455 size_t bs;
456 bs = atoi(opt_buffsizestr);
457 if (bs <= MAX_BUFF_SIZE)
458 buffsize = bs;
459 else
460 tst_brkm(TBROK, cleanup,
461 "Invalid arg for -b (max: %u): %s",
462 MAX_BUFF_SIZE, opt_buffsizestr);
463 }
464 if (opt_maxmbytes)
465 maxbytes = atoi(opt_maxmbytesstr) * 1024 * 1024;
466
467 if (opt_procpath)
468 procpath = opt_procpathstr;
469
470 setup();
471
472 for (lc = 0; TEST_LOOPING(lc); lc++) {
473 tst_count = 0;
474
475 TEST(readproc(procpath));
476
477 if (TEST_RETURN != 0) {
478 tst_resm(TFAIL, "readproc() failed with %ld errors.",
479 TEST_RETURN);
480 } else {
481 tst_resm(TPASS, "readproc() completed successfully, "
482 "total read: %llu bytes, %u objs", total_read,
483 total_obj);
484 }
485 }
486
487 cleanup();
488 tst_exit();
489 }
490