diff options
author | Alexey Dobriyan <adobriyan@gmail.com> | 2018-06-08 03:10:20 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-08 03:34:38 +0300 |
commit | b2f5de0334f03e90ae7dee72a7fc597ef555a9a8 (patch) | |
tree | 6eddd34b28ecd67582164b588f862951bf392680 /tools/testing/selftests/proc/fd-001-lookup.c | |
parent | 5d008fb414f7c73e0761835d941943e17d32463d (diff) | |
download | linux-b2f5de0334f03e90ae7dee72a7fc597ef555a9a8.tar.xz |
tools/testing/selftests/proc: test /proc/*/fd a bit (+ PF_KTHREAD is ABI!)
* Test lookup in /proc/self/fd.
"map_files" lookup story showed that lookup is not that simple.
* Test that all those symlinks open the same file.
Check with (st_dev, st_info).
* Test that kernel threads do not have anything in their /proc/*/fd/
directory.
Now this is where things get interesting.
First, kernel threads aren't pinned by /proc/self or equivalent,
thus some "atomicity" is required.
Second, ->comm can contain whitespace and ')'.
No, they are not escaped.
Third, the only reliable way to check if process is kernel thread
appears to be field #9 in /proc/*/stat.
This field is struct task_struct::flags in decimal!
Check is done by testing PF_KTHREAD flags like we do in kernel.
PF_KTREAD value is a part of userspace ABI !!!
Other methods for determining kernel threadness are not reliable:
* RSS can be 0 if everything is swapped, even while reading
from /proc/self.
* ->total_vm CAN BE ZERO if process is finishing
munmap(NULL, whole address space);
* /proc/*/maps and similar files can be empty because unmapping
everything works. Read returning 0 can't distinguish between
kernel thread and such suicide process.
Link: http://lkml.kernel.org/r/20180505000414.GA15090@avx2
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'tools/testing/selftests/proc/fd-001-lookup.c')
-rw-r--r-- | tools/testing/selftests/proc/fd-001-lookup.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/tools/testing/selftests/proc/fd-001-lookup.c b/tools/testing/selftests/proc/fd-001-lookup.c new file mode 100644 index 000000000000..a2010dfb2110 --- /dev/null +++ b/tools/testing/selftests/proc/fd-001-lookup.c @@ -0,0 +1,168 @@ +/* + * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +// Test /proc/*/fd lookup. +#define _GNU_SOURCE +#undef NDEBUG +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <limits.h> +#include <sched.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "proc.h" + +/* lstat(2) has more "coverage" in case non-symlink pops up somehow. */ +static void test_lookup_pass(const char *pathname) +{ + struct stat st; + ssize_t rv; + + memset(&st, 0, sizeof(struct stat)); + rv = lstat(pathname, &st); + assert(rv == 0); + assert(S_ISLNK(st.st_mode)); +} + +static void test_lookup_fail(const char *pathname) +{ + struct stat st; + ssize_t rv; + + rv = lstat(pathname, &st); + assert(rv == -1 && errno == ENOENT); +} + +static void test_lookup(unsigned int fd) +{ + char buf[64]; + unsigned int c; + unsigned int u; + int i; + + snprintf(buf, sizeof(buf), "/proc/self/fd/%u", fd); + test_lookup_pass(buf); + + /* leading junk */ + for (c = 1; c <= 255; c++) { + if (c == '/') + continue; + snprintf(buf, sizeof(buf), "/proc/self/fd/%c%u", c, fd); + test_lookup_fail(buf); + } + + /* trailing junk */ + for (c = 1; c <= 255; c++) { + if (c == '/') + continue; + snprintf(buf, sizeof(buf), "/proc/self/fd/%u%c", fd, c); + test_lookup_fail(buf); + } + + for (i = INT_MIN; i < INT_MIN + 1024; i++) { + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", i); + test_lookup_fail(buf); + } + for (i = -1024; i < 0; i++) { + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", i); + test_lookup_fail(buf); + } + for (u = INT_MAX - 1024; u <= (unsigned int)INT_MAX + 1024; u++) { + snprintf(buf, sizeof(buf), "/proc/self/fd/%u", u); + test_lookup_fail(buf); + } + for (u = UINT_MAX - 1024; u != 0; u++) { + snprintf(buf, sizeof(buf), "/proc/self/fd/%u", u); + test_lookup_fail(buf); + } + + +} + +int main(void) +{ + struct dirent *de; + unsigned int fd, target_fd; + + if (unshare(CLONE_FILES) == -1) + return 1; + + /* Wipe fdtable. */ + do { + DIR *d; + + d = opendir("/proc/self/fd"); + if (!d) + return 1; + + de = xreaddir(d); + assert(de->d_type == DT_DIR); + assert(streq(de->d_name, ".")); + + de = xreaddir(d); + assert(de->d_type == DT_DIR); + assert(streq(de->d_name, "..")); +next: + de = xreaddir(d); + if (de) { + unsigned long long fd_ull; + unsigned int fd; + char *end; + + assert(de->d_type == DT_LNK); + + fd_ull = xstrtoull(de->d_name, &end); + assert(*end == '\0'); + assert(fd_ull == (unsigned int)fd_ull); + + fd = fd_ull; + if (fd == dirfd(d)) + goto next; + close(fd); + } + + closedir(d); + } while (de); + + /* Now fdtable is clean. */ + + fd = open("/", O_PATH|O_DIRECTORY); + assert(fd == 0); + test_lookup(fd); + close(fd); + + /* Clean again! */ + + fd = open("/", O_PATH|O_DIRECTORY); + assert(fd == 0); + /* Default RLIMIT_NOFILE-1 */ + target_fd = 1023; + while (target_fd > 0) { + if (dup2(fd, target_fd) == target_fd) + break; + target_fd /= 2; + } + assert(target_fd > 0); + close(fd); + test_lookup(target_fd); + close(target_fd); + + return 0; +} |