1 /*
2 * Check that fault injection works properly.
3 *
4 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2016-2021 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12
13 #include <assert.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/stat.h>
22 #include <sys/uio.h>
23 #include <sys/wait.h>
24
25 static int exp_fd;
26 static int got_fd;
27 static int out_fd;
28
29 #define DEFAULT_ERRNO ENOSYS
30
31 static const char *errstr;
32 static int is_raw, err;
33
34 static void
35 invoke(int iter, int fail)
36 {
37 static char buf[sizeof(int) * 3 + 3];
38 static int try;
39 const struct iovec io = {
40 .iov_base = buf,
41 .iov_len = sprintf(buf, "%d.", ++try)
42 };
43 int rc;
44
45 if (!fail) {
46 rc = write(exp_fd, io.iov_base, io.iov_len);
47 if (rc != (int) io.iov_len)
48 perror_msg_and_fail("iter %d: write", iter);
49 }
50
51 errno = 0;
52 rc = writev(got_fd, &io, 1);
53
54 if (fail) {
55 if (!(rc == -1 && errno == err))
56 perror_msg_and_fail("iter %d: expected errno %d"
57 ", got rc == %d, errno == %d",
58 iter, err, rc, errno);
59
60 if (is_raw)
61 tprintf("writev(%#x, %p, 0x1)"
62 " = -1 %s (%m) (INJECTED)\n",
63 got_fd, &io, errstr);
64 else
65 tprintf("writev(%d, [{iov_base=\"%s\", iov_len=%d}], 1)"
66 " = -1 %s (%m) (INJECTED)\n",
67 got_fd, buf, (int) io.iov_len, errstr);
68 } else {
69 if (rc != (int) io.iov_len)
70 perror_msg_and_fail("iter %d: expected %d"
71 ", got rc == %d, errno == %d",
72 iter, (int) io.iov_len, rc, errno);
73
74 if (is_raw)
75 tprintf("writev(%#x, %p, 0x1) = %#x\n",
76 got_fd, &io, rc);
77 else
78 tprintf("writev(%d, [{iov_base=\"%s\", iov_len=%d}], 1)"
79 " = %d\n",
80 got_fd, buf, (int) io.iov_len,
81 (int) io.iov_len);
82 }
83 }
84
85 static int
86 open_file(const char *prefix, int proc)
87 {
88 static const int open_flags = O_WRONLY | O_TRUNC | O_CREAT;
89 static char path[PATH_MAX + 1];
90
91 snprintf(path, sizeof(path), "%s.%d", prefix, proc);
92
93 int fd = open(path, open_flags, 0600);
94 if (fd < 0)
95 perror_msg_and_fail("open: %s", path);
96
97 return fd;
98 }
99
100 int
101 main(int argc, char *argv[])
102 {
103 assert(argc == 12);
104
105 is_raw = !strcmp("raw", argv[1]);
106
107 errstr = argv[2];
108 err = atoi(errstr);
109 assert(err >= 0);
110
111 if (!err) {
112 if (!*errstr)
113 err = DEFAULT_ERRNO;
114 else if (!strcasecmp(errstr, "EINVAL"))
115 err = EINVAL;
116 else
117 err = ENOSYS;
118 }
119
120 errno = err;
121 errstr = errno2name();
122
123 int first = atoi(argv[3]);
124 int last = atoi(argv[4]);
125 int step = atoi(argv[5]);
126 int iter = atoi(argv[6]);
127 int num_procs = atoi(argv[7]);
128 char *exp_prefix = argv[8];
129 char *got_prefix = argv[9];
130 char *out_prefix = argv[10];
131 char *pid_prefix = argv[11];
132
133 assert(first > 0);
134 assert(step >= 0);
135 assert(num_procs > 0);
136
137 for (int proc = 0; proc < num_procs; ++proc) {
138 int ret = fork();
139
140 if (ret < 0)
141 perror_msg_and_fail("fork");
142
143 if (ret > 0) {
144 int pidfd = open_file(pid_prefix, proc);
145
146 char pidstr[sizeof(ret) * 3];
147 int len = snprintf(pidstr, sizeof(pidstr), "%d", ret);
148 assert(len > 0 && len < (int) sizeof(pidstr));
149 assert(write(pidfd, pidstr, len) == len);
150
151 close(pidfd);
152
153 continue;
154 }
155
156 tprintf("%s", "");
157
158 exp_fd = open_file(exp_prefix, proc);
159 got_fd = open_file(got_prefix, proc);
160 out_fd = open_file(out_prefix, proc);
161
162 /* This magic forces tprintf to write where we want it. */
163 dup2(out_fd, 3);
164
165 for (int i = 1; i <= iter; ++i) {
166 int fail = 0;
167 if (last != 0) {
168 --first;
169 if (last != -1)
170 --last;
171 if (first == 0) {
172 fail = 1;
173 first = step;
174 }
175 }
176 invoke(i, fail);
177 }
178
179 tprintf("%s\n", "+++ exited with 0 +++");
180 return 0;
181 }
182
183 for (int proc = 0; proc < num_procs; ++proc) {
184 int status;
185 int ret = wait(&status);
186 if (ret <= 0)
187 perror_msg_and_fail("wait %d", proc);
188 if (status)
189 error_msg_and_fail("wait: pid=%d status=%d",
190 ret, status);
191 }
192
193 return 0;
194 }