1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) International Business Machines  Corp., 2012
4 * Copyright (c) Linux Test Project, 2012
5 * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
6 */
7
8/*\
9 * [Description]
10 *
11 * Fork two children, one child mallocs randomly sized trunks of memory
12 * and initializes them; the other child calls process_vm_readv with
13 * the remote iovecs initialized to the original process memory
14 * locations and the local iovecs initialized to randomly sized and
15 * allocated local memory locations. The second child then verifies
16 * that the data is copied correctly.
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <sys/types.h>
22#include <sys/wait.h>
23#include "tst_test.h"
24#include "lapi/syscalls.h"
25
26#define MAX_IOVECS 1024
27
28static struct tcase {
29	int bufsize;
30	int remote_iovecs;
31	int local_iovecs;
32} testcases[] = {
33	{ .bufsize = 1024, .remote_iovecs = 1024, .local_iovecs = 8 },
34	{ .bufsize = 1024, .remote_iovecs = 512, .local_iovecs = 16 },
35	{ .bufsize = 1024, .remote_iovecs = 256, .local_iovecs = 32 },
36	{ .bufsize = 1024, .remote_iovecs = 128, .local_iovecs = 64 },
37	{ .bufsize = 1024, .remote_iovecs = 64, .local_iovecs = 128 },
38	{ .bufsize = 1024, .remote_iovecs = 32, .local_iovecs = 256 },
39	{ .bufsize = 1024, .remote_iovecs = 16, .local_iovecs = 512 },
40	{ .bufsize = 1024, .remote_iovecs = 8, .local_iovecs = 1024 },
41
42	{ .bufsize = 131072, .remote_iovecs = 1024, .local_iovecs = 8 },
43	{ .bufsize = 131072, .remote_iovecs = 512, .local_iovecs = 16 },
44	{ .bufsize = 131072, .remote_iovecs = 256, .local_iovecs = 32 },
45	{ .bufsize = 131072, .remote_iovecs = 128, .local_iovecs = 64 },
46	{ .bufsize = 131072, .remote_iovecs = 64, .local_iovecs = 128 },
47	{ .bufsize = 131072, .remote_iovecs = 32, .local_iovecs = 256 },
48	{ .bufsize = 131072, .remote_iovecs = 16, .local_iovecs = 512 },
49	{ .bufsize = 131072, .remote_iovecs = 8, .local_iovecs = 1024 },
50};
51
52static char **data_ptr;
53
54static void create_data_size(int *arr, int arr_sz, int buffsize)
55{
56	long bufsz_left;
57	int i;
58
59	bufsz_left = buffsize;
60	for (i = 0; i < arr_sz - 1; i++) {
61		arr[i] = rand() % ((bufsz_left / 2) + 1);
62		bufsz_left -= arr[i];
63	}
64
65	arr[arr_sz - 1] = bufsz_left;
66}
67
68static void child_alloc(const int *sizes, int nr_iovecs)
69{
70	int i, j;
71	long count;
72
73	count = 0;
74	for (i = 0; i < nr_iovecs; i++) {
75		data_ptr[i] = sizes[i] ? SAFE_MALLOC(sizes[i]) : NULL;
76
77		for (j = 0; j < sizes[i]; j++) {
78			data_ptr[i][j] = count % 256;
79			count++;
80		}
81	}
82
83	tst_res(TINFO, "child_alloc: memory allocated and initialized");
84
85	TST_CHECKPOINT_WAKE_AND_WAIT(0);
86}
87
88static void child_read(const int *sizes, int local_iovecs, int remote_iovecs,
89			 pid_t pid_alloc, int buffsize)
90{
91	struct iovec local[local_iovecs];
92	struct iovec remote[remote_iovecs];
93	int i, j;
94	int count;
95	int nr_error;
96	int local_sizes[local_iovecs];
97	unsigned char expect, actual;
98
99	for (i = 0; i < remote_iovecs; i++) {
100		remote[i].iov_base = (void *)data_ptr[i];
101		remote[i].iov_len = sizes[i];
102	}
103
104	create_data_size(local_sizes, local_iovecs, buffsize);
105	for (i = 0; i < local_iovecs; i++) {
106		local[i].iov_base = SAFE_MALLOC(local_sizes[i]);
107		local[i].iov_len = local_sizes[i];
108	}
109
110	tst_res(TINFO, "child_read: reading string from same memory location");
111
112	TST_EXP_POSITIVE(tst_syscall(__NR_process_vm_readv, pid_alloc, local,
113				     local_iovecs, remote, remote_iovecs, 0UL),
114			 "process_vm_read()");
115
116	if (TST_RET != buffsize) {
117		tst_brk(TBROK, "process_vm_readv: expected %d bytes but got %ld",
118			buffsize, TST_RET);
119	}
120
121	count = 0;
122	nr_error = 0;
123	for (i = 0; i < local_iovecs; i++) {
124		for (j = 0; j < (int)local[i].iov_len; j++) {
125			expect = count % 256;
126			actual = ((unsigned char *)local[i].iov_base)[j];
127			if (expect != actual)
128				nr_error++;
129
130			count++;
131		}
132	}
133
134	if (nr_error)
135		tst_brk(TFAIL, "child_read: %d incorrect bytes received", nr_error);
136	else
137		tst_res(TPASS, "child_read: all bytes are correctly received");
138}
139
140static void setup(void)
141{
142	tst_syscall(__NR_process_vm_readv, getpid(), NULL, 0UL, NULL, 0UL, 0UL);
143
144	data_ptr = SAFE_MMAP(NULL, sizeof(void *) * MAX_IOVECS, PROT_READ | PROT_WRITE,
145			     MAP_SHARED | MAP_ANONYMOUS, -1, 0);
146}
147
148static void cleanup(void)
149{
150	if (data_ptr)
151		SAFE_MUNMAP(data_ptr, sizeof(void *) * MAX_IOVECS);
152}
153
154static void run(unsigned int i)
155{
156	int bufsize = testcases[i].bufsize;
157	int remote_iovecs = testcases[i].remote_iovecs;
158	int local_iovecs = testcases[i].local_iovecs;
159	pid_t pid_alloc;
160	pid_t pid_read;
161	int status;
162	int sizes[remote_iovecs];
163
164	tst_res(TINFO, "bufsize=%d, remote_iovecs=%d, local_iovecs=%d", bufsize,
165		remote_iovecs, local_iovecs);
166
167	create_data_size(sizes, remote_iovecs, bufsize);
168
169	pid_alloc = SAFE_FORK();
170	if (!pid_alloc) {
171		child_alloc(sizes, remote_iovecs);
172		return;
173	}
174
175	TST_CHECKPOINT_WAIT(0);
176
177	pid_read = SAFE_FORK();
178	if (!pid_read) {
179		child_read(sizes, local_iovecs, remote_iovecs, pid_alloc, bufsize);
180		return;
181	}
182
183	SAFE_WAITPID(pid_read, &status, 0);
184	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
185		tst_res(TFAIL, "child_read: %s", tst_strstatus(status));
186
187	TST_CHECKPOINT_WAKE(0);
188}
189
190static struct tst_test test = {
191	.test = run,
192	.setup = setup,
193	.cleanup = cleanup,
194	.forks_child = 1,
195	.needs_checkpoints = 1,
196	.tcnt = ARRAY_SIZE(testcases),
197};
198