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 <[email protected]>
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
28 static 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
52 static char **data_ptr;
53
create_data_size(int * arr,int arr_sz,int buffsize)54 static 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
child_alloc(const int * sizes,int nr_iovecs)68 static 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
child_read(const int * sizes,int local_iovecs,int remote_iovecs,pid_t pid_alloc,int buffsize)88 static 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
setup(void)140 static 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
cleanup(void)148 static void cleanup(void)
149 {
150 if (data_ptr)
151 SAFE_MUNMAP(data_ptr, sizeof(void *) * MAX_IOVECS);
152 }
153
run(unsigned int i)154 static 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
190 static 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