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