1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
#include <sys/statfs.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "zdtmtst.h"
const char *test_doc = "Check C/R of processes that point to a common dead pidfd\n";
const char *test_author = "Bhavik Sachdev <b.sachdev1904@gmail.com>";
#ifndef PID_FS_MAGIC
#define PID_FS_MAGIC 0x50494446
#endif
/*
* main
* `- child
* `- grandchild
*
* main and child open a pidfd for grandchild.
* Before C/R we kill grandchild.
* We end up with two pidfds in two diff processes that point to the same dead process.
*/
static long get_fs_type(int lfd)
{
struct statfs fst;
if (fstatfs(lfd, &fst)) {
return -1;
}
return fst.f_type;
}
static int pidfd_open(pid_t pid, unsigned int flags)
{
return syscall(__NR_pidfd_open, pid, flags);
}
static int pidfd_send_signal(int pidfd, int sig, siginfo_t *info, unsigned int flags)
{
return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
}
static int check_for_pidfs(void)
{
long type;
int pidfd = pidfd_open(getpid(), 0);
if (pidfd < 0) {
pr_perror("pidfd open() failed");
return -1;
}
type = get_fs_type(pidfd);
close(pidfd);
return type == PID_FS_MAGIC;
}
int main(int argc, char *argv[])
{
#define READ 0
#define WRITE 1
int child, ret, gchild, status;
struct statx stat;
task_waiter_t t;
unsigned long long ino;
/*
* We use the inop pipe to send the inode number of the
* pidfd opened in the child to the main process for
* comparison.
*/
int p[2];
int pidfd;
test_init(argc, argv);
task_waiter_init(&t);
ret = check_for_pidfs();
if (ret < 0)
return 1;
if (ret == 0) {
test_daemon();
test_waitsig();
skip("Test requires pidfs. skipping...");
pass();
return 0;
}
if (pipe(p)) {
pr_perror("pipe");
return 1;
}
child = test_fork();
if (child < 0) {
pr_perror("fork");
return 1;
} else if (child == 0) {
int gchild;
gchild = test_fork();
if (gchild < 0) {
pr_perror("fork");
return 1;
} else if (gchild == 0) {
close(p[READ]);
close(p[WRITE]);
while (1)
sleep(1000);
} else {
if (write(p[WRITE], &gchild, sizeof(int)) != sizeof(int)) {
pr_perror("write");
return 1;
}
pidfd = pidfd_open(gchild, 0);
if (pidfd < 0) {
pr_perror("pidfd_open");
return 1;
}
if (waitpid(gchild, &status, 0) != gchild) {
pr_perror("waitpid");
return 1;
}
if (!WIFSIGNALED(status)) {
fail("Expected grandchild to be terminated by a signal");
return 1;
}
if (WTERMSIG(status) != SIGKILL) {
fail("Expected grandchild to be terminated by SIGKILL");
return 1;
}
task_waiter_complete(&t, 1);
test_waitsig();
if (statx(pidfd, "", AT_EMPTY_PATH, STATX_ALL, &stat) < 0) {
pr_perror("statx");
return 1;
}
close(p[WRITE]);
if (read(p[READ], &ino, sizeof(ino)) != sizeof(ino)) {
pr_perror("read");
return 1;
}
close(p[READ]);
close(pidfd);
/* ino number should be same because both pidfds were for the same process */
if (ino != stat.stx_ino) {
exit(1);
}
exit(0);
}
}
if (read(p[READ], &gchild, sizeof(int)) != sizeof(int)) {
pr_perror("write");
return 1;
}
pidfd = pidfd_open(gchild, 0);
if (pidfd < 0) {
pr_perror("pidfd_open");
return 1;
}
/*
* We kill grandchild process only after opening pidfd.
*/
if (pidfd_send_signal(pidfd, SIGKILL, NULL, 0)) {
pr_perror("pidfd_send_signal");
return 1;
}
/* Wait for child to waitpid on gchild */
task_waiter_wait4(&t, 1);
test_daemon();
test_waitsig();
close(p[READ]);
if (statx(pidfd, "", AT_EMPTY_PATH, STATX_ALL, &stat) < 0) {
pr_perror("statx");
goto err;
}
/* Send inode number of pidfd to child for comparison */
if (write(p[WRITE], &stat.stx_ino, sizeof(stat.stx_ino)) != sizeof(stat.stx_ino)) {
pr_perror("write");
goto err;
}
close(p[WRITE]);
if (kill(child, SIGTERM)) {
pr_perror("kill");
goto err;
}
if (waitpid(child, &status, 0) != child) {
pr_perror("waitpid");
goto err;
}
if (!WIFEXITED(status)) {
fail("Expected child to terminate normally");
goto err;
}
if (WEXITSTATUS(status) != 0) {
fail("Child failed");
goto err;
}
pass();
close(pidfd);
return 0;
err:
close(pidfd);
return 1;
}
|