1 /*
2 * Copyright (C) 2013-2021 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * This code is a complete clean re-write of the stress tool by
19 * Colin Ian King <colin.king@canonical.com> and attempts to be
20 * backwardly compatible with the stress tool by Amos Waterland
21 * <apw@rossby.metr.ou.edu> but has more stress tests and more
22 * functionality.
23 *
24 */
25 #include "stress-ng.h"
26
27 static const stress_help_t help[] = {
28 { NULL, "verity N", "start N workers exercising file verity ioctls" },
29 { NULL, "verity-ops N", "stop after N file verity bogo operations" },
30 { NULL, NULL, NULL }
31 };
32
33 #if defined(HAVE_LINUX_FSVERITY_H) && \
34 defined(HAVE_FSVERITY_ENABLE_ARG) && \
35 defined(HAVE_FSVERITY_DIGEST) && \
36 defined(FS_IOC_ENABLE_VERITY) && \
37 defined(FS_IOC_MEASURE_VERITY) && \
38 (defined(FS_VERITY_HASH_ALG_SHA256) || \
39 defined(FS_VERITY_HASH_ALG_SHA512))
40
41 static const uint32_t hash_algorithms[] = {
42 #if defined(FS_VERITY_HASH_ALG_SHA256)
43 FS_VERITY_HASH_ALG_SHA256,
44 #endif
45 #if defined(FS_VERITY_HASH_ALG_SHA512)
46 FS_VERITY_HASH_ALG_SHA512,
47 #endif
48 };
49
50
51 /*
52 * For FS_IOC_READ_VERITY_METADATA, introduced in Linux 5.12
53 */
54 struct shim_fsverity_read_metadata_arg {
55 uint64_t metadata_type;
56 uint64_t offset;
57 uint64_t length;
58 uint64_t buf_ptr;
59 uint64_t __reserved;
60 };
61
62 /*
63 * stress_verity
64 * stress file verity
65 */
stress_verity(const stress_args_t * args)66 static int stress_verity(const stress_args_t *args)
67 {
68 char filename[PATH_MAX];
69 int ret, fd;
70 uint32_t rnd = stress_mwc32();
71 size_t hash = 0;
72
73 if (SIZEOF_ARRAY(hash_algorithms) == (0)) {
74 if (args->instance == 0)
75 pr_inf_skip("%s: no hash algorithms defined, skipping stressor\n",
76 args->name);
77 return EXIT_NO_RESOURCE;
78 }
79
80 ret = stress_temp_dir_mk_args(args);
81 if (ret < 0)
82 return exit_status(-ret);
83
84 (void)stress_temp_filename_args(args, filename, sizeof(filename), rnd);
85
86 stress_set_proc_state(args->name, STRESS_STATE_RUN);
87
88 do {
89 struct fsverity_enable_arg enable;
90 char digest_buf[256];
91 struct fsverity_digest *digest = (struct fsverity_digest *)digest_buf;
92 char block[512];
93 int i;
94 #if defined(FS_IOC_READ_VERITY_METADATA)
95 struct shim_fsverity_read_metadata_arg md_arg;
96 char md_buf[4096];
97 #endif
98
99 fd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
100 if (fd < 0) {
101 ret = exit_status(errno);
102 pr_err("%s: cannot create %s, errno=%d (%s)\n",
103 args->name, filename, errno, strerror(errno));
104 return ret;
105 }
106 for (i = 0; i < 16; i++) {
107 const off_t off = (off_t)i * 64 * 1024;
108
109 (void)memset(block, i, sizeof(block));
110 ret = (int)lseek(fd, off, SEEK_SET);
111 (void)ret;
112
113 ret = (int)write(fd, block, sizeof(block));
114 if (ret < 0) {
115 ret = exit_status(errno);
116 pr_err("%s: cannot write %s\n", args->name, filename);
117 (void)close(fd);
118 goto clean;
119 }
120 }
121 (void)shim_fsync(fd);
122 (void)close(fd);
123 (void)sync();
124
125 fd = open(filename, O_RDONLY);
126 if (fd < 0) {
127 ret = exit_status(errno);
128 pr_err("%s: cannot re-open %s, errno=%d (%s)\n",
129 args->name, filename, errno, strerror(errno));
130 goto clean;
131 }
132
133 (void)memset(&enable, 0, sizeof(enable));
134 enable.version = 1;
135 enable.hash_algorithm = hash_algorithms[hash];
136 enable.block_size = (uint32_t)args->page_size;
137 enable.salt_size = 0;
138 enable.salt_ptr = (intptr_t)NULL;
139 enable.sig_size = 0;
140 enable.sig_ptr = (intptr_t)NULL;
141
142 hash++;
143 if (hash >= SIZEOF_ARRAY(hash_algorithms))
144 hash = 0;
145
146 ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &enable);
147 if (ret < 0) {
148 switch (errno) {
149 case EINVAL:
150 case ENOTTY:
151 case EOPNOTSUPP:
152 case ENOSYS:
153 if (args->instance == 0)
154 pr_inf_skip("%s: verity is not supported on the "
155 "file system or by the kernel, skipping stress test\n",
156 args->name);
157 ret = EXIT_NOT_IMPLEMENTED;
158 break;
159 case ENOPKG:
160 pr_inf("%s: kernel does not have sha256 "
161 "crypto enabled\n",
162 args->name);
163 ret = EXIT_NOT_IMPLEMENTED;
164 break;
165 case EROFS:
166 case EACCES:
167 case EBUSY:
168 case EINTR:
169 case ENOSPC:
170 ret = EXIT_NO_RESOURCE;
171 break;
172 default:
173 pr_inf("%s: verity ioctl FS_IOC_ENABLE_VERITY "
174 "failed on file %s, errno=%d (%s)\n",
175 args->name, filename, errno, strerror(errno));
176 ret = EXIT_FAILURE;
177 }
178 (void)close(fd);
179 goto clean;
180 }
181
182 /*
183 * Exercise measuring verity, ignore return for now
184 */
185 digest->digest_algorithm = FS_VERITY_HASH_ALG_SHA256;
186 digest->digest_size = 32;
187 ret = ioctl(fd, FS_IOC_MEASURE_VERITY, digest);
188 (void)ret;
189
190 #if defined(FS_IOC_GETFLAGS) && \
191 defined(FS_VERITY_FL)
192 {
193 int flags = 0;
194
195 ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
196 if ((ret == 0) && !(flags & FS_VERITY_FL)) {
197 pr_fail("%s: verity enabled but FS_VERITY_FL bit not "
198 "set on file flags from ioctl FS_IOC_GETFLAGS\n",
199 args->name);
200 }
201 }
202 #endif
203 (void)close(fd);
204
205 /*
206 * Read data back, should exercise verity verification
207 */
208 fd = open(filename, O_RDONLY);
209 if (fd < 0) {
210 ret = exit_status(errno);
211 pr_err("%s: cannot re-open %s, errno=%d (%s)\n",
212 args->name, filename, errno, strerror(errno));
213 goto clean;
214 }
215 for (i = 0; i < 16; i++) {
216 const off_t off = (off_t)i * 64 * 1024;
217
218 (void)memset(block, i, sizeof(block));
219 ret = (int)lseek(fd, off, SEEK_SET);
220 (void)ret;
221
222 ret = (int)read(fd, block, sizeof(block));
223 if (ret < 0) {
224 ret = exit_status(errno);
225 pr_err("%s: cannot read %s\n", args->name, filename);
226 (void)close(fd);
227 goto clean;
228 }
229 if (block[0] != i) {
230 pr_err("%s: data in file block %d is incorrect\n",
231 args->name, i);
232 (void)close(fd);
233 goto clean;
234 }
235 }
236 (void)shim_fsync(fd);
237
238 #if defined(FS_IOC_READ_VERITY_METADATA)
239 (void)memset(&md_arg, 0, sizeof(md_arg));
240 md_arg.metadata_type = 0ULL;
241 md_arg.offset = 0ULL;
242 md_arg.buf_ptr = (uint64_t)(intptr_t)md_buf;
243 md_arg.length = (uint64_t)sizeof(md_buf);
244
245 (void)ioctl(fd, FS_IOC_READ_VERITY_METADATA, &md_arg);
246 #endif
247
248 (void)close(fd);
249 (void)unlink(filename);
250
251 inc_counter(args);
252 } while (keep_stressing(args));
253
254 ret = EXIT_SUCCESS;
255
256 clean:
257 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
258
259 (void)unlink(filename);
260 (void)stress_temp_dir_rm_args(args);
261
262 return ret;
263 }
264
265 stressor_info_t stress_verity_info = {
266 .stressor = stress_verity,
267 .class = CLASS_FILESYSTEM | CLASS_OS,
268 .help = help
269 };
270 #else
271 stressor_info_t stress_verity_info = {
272 .stressor = stress_not_implemented,
273 .class = CLASS_FILESYSTEM | CLASS_OS,
274 .help = help
275 };
276 #endif
277