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