• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) Linux Test Project, 2012
4  * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
5  */
6 
7 /*\
8  * [Description]
9  *
10  * Test errno codes in process_vm_readv and process_vm_writev syscalls.
11  */
12 
13 #include <pwd.h>
14 #include <stdlib.h>
15 #include "tst_test.h"
16 #include "lapi/syscalls.h"
17 
18 struct process_vm_params {
19 	int len;
20 	char *ldummy;
21 	char *rdummy;
22 	pid_t pid;
23 	struct iovec *lvec;
24 	unsigned long liovcnt;
25 	struct iovec *rvec;
26 	unsigned long riovcnt;
27 	unsigned long flags;
28 };
29 
30 static char *str_read;
31 static void (*test_params)(struct process_vm_params *params);
32 
alloc_params(void)33 static struct process_vm_params *alloc_params(void)
34 {
35 	struct process_vm_params *sane_params;
36 	int len;
37 
38 	len = getpagesize();
39 
40 	sane_params = SAFE_MALLOC(sizeof(struct process_vm_params));
41 	sane_params->len = len;
42 	sane_params->ldummy = SAFE_MALLOC(len);
43 	sane_params->rdummy = SAFE_MALLOC(len);
44 
45 	sane_params->lvec = SAFE_MALLOC(sizeof(struct process_vm_params));
46 	sane_params->lvec->iov_base = sane_params->ldummy;
47 	sane_params->lvec->iov_len = len;
48 	sane_params->liovcnt = 1;
49 
50 	sane_params->rvec = SAFE_MALLOC(sizeof(struct process_vm_params));
51 	sane_params->rvec->iov_base = sane_params->rdummy;
52 	sane_params->rvec->iov_len = len;
53 	sane_params->riovcnt = 1;
54 
55 	sane_params->flags = 0;
56 	sane_params->pid = getpid();
57 
58 	return sane_params;
59 }
60 
free_params(struct process_vm_params * params)61 static void free_params(struct process_vm_params *params)
62 {
63 	if (params) {
64 		free(params->ldummy);
65 		free(params->rdummy);
66 		free(params->lvec);
67 		free(params->rvec);
68 		free(params);
69 	}
70 }
71 
test_readv(struct process_vm_params * params)72 static void test_readv(struct process_vm_params *params)
73 {
74 	TEST(tst_syscall(__NR_process_vm_readv,
75 		params->pid,
76 		params->lvec, params->liovcnt,
77 		params->rvec, params->riovcnt,
78 		params->flags));
79 }
80 
test_writev(struct process_vm_params * params)81 static void test_writev(struct process_vm_params *params)
82 {
83 	TEST(tst_syscall(__NR_process_vm_writev,
84 		params->pid,
85 		params->lvec, params->liovcnt,
86 		params->rvec, params->riovcnt,
87 		params->flags));
88 }
89 
check_errno(long expected_errno)90 static void check_errno(long expected_errno)
91 {
92 	if (TST_ERR == expected_errno)
93 		tst_res(TPASS | TTERRNO, "expected failure");
94 	else if (TST_ERR == 0)
95 		tst_res(TFAIL, "call succeeded unexpectedly");
96 	else
97 		tst_res(TFAIL | TTERRNO, "unexpected failure - "
98 			"expected = %ld : %s, actual",
99 			expected_errno, strerror(expected_errno));
100 }
101 
test_sane_params(void)102 static void test_sane_params(void)
103 {
104 	struct process_vm_params *sane_params;
105 
106 	tst_res(TINFO, "Testing sane parameters");
107 
108 	sane_params = alloc_params();
109 	test_params(sane_params);
110 	TST_EXP_EQ_LI(TST_RET, sane_params->len);
111 	free_params(sane_params);
112 }
113 
test_flags(void)114 static void test_flags(void)
115 {
116 	struct process_vm_params *params;
117 	long flags[] = { -INT_MAX, -1, 1, INT_MAX, 0 };
118 	int flags_size = ARRAY_SIZE(flags) / sizeof(flags[0]);
119 	int i;
120 
121 	params = alloc_params();
122 
123 	for (i = 0; i < flags_size; i++) {
124 		params->flags = flags[i];
125 
126 		tst_res(TINFO, "Testing flags=%ld", flags[i]);
127 		test_params(params);
128 
129 		/* atm. only flags == 0 is allowed, everything else
130 		 * should fail with EINVAL
131 		 */
132 		if (flags[i] != 0) {
133 			TST_EXP_EQ_LI(TST_RET, -1);
134 			check_errno(EINVAL);
135 		} else {
136 			TST_EXP_EQ_LI(TST_RET, params->len);
137 		}
138 	}
139 
140 	free_params(params);
141 }
142 
test_iov_len_overflow(void)143 static void test_iov_len_overflow(void)
144 {
145 	struct process_vm_params *params;
146 
147 	tst_res(TINFO, "Testing iov_len = -1");
148 
149 	params = alloc_params();
150 	params->lvec->iov_len = -1;
151 	params->rvec->iov_len = -1;
152 	test_params(params);
153 	TST_EXP_EQ_LI(TST_RET, -1);
154 	check_errno(EINVAL);
155 	free_params(params);
156 }
157 
test_iov_invalid(void)158 static void test_iov_invalid(void)
159 {
160 	struct process_vm_params *sane_params;
161 	struct process_vm_params params_copy;
162 
163 	sane_params = alloc_params();
164 
165 	tst_res(TINFO, "Testing lvec->iov_base = -1");
166 	params_copy = *sane_params;
167 	params_copy.lvec->iov_base = (void *)-1;
168 	test_params(&params_copy);
169 	TST_EXP_EQ_LI(TST_RET, -1);
170 	check_errno(EFAULT);
171 
172 	tst_res(TINFO, "Testing rvec->iov_base = -1");
173 	params_copy = *sane_params;
174 	params_copy.rvec->iov_base = (void *)-1;
175 	test_params(&params_copy);
176 	TST_EXP_EQ_LI(TST_RET, -1);
177 	check_errno(EFAULT);
178 
179 	tst_res(TINFO, "Testing lvec = -1");
180 	params_copy = *sane_params;
181 	params_copy.lvec = (void *)-1;
182 	test_params(&params_copy);
183 	TST_EXP_EQ_LI(TST_RET, -1);
184 	check_errno(EFAULT);
185 
186 	tst_res(TINFO, "Testing rvec = -1");
187 	params_copy = *sane_params;
188 	params_copy.rvec = (void *)-1;
189 	test_params(&params_copy);
190 	TST_EXP_EQ_LI(TST_RET, -1);
191 	check_errno(EFAULT);
192 
193 	free_params(sane_params);
194 }
195 
test_invalid_pid(void)196 static void test_invalid_pid(void)
197 {
198 	pid_t invalid_pid = -1;
199 	struct process_vm_params *params;
200 	struct process_vm_params params_copy;
201 
202 	params = alloc_params();
203 
204 	tst_res(TINFO, "Testing invalid PID");
205 	params_copy = *params;
206 	params_copy.pid = invalid_pid;
207 	test_params(&params_copy);
208 	TST_EXP_EQ_LI(TST_RET, -1);
209 	check_errno(ESRCH);
210 
211 	tst_res(TINFO, "Testing unused PID");
212 	params_copy = *params;
213 	invalid_pid = tst_get_unused_pid();
214 	params_copy.pid = invalid_pid;
215 	test_params(&params_copy);
216 	TST_EXP_EQ_LI(TST_RET, -1);
217 	check_errno(ESRCH);
218 
219 	free_params(params);
220 }
221 
test_invalid_perm(void)222 static void test_invalid_perm(void)
223 {
224 	char nobody_uid[] = "nobody";
225 	struct passwd *ltpuser;
226 	struct process_vm_params *params;
227 	pid_t child_pid;
228 	pid_t parent_pid;
229 
230 	tst_res(TINFO, "Testing invalid permissions on given PID");
231 
232 	parent_pid = getpid();
233 	child_pid = SAFE_FORK();
234 	if (!child_pid) {
235 		ltpuser = SAFE_GETPWNAM(nobody_uid);
236 		SAFE_SETUID(ltpuser->pw_uid);
237 
238 		params = alloc_params();
239 		params->pid = parent_pid;
240 		test_params(params);
241 		TST_EXP_EQ_LI(TST_RET, -1);
242 		check_errno(EPERM);
243 		free_params(params);
244 		return;
245 	}
246 
247 	/* collect result from child  before the next test, otherwise
248 	 * TFAIL/TPASS messages will arrive asynchronously
249 	 */
250 	tst_reap_children();
251 }
252 
test_invalid_protection(void)253 static void test_invalid_protection(void)
254 {
255 	struct process_vm_params *sane_params;
256 	struct process_vm_params params_copy;
257 	void *data;
258 	int len;
259 
260 	len = getpagesize();
261 	sane_params = alloc_params();
262 	data = SAFE_MMAP(NULL, len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
263 
264 	tst_res(TINFO, "Testing data with invalid protection (lvec)");
265 	params_copy = *sane_params;
266 	params_copy.lvec->iov_base = data;
267 	test_params(&params_copy);
268 	TST_EXP_EQ_LI(TST_RET, -1);
269 	check_errno(EFAULT);
270 
271 	tst_res(TINFO, "Testing data with invalid protection (rvec)");
272 	params_copy = *sane_params;
273 	params_copy.rvec->iov_base = data;
274 	test_params(&params_copy);
275 	TST_EXP_EQ_LI(TST_RET, -1);
276 	check_errno(EFAULT);
277 
278 	SAFE_MUNMAP(data, len);
279 	free_params(sane_params);
280 }
281 
run(void)282 static void run(void)
283 {
284 	test_sane_params();
285 	test_flags();
286 	test_iov_len_overflow();
287 	test_iov_invalid();
288 	test_invalid_pid();
289 	test_invalid_perm();
290 	test_invalid_protection();
291 }
292 
setup(void)293 static void setup(void)
294 {
295 	if (str_read) {
296 		tst_res(TINFO, "Selected process_vm_readv");
297 		test_params = test_readv;
298 	} else {
299 		tst_res(TINFO, "Selected process_vm_writev");
300 		test_params = test_writev;
301 	}
302 }
303 
304 static struct tst_test test = {
305 	.test_all = run,
306 	.setup = setup,
307 	.forks_child = 1,
308 	.needs_root = 1,
309 	.options = (struct tst_option[]) {
310 		{"r", &str_read, "Use process_vm_read instead of process_vm_write"},
311 		{},
312 	},
313 };
314