From 6beeabcd42cbc4c82c4c11f8b8e5a28bdb146ba6 Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Fri, 20 Mar 2020 22:17:30 +0300 Subject: [PATCH] zdtm: add sk-unix-dgram-ghost test case This testcase reproduces deadlock in "wait_fds_event" futex in open_fdinfos() function (files subsystem). Signed-off-by: Alexander Mikhalitsyn --- test/zdtm/static/Makefile | 1 + test/zdtm/static/sk-unix-dgram-ghost.c | 230 ++++++++++++++++++++++ test/zdtm/static/sk-unix-dgram-ghost.desc | 1 + 3 files changed, 232 insertions(+) create mode 100644 test/zdtm/static/sk-unix-dgram-ghost.c create mode 100644 test/zdtm/static/sk-unix-dgram-ghost.desc diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile index b021b18fb..6e4f50880 100644 --- a/test/zdtm/static/Makefile +++ b/test/zdtm/static/Makefile @@ -381,6 +381,7 @@ TST_DIR = \ del_standalone_un \ sk-unix-mntns \ sk-unix01 \ + sk-unix-dgram-ghost \ unsupported_children_collision \ shared_slave_mount_children \ non_uniform_share_propagation \ diff --git a/test/zdtm/static/sk-unix-dgram-ghost.c b/test/zdtm/static/sk-unix-dgram-ghost.c new file mode 100644 index 000000000..a726d6a64 --- /dev/null +++ b/test/zdtm/static/sk-unix-dgram-ghost.c @@ -0,0 +1,230 @@ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zdtmtst.h" + +const char *test_doc = "Check data of bound ghost DGRAM unix socket and possibility to connect"; +const char *test_author = "Alexander Mikhalitsyn "; + +/* + * PROCESS_NUM | FREEZE_FREQ + * 3 | 1 / 5 + * 4 | 1 / 5 + * 5 | 2 / 5 + * 10 | 10 / 10 + */ +#define PROCESSES_NUM 10 + +#define MSG "hello" +char filename[PATH_MAX]; +char *dirname; +TEST_OPTION(dirname, string, "directory name", 1); + +static int fill_sock_name(struct sockaddr_un *name, const char *filename) +{ + char *cwd; + + cwd = get_current_dir_name(); + if (strlen(filename) + strlen(cwd) + 1 >= sizeof(name->sun_path)) { + pr_err("Name %s/%s is too long for socket\n", + cwd, filename); + return -1; + } + + name->sun_family = AF_LOCAL; + ssprintf(name->sun_path, "%s/%s", cwd, filename); + return 0; +} + +static int client(int i, task_waiter_t t) +{ + struct sockaddr_un addr; + int sk; + + sk = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sk < 0) { + pr_perror("open client %d", i); + return 1; + } + + if (fill_sock_name(&addr, filename) < 0) { + pr_err("%s is too long for socket\n", filename); + return 1; + } + + if (connect(sk, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) { + pr_perror("connect failed %d", i); + return 1; + } + + /* we are ready to c/r */ + task_waiter_complete(&t, 1); + + /* wait for server let us to send data */ + task_waiter_wait4(&t, 2); + + test_msg("child %d: lets send\n", i); + + if (send(sk, MSG, sizeof(MSG), 0) != sizeof(MSG)) { + pr_perror("send failed %d", i); + return 1; + } + + test_msg("child %d: MSG was sent\n", i); + + /* notify server that we sent data */ + task_waiter_complete(&t, 3); + + return 0; +} + +static void child_exited(int signo) +{ + int status; + pid_t pid; + + while (1) { + pid = waitpid(-1, &status, WNOHANG); + if (pid == -1) { + if (errno == ECHILD) + break; + + fail("wait failed"); + exit(1); + } + + if (pid == 0) + return; + + if (status) { + pr_err("A child (pid: %d) exited with 0x%x\n", pid, status); + exit(1); + } + } +} + +int main(int argc, char **argv) +{ + struct sockaddr_un addr; + int srv, ret; + size_t i; + char buf[1024]; + task_waiter_t t[PROCESSES_NUM]; + + test_init(argc, argv); + + ssprintf(filename, "%s/%s", dirname, "sk"); + + if (fill_sock_name(&addr, filename) < 0) { + pr_err("%s is too long for socket\n", filename); + ret = 1; + goto clean; + } + + if (signal(SIGCHLD, child_exited) == SIG_ERR) { + pr_perror("can't set SIGCHLD handler"); + exit(1); + } + + for (i = 0; i < PROCESSES_NUM; i++) + task_waiter_init(&t[i]); + + if (mkdir(dirname, 0755) < 0) { + if (errno != EEXIST) { + pr_perror("Can't create %s", dirname); + return 1; + } + } + + /* + * Freeze happens if unix socket is the *last* file descriptor. + * So, if we for example, move task_waiter_init() after server + * socket creation we loose reproduce. + */ + srv = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); + if (srv < 0) { + pr_perror("open srv"); + ret = 1; + goto clean; + } + + if (bind(srv, (struct sockaddr *) &addr, sizeof(struct sockaddr_un))) { + pr_perror("bind srv"); + ret = 1; + goto clean; + } + + for (i = 0; i < PROCESSES_NUM; i++) { + ret = test_fork(); + if (ret == -1) { + pr_perror("fork"); + ret = 1; + goto clean; + } + + if (ret == 0) { + close(srv); + exit(client(i, t[i])); + } + + task_waiter_wait4(&t[i], 1); + } + + /* + * It's very important part of this test-case to make + * *ghost* unix socket. Because problem with criu freeze + * appears especially with *ghost* unix socket. + */ + unlink(addr.sun_path); + + ret = 1; + + test_daemon(); + test_waitsig(); + + test_msg("C/R complete\n"); + + /* let childs to send data to server socket */ + for (i = 0; i < PROCESSES_NUM; i++) + task_waiter_complete(&t[i], 2); + + /* wait childs for send data */ + for (i = 0; i < PROCESSES_NUM; i++) + task_waiter_wait4(&t[i], 3); + + test_msg("Checking result\n"); + + /* check we can read all client messages */ + for (i = 0; i < PROCESSES_NUM; i++) { + ret = read(srv, buf, sizeof(MSG)); + buf[ret > 0 ? ret : 0] = 0; + if (ret != sizeof(MSG)) { + fail("%d: %s", ret, buf); + ret = 1; + goto clean; + } + } + + ret = 0; + pass(); + +clean: + unlink(addr.sun_path); + rmdir(dirname); + return ret; +} diff --git a/test/zdtm/static/sk-unix-dgram-ghost.desc b/test/zdtm/static/sk-unix-dgram-ghost.desc new file mode 100644 index 000000000..2651c4d77 --- /dev/null +++ b/test/zdtm/static/sk-unix-dgram-ghost.desc @@ -0,0 +1 @@ +{'flavor': 'h ns uns', 'flags': 'suid'}