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 <alexander.mikhalitsyn@virtuozzo.com>
This commit is contained in:
Alexander Mikhalitsyn 2020-03-20 22:17:30 +03:00 committed by Andrei Vagin
parent 2609e98ee7
commit 6beeabcd42
3 changed files with 232 additions and 0 deletions

View file

@ -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 \

View file

@ -0,0 +1,230 @@
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>
#include <errno.h>
#include <dirent.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#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 <alexander@mihalicyn.com>";
/*
* 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;
}

View file

@ -0,0 +1 @@
{'flavor': 'h ns uns', 'flags': 'suid'}