162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/mm/process_vm_access.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010-2011 Christopher Yeoh <cyeoh@au1.ibm.com>, IBM Corp. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/compat.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/uio.h> 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/sched/mm.h> 1362306a36Sopenharmony_ci#include <linux/highmem.h> 1462306a36Sopenharmony_ci#include <linux/ptrace.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/syscalls.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/** 1962306a36Sopenharmony_ci * process_vm_rw_pages - read/write pages from task specified 2062306a36Sopenharmony_ci * @pages: array of pointers to pages we want to copy 2162306a36Sopenharmony_ci * @offset: offset in page to start copying from/to 2262306a36Sopenharmony_ci * @len: number of bytes to copy 2362306a36Sopenharmony_ci * @iter: where to copy to/from locally 2462306a36Sopenharmony_ci * @vm_write: 0 means copy from, 1 means copy to 2562306a36Sopenharmony_ci * Returns 0 on success, error code otherwise 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_cistatic int process_vm_rw_pages(struct page **pages, 2862306a36Sopenharmony_ci unsigned offset, 2962306a36Sopenharmony_ci size_t len, 3062306a36Sopenharmony_ci struct iov_iter *iter, 3162306a36Sopenharmony_ci int vm_write) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci /* Do the copy for each page */ 3462306a36Sopenharmony_ci while (len && iov_iter_count(iter)) { 3562306a36Sopenharmony_ci struct page *page = *pages++; 3662306a36Sopenharmony_ci size_t copy = PAGE_SIZE - offset; 3762306a36Sopenharmony_ci size_t copied; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (copy > len) 4062306a36Sopenharmony_ci copy = len; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (vm_write) 4362306a36Sopenharmony_ci copied = copy_page_from_iter(page, offset, copy, iter); 4462306a36Sopenharmony_ci else 4562306a36Sopenharmony_ci copied = copy_page_to_iter(page, offset, copy, iter); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci len -= copied; 4862306a36Sopenharmony_ci if (copied < copy && iov_iter_count(iter)) 4962306a36Sopenharmony_ci return -EFAULT; 5062306a36Sopenharmony_ci offset = 0; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Maximum number of pages kmalloc'd to hold struct page's during copy */ 5662306a36Sopenharmony_ci#define PVM_MAX_KMALLOC_PAGES (PAGE_SIZE * 2) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * process_vm_rw_single_vec - read/write pages from task specified 6062306a36Sopenharmony_ci * @addr: start memory address of target process 6162306a36Sopenharmony_ci * @len: size of area to copy to/from 6262306a36Sopenharmony_ci * @iter: where to copy to/from locally 6362306a36Sopenharmony_ci * @process_pages: struct pages area that can store at least 6462306a36Sopenharmony_ci * nr_pages_to_copy struct page pointers 6562306a36Sopenharmony_ci * @mm: mm for task 6662306a36Sopenharmony_ci * @task: task to read/write from 6762306a36Sopenharmony_ci * @vm_write: 0 means copy from, 1 means copy to 6862306a36Sopenharmony_ci * Returns 0 on success or on failure error code 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic int process_vm_rw_single_vec(unsigned long addr, 7162306a36Sopenharmony_ci unsigned long len, 7262306a36Sopenharmony_ci struct iov_iter *iter, 7362306a36Sopenharmony_ci struct page **process_pages, 7462306a36Sopenharmony_ci struct mm_struct *mm, 7562306a36Sopenharmony_ci struct task_struct *task, 7662306a36Sopenharmony_ci int vm_write) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned long pa = addr & PAGE_MASK; 7962306a36Sopenharmony_ci unsigned long start_offset = addr - pa; 8062306a36Sopenharmony_ci unsigned long nr_pages; 8162306a36Sopenharmony_ci ssize_t rc = 0; 8262306a36Sopenharmony_ci unsigned long max_pages_per_loop = PVM_MAX_KMALLOC_PAGES 8362306a36Sopenharmony_ci / sizeof(struct pages *); 8462306a36Sopenharmony_ci unsigned int flags = 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Work out address and page range required */ 8762306a36Sopenharmony_ci if (len == 0) 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci nr_pages = (addr + len - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (vm_write) 9262306a36Sopenharmony_ci flags |= FOLL_WRITE; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci while (!rc && nr_pages && iov_iter_count(iter)) { 9562306a36Sopenharmony_ci int pinned_pages = min(nr_pages, max_pages_per_loop); 9662306a36Sopenharmony_ci int locked = 1; 9762306a36Sopenharmony_ci size_t bytes; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * Get the pages we're interested in. We must 10162306a36Sopenharmony_ci * access remotely because task/mm might not 10262306a36Sopenharmony_ci * current/current->mm 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci mmap_read_lock(mm); 10562306a36Sopenharmony_ci pinned_pages = pin_user_pages_remote(mm, pa, pinned_pages, 10662306a36Sopenharmony_ci flags, process_pages, 10762306a36Sopenharmony_ci &locked); 10862306a36Sopenharmony_ci if (locked) 10962306a36Sopenharmony_ci mmap_read_unlock(mm); 11062306a36Sopenharmony_ci if (pinned_pages <= 0) 11162306a36Sopenharmony_ci return -EFAULT; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci bytes = pinned_pages * PAGE_SIZE - start_offset; 11462306a36Sopenharmony_ci if (bytes > len) 11562306a36Sopenharmony_ci bytes = len; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci rc = process_vm_rw_pages(process_pages, 11862306a36Sopenharmony_ci start_offset, bytes, iter, 11962306a36Sopenharmony_ci vm_write); 12062306a36Sopenharmony_ci len -= bytes; 12162306a36Sopenharmony_ci start_offset = 0; 12262306a36Sopenharmony_ci nr_pages -= pinned_pages; 12362306a36Sopenharmony_ci pa += pinned_pages * PAGE_SIZE; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* If vm_write is set, the pages need to be made dirty: */ 12662306a36Sopenharmony_ci unpin_user_pages_dirty_lock(process_pages, pinned_pages, 12762306a36Sopenharmony_ci vm_write); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return rc; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* Maximum number of entries for process pages array 13462306a36Sopenharmony_ci which lives on stack */ 13562306a36Sopenharmony_ci#define PVM_MAX_PP_ARRAY_COUNT 16 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * process_vm_rw_core - core of reading/writing pages from task specified 13962306a36Sopenharmony_ci * @pid: PID of process to read/write from/to 14062306a36Sopenharmony_ci * @iter: where to copy to/from locally 14162306a36Sopenharmony_ci * @rvec: iovec array specifying where to copy to/from in the other process 14262306a36Sopenharmony_ci * @riovcnt: size of rvec array 14362306a36Sopenharmony_ci * @flags: currently unused 14462306a36Sopenharmony_ci * @vm_write: 0 if reading from other process, 1 if writing to other process 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * Returns the number of bytes read/written or error code. May 14762306a36Sopenharmony_ci * return less bytes than expected if an error occurs during the copying 14862306a36Sopenharmony_ci * process. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter, 15162306a36Sopenharmony_ci const struct iovec *rvec, 15262306a36Sopenharmony_ci unsigned long riovcnt, 15362306a36Sopenharmony_ci unsigned long flags, int vm_write) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct task_struct *task; 15662306a36Sopenharmony_ci struct page *pp_stack[PVM_MAX_PP_ARRAY_COUNT]; 15762306a36Sopenharmony_ci struct page **process_pages = pp_stack; 15862306a36Sopenharmony_ci struct mm_struct *mm; 15962306a36Sopenharmony_ci unsigned long i; 16062306a36Sopenharmony_ci ssize_t rc = 0; 16162306a36Sopenharmony_ci unsigned long nr_pages = 0; 16262306a36Sopenharmony_ci unsigned long nr_pages_iov; 16362306a36Sopenharmony_ci ssize_t iov_len; 16462306a36Sopenharmony_ci size_t total_len = iov_iter_count(iter); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * Work out how many pages of struct pages we're going to need 16862306a36Sopenharmony_ci * when eventually calling get_user_pages 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci for (i = 0; i < riovcnt; i++) { 17162306a36Sopenharmony_ci iov_len = rvec[i].iov_len; 17262306a36Sopenharmony_ci if (iov_len > 0) { 17362306a36Sopenharmony_ci nr_pages_iov = ((unsigned long)rvec[i].iov_base 17462306a36Sopenharmony_ci + iov_len) 17562306a36Sopenharmony_ci / PAGE_SIZE - (unsigned long)rvec[i].iov_base 17662306a36Sopenharmony_ci / PAGE_SIZE + 1; 17762306a36Sopenharmony_ci nr_pages = max(nr_pages, nr_pages_iov); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (nr_pages == 0) 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (nr_pages > PVM_MAX_PP_ARRAY_COUNT) { 18562306a36Sopenharmony_ci /* For reliability don't try to kmalloc more than 18662306a36Sopenharmony_ci 2 pages worth */ 18762306a36Sopenharmony_ci process_pages = kmalloc(min_t(size_t, PVM_MAX_KMALLOC_PAGES, 18862306a36Sopenharmony_ci sizeof(struct pages *)*nr_pages), 18962306a36Sopenharmony_ci GFP_KERNEL); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (!process_pages) 19262306a36Sopenharmony_ci return -ENOMEM; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Get process information */ 19662306a36Sopenharmony_ci task = find_get_task_by_vpid(pid); 19762306a36Sopenharmony_ci if (!task) { 19862306a36Sopenharmony_ci rc = -ESRCH; 19962306a36Sopenharmony_ci goto free_proc_pages; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci mm = mm_access(task, PTRACE_MODE_ATTACH_REALCREDS); 20362306a36Sopenharmony_ci if (!mm || IS_ERR(mm)) { 20462306a36Sopenharmony_ci rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * Explicitly map EACCES to EPERM as EPERM is a more 20762306a36Sopenharmony_ci * appropriate error code for process_vw_readv/writev 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci if (rc == -EACCES) 21062306a36Sopenharmony_ci rc = -EPERM; 21162306a36Sopenharmony_ci goto put_task_struct; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci for (i = 0; i < riovcnt && iov_iter_count(iter) && !rc; i++) 21562306a36Sopenharmony_ci rc = process_vm_rw_single_vec( 21662306a36Sopenharmony_ci (unsigned long)rvec[i].iov_base, rvec[i].iov_len, 21762306a36Sopenharmony_ci iter, process_pages, mm, task, vm_write); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* copied = space before - space after */ 22062306a36Sopenharmony_ci total_len -= iov_iter_count(iter); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* If we have managed to copy any data at all then 22362306a36Sopenharmony_ci we return the number of bytes copied. Otherwise 22462306a36Sopenharmony_ci we return the error code */ 22562306a36Sopenharmony_ci if (total_len) 22662306a36Sopenharmony_ci rc = total_len; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci mmput(mm); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciput_task_struct: 23162306a36Sopenharmony_ci put_task_struct(task); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cifree_proc_pages: 23462306a36Sopenharmony_ci if (process_pages != pp_stack) 23562306a36Sopenharmony_ci kfree(process_pages); 23662306a36Sopenharmony_ci return rc; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/** 24062306a36Sopenharmony_ci * process_vm_rw - check iovecs before calling core routine 24162306a36Sopenharmony_ci * @pid: PID of process to read/write from/to 24262306a36Sopenharmony_ci * @lvec: iovec array specifying where to copy to/from locally 24362306a36Sopenharmony_ci * @liovcnt: size of lvec array 24462306a36Sopenharmony_ci * @rvec: iovec array specifying where to copy to/from in the other process 24562306a36Sopenharmony_ci * @riovcnt: size of rvec array 24662306a36Sopenharmony_ci * @flags: currently unused 24762306a36Sopenharmony_ci * @vm_write: 0 if reading from other process, 1 if writing to other process 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * Returns the number of bytes read/written or error code. May 25062306a36Sopenharmony_ci * return less bytes than expected if an error occurs during the copying 25162306a36Sopenharmony_ci * process. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_cistatic ssize_t process_vm_rw(pid_t pid, 25462306a36Sopenharmony_ci const struct iovec __user *lvec, 25562306a36Sopenharmony_ci unsigned long liovcnt, 25662306a36Sopenharmony_ci const struct iovec __user *rvec, 25762306a36Sopenharmony_ci unsigned long riovcnt, 25862306a36Sopenharmony_ci unsigned long flags, int vm_write) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct iovec iovstack_l[UIO_FASTIOV]; 26162306a36Sopenharmony_ci struct iovec iovstack_r[UIO_FASTIOV]; 26262306a36Sopenharmony_ci struct iovec *iov_l = iovstack_l; 26362306a36Sopenharmony_ci struct iovec *iov_r; 26462306a36Sopenharmony_ci struct iov_iter iter; 26562306a36Sopenharmony_ci ssize_t rc; 26662306a36Sopenharmony_ci int dir = vm_write ? ITER_SOURCE : ITER_DEST; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (flags != 0) 26962306a36Sopenharmony_ci return -EINVAL; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Check iovecs */ 27262306a36Sopenharmony_ci rc = import_iovec(dir, lvec, liovcnt, UIO_FASTIOV, &iov_l, &iter); 27362306a36Sopenharmony_ci if (rc < 0) 27462306a36Sopenharmony_ci return rc; 27562306a36Sopenharmony_ci if (!iov_iter_count(&iter)) 27662306a36Sopenharmony_ci goto free_iov_l; 27762306a36Sopenharmony_ci iov_r = iovec_from_user(rvec, riovcnt, UIO_FASTIOV, iovstack_r, 27862306a36Sopenharmony_ci in_compat_syscall()); 27962306a36Sopenharmony_ci if (IS_ERR(iov_r)) { 28062306a36Sopenharmony_ci rc = PTR_ERR(iov_r); 28162306a36Sopenharmony_ci goto free_iov_l; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci rc = process_vm_rw_core(pid, &iter, iov_r, riovcnt, flags, vm_write); 28462306a36Sopenharmony_ci if (iov_r != iovstack_r) 28562306a36Sopenharmony_ci kfree(iov_r); 28662306a36Sopenharmony_cifree_iov_l: 28762306a36Sopenharmony_ci kfree(iov_l); 28862306a36Sopenharmony_ci return rc; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ciSYSCALL_DEFINE6(process_vm_readv, pid_t, pid, const struct iovec __user *, lvec, 29262306a36Sopenharmony_ci unsigned long, liovcnt, const struct iovec __user *, rvec, 29362306a36Sopenharmony_ci unsigned long, riovcnt, unsigned long, flags) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci return process_vm_rw(pid, lvec, liovcnt, rvec, riovcnt, flags, 0); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciSYSCALL_DEFINE6(process_vm_writev, pid_t, pid, 29962306a36Sopenharmony_ci const struct iovec __user *, lvec, 30062306a36Sopenharmony_ci unsigned long, liovcnt, const struct iovec __user *, rvec, 30162306a36Sopenharmony_ci unsigned long, riovcnt, unsigned long, flags) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci return process_vm_rw(pid, lvec, liovcnt, rvec, riovcnt, flags, 1); 30462306a36Sopenharmony_ci} 305