From: Kees Cook <keescook@xxxxxxxxxxxx> This makes sure that wchan contains a sensible symbol when a process is blocked. Specifically this calls the sleep() syscall, and expects the architecture to have called schedule() from a function that has "sleep" somewhere in its name. For example, on the architectures I tested (x86_64, arm64, arm, mips, and powerpc) this is "hrtimer_nanosleep": $ tools/testing/selftests/proc/proc-pid-wchan ok: found 'sleep' in wchan 'hrtimer_nanosleep' Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx> Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx> Link: https://lkml.kernel.org/r/20211008235504.2957528-1-keescook@xxxxxxxxxxxx --- tools/testing/selftests/proc/Makefile | 1 tools/testing/selftests/proc/proc-pid-wchan.c | 69 ++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tools/testing/selftests/proc/proc-pid-wchan.c --- a/tools/testing/selftests/proc/Makefile +++ b/tools/testing/selftests/proc/Makefile @@ -8,6 +8,7 @@ TEST_GEN_PROGS += fd-002-posix-eq TEST_GEN_PROGS += fd-003-kthread TEST_GEN_PROGS += proc-loadavg-001 TEST_GEN_PROGS += proc-pid-vm +TEST_GEN_PROGS += proc-pid-wchan TEST_GEN_PROGS += proc-self-map-files-001 TEST_GEN_PROGS += proc-self-map-files-002 TEST_GEN_PROGS += proc-self-syscall --- /dev/null +++ b/tools/testing/selftests/proc/proc-pid-wchan.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Make sure that wchan returns a reasonable symbol when blocked. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> + +#define perror_exit(str) do { perror(str); _exit(1); } while (0) + +int main(void) +{ + char buf[64]; + pid_t child; + int sync[2], fd; + + if (pipe(sync) < 0) + perror_exit("pipe"); + + child = fork(); + if (child < 0) + perror_exit("fork"); + if (child == 0) { + /* Child */ + if (close(sync[0]) < 0) + perror_exit("child close sync[0]"); + if (close(sync[1]) < 0) + perror_exit("child close sync[1]"); + sleep(10); + _exit(0); + } + /* Parent */ + if (close(sync[1]) < 0) + perror_exit("parent close sync[1]"); + if (read(sync[0], buf, 1) != 0) + perror_exit("parent read sync[0]"); + + snprintf(buf, sizeof(buf), "/proc/%d/wchan", child); + fd = open(buf, O_RDONLY); + if (fd < 0) { + if (errno == ENOENT) + return 4; + perror_exit(buf); + } + + memset(buf, 0, sizeof(buf)); + if (read(fd, buf, sizeof(buf) - 1) < 1) + perror_exit(buf); + if (strstr(buf, "sleep") == NULL) { + fprintf(stderr, "FAIL: did not find 'sleep' in wchan '%s'\n", buf); + return 1; + } + printf("ok: found 'sleep' in wchan '%s'\n", buf); + + if (kill(child, SIGKILL) < 0) + perror_exit("kill"); + if (waitpid(child, NULL, 0) != child) { + fprintf(stderr, "waitpid: got the wrong child!?\n"); + return 1; + } + + return 0; +}