diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /mm/process_vm_access.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'mm/process_vm_access.c')
-rw-r--r-- | mm/process_vm_access.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c new file mode 100644 index 000000000..c90d722c6 --- /dev/null +++ b/mm/process_vm_access.c | |||
@@ -0,0 +1,305 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
2 | /* | ||
3 | * linux/mm/process_vm_access.c | ||
4 | * | ||
5 | * Copyright (C) 2010-2011 Christopher Yeoh <cyeoh@au1.ibm.com>, IBM Corp. | ||
6 | */ | ||
7 | |||
8 | #include <linux/compat.h> | ||
9 | #include <linux/mm.h> | ||
10 | #include <linux/uio.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/compat.h> | ||
13 | #include <linux/sched/mm.h> | ||
14 | #include <linux/highmem.h> | ||
15 | #include <linux/ptrace.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/syscalls.h> | ||
18 | |||
19 | /** | ||
20 | * process_vm_rw_pages - read/write pages from task specified | ||
21 | * @pages: array of pointers to pages we want to copy | ||
22 | * @offset: offset in page to start copying from/to | ||
23 | * @len: number of bytes to copy | ||
24 | * @iter: where to copy to/from locally | ||
25 | * @vm_write: 0 means copy from, 1 means copy to | ||
26 | * Returns 0 on success, error code otherwise | ||
27 | */ | ||
28 | static int process_vm_rw_pages(struct page **pages, | ||
29 | unsigned offset, | ||
30 | size_t len, | ||
31 | struct iov_iter *iter, | ||
32 | int vm_write) | ||
33 | { | ||
34 | /* Do the copy for each page */ | ||
35 | while (len && iov_iter_count(iter)) { | ||
36 | struct page *page = *pages++; | ||
37 | size_t copy = PAGE_SIZE - offset; | ||
38 | size_t copied; | ||
39 | |||
40 | if (copy > len) | ||
41 | copy = len; | ||
42 | |||
43 | if (vm_write) | ||
44 | copied = copy_page_from_iter(page, offset, copy, iter); | ||
45 | else | ||
46 | copied = copy_page_to_iter(page, offset, copy, iter); | ||
47 | |||
48 | len -= copied; | ||
49 | if (copied < copy && iov_iter_count(iter)) | ||
50 | return -EFAULT; | ||
51 | offset = 0; | ||
52 | } | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | /* Maximum number of pages kmalloc'd to hold struct page's during copy */ | ||
57 | #define PVM_MAX_KMALLOC_PAGES (PAGE_SIZE * 2) | ||
58 | |||
59 | /** | ||
60 | * process_vm_rw_single_vec - read/write pages from task specified | ||
61 | * @addr: start memory address of target process | ||
62 | * @len: size of area to copy to/from | ||
63 | * @iter: where to copy to/from locally | ||
64 | * @process_pages: struct pages area that can store at least | ||
65 | * nr_pages_to_copy struct page pointers | ||
66 | * @mm: mm for task | ||
67 | * @task: task to read/write from | ||
68 | * @vm_write: 0 means copy from, 1 means copy to | ||
69 | * Returns 0 on success or on failure error code | ||
70 | */ | ||
71 | static int process_vm_rw_single_vec(unsigned long addr, | ||
72 | unsigned long len, | ||
73 | struct iov_iter *iter, | ||
74 | struct page **process_pages, | ||
75 | struct mm_struct *mm, | ||
76 | struct task_struct *task, | ||
77 | int vm_write) | ||
78 | { | ||
79 | unsigned long pa = addr & PAGE_MASK; | ||
80 | unsigned long start_offset = addr - pa; | ||
81 | unsigned long nr_pages; | ||
82 | ssize_t rc = 0; | ||
83 | unsigned long max_pages_per_loop = PVM_MAX_KMALLOC_PAGES | ||
84 | / sizeof(struct pages *); | ||
85 | unsigned int flags = 0; | ||
86 | |||
87 | /* Work out address and page range required */ | ||
88 | if (len == 0) | ||
89 | return 0; | ||
90 | nr_pages = (addr + len - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1; | ||
91 | |||
92 | if (vm_write) | ||
93 | flags |= FOLL_WRITE; | ||
94 | |||
95 | while (!rc && nr_pages && iov_iter_count(iter)) { | ||
96 | int pinned_pages = min(nr_pages, max_pages_per_loop); | ||
97 | int locked = 1; | ||
98 | size_t bytes; | ||
99 | |||
100 | /* | ||
101 | * Get the pages we're interested in. We must | ||
102 | * access remotely because task/mm might not | ||
103 | * current/current->mm | ||
104 | */ | ||
105 | mmap_read_lock(mm); | ||
106 | pinned_pages = pin_user_pages_remote(mm, pa, pinned_pages, | ||
107 | flags, process_pages, | ||
108 | NULL, &locked); | ||
109 | if (locked) | ||
110 | mmap_read_unlock(mm); | ||
111 | if (pinned_pages <= 0) | ||
112 | return -EFAULT; | ||
113 | |||
114 | bytes = pinned_pages * PAGE_SIZE - start_offset; | ||
115 | if (bytes > len) | ||
116 | bytes = len; | ||
117 | |||
118 | rc = process_vm_rw_pages(process_pages, | ||
119 | start_offset, bytes, iter, | ||
120 | vm_write); | ||
121 | len -= bytes; | ||
122 | start_offset = 0; | ||
123 | nr_pages -= pinned_pages; | ||
124 | pa += pinned_pages * PAGE_SIZE; | ||
125 | |||
126 | /* If vm_write is set, the pages need to be made dirty: */ | ||
127 | unpin_user_pages_dirty_lock(process_pages, pinned_pages, | ||
128 | vm_write); | ||
129 | } | ||
130 | |||
131 | return rc; | ||
132 | } | ||
133 | |||
134 | /* Maximum number of entries for process pages array | ||
135 | which lives on stack */ | ||
136 | #define PVM_MAX_PP_ARRAY_COUNT 16 | ||
137 | |||
138 | /** | ||
139 | * process_vm_rw_core - core of reading/writing pages from task specified | ||
140 | * @pid: PID of process to read/write from/to | ||
141 | * @iter: where to copy to/from locally | ||
142 | * @rvec: iovec array specifying where to copy to/from in the other process | ||
143 | * @riovcnt: size of rvec array | ||
144 | * @flags: currently unused | ||
145 | * @vm_write: 0 if reading from other process, 1 if writing to other process | ||
146 | * | ||
147 | * Returns the number of bytes read/written or error code. May | ||
148 | * return less bytes than expected if an error occurs during the copying | ||
149 | * process. | ||
150 | */ | ||
151 | static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter, | ||
152 | const struct iovec *rvec, | ||
153 | unsigned long riovcnt, | ||
154 | unsigned long flags, int vm_write) | ||
155 | { | ||
156 | struct task_struct *task; | ||
157 | struct page *pp_stack[PVM_MAX_PP_ARRAY_COUNT]; | ||
158 | struct page **process_pages = pp_stack; | ||
159 | struct mm_struct *mm; | ||
160 | unsigned long i; | ||
161 | ssize_t rc = 0; | ||
162 | unsigned long nr_pages = 0; | ||
163 | unsigned long nr_pages_iov; | ||
164 | ssize_t iov_len; | ||
165 | size_t total_len = iov_iter_count(iter); | ||
166 | |||
167 | /* | ||
168 | * Work out how many pages of struct pages we're going to need | ||
169 | * when eventually calling get_user_pages | ||
170 | */ | ||
171 | for (i = 0; i < riovcnt; i++) { | ||
172 | iov_len = rvec[i].iov_len; | ||
173 | if (iov_len > 0) { | ||
174 | nr_pages_iov = ((unsigned long)rvec[i].iov_base | ||
175 | + iov_len) | ||
176 | / PAGE_SIZE - (unsigned long)rvec[i].iov_base | ||
177 | / PAGE_SIZE + 1; | ||
178 | nr_pages = max(nr_pages, nr_pages_iov); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | if (nr_pages == 0) | ||
183 | return 0; | ||
184 | |||
185 | if (nr_pages > PVM_MAX_PP_ARRAY_COUNT) { | ||
186 | /* For reliability don't try to kmalloc more than | ||
187 | 2 pages worth */ | ||
188 | process_pages = kmalloc(min_t(size_t, PVM_MAX_KMALLOC_PAGES, | ||
189 | sizeof(struct pages *)*nr_pages), | ||
190 | GFP_KERNEL); | ||
191 | |||
192 | if (!process_pages) | ||
193 | return -ENOMEM; | ||
194 | } | ||
195 | |||
196 | /* Get process information */ | ||
197 | task = find_get_task_by_vpid(pid); | ||
198 | if (!task) { | ||
199 | rc = -ESRCH; | ||
200 | goto free_proc_pages; | ||
201 | } | ||
202 | |||
203 | mm = mm_access(task, PTRACE_MODE_ATTACH_REALCREDS); | ||
204 | if (!mm || IS_ERR(mm)) { | ||
205 | rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; | ||
206 | /* | ||
207 | * Explicitly map EACCES to EPERM as EPERM is a more | ||
208 | * appropriate error code for process_vw_readv/writev | ||
209 | */ | ||
210 | if (rc == -EACCES) | ||
211 | rc = -EPERM; | ||
212 | goto put_task_struct; | ||
213 | } | ||
214 | |||
215 | for (i = 0; i < riovcnt && iov_iter_count(iter) && !rc; i++) | ||
216 | rc = process_vm_rw_single_vec( | ||
217 | (unsigned long)rvec[i].iov_base, rvec[i].iov_len, | ||
218 | iter, process_pages, mm, task, vm_write); | ||
219 | |||
220 | /* copied = space before - space after */ | ||
221 | total_len -= iov_iter_count(iter); | ||
222 | |||
223 | /* If we have managed to copy any data at all then | ||
224 | we return the number of bytes copied. Otherwise | ||
225 | we return the error code */ | ||
226 | if (total_len) | ||
227 | rc = total_len; | ||
228 | |||
229 | mmput(mm); | ||
230 | |||
231 | put_task_struct: | ||
232 | put_task_struct(task); | ||
233 | |||
234 | free_proc_pages: | ||
235 | if (process_pages != pp_stack) | ||
236 | kfree(process_pages); | ||
237 | return rc; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * process_vm_rw - check iovecs before calling core routine | ||
242 | * @pid: PID of process to read/write from/to | ||
243 | * @lvec: iovec array specifying where to copy to/from locally | ||
244 | * @liovcnt: size of lvec array | ||
245 | * @rvec: iovec array specifying where to copy to/from in the other process | ||
246 | * @riovcnt: size of rvec array | ||
247 | * @flags: currently unused | ||
248 | * @vm_write: 0 if reading from other process, 1 if writing to other process | ||
249 | * | ||
250 | * Returns the number of bytes read/written or error code. May | ||
251 | * return less bytes than expected if an error occurs during the copying | ||
252 | * process. | ||
253 | */ | ||
254 | static ssize_t process_vm_rw(pid_t pid, | ||
255 | const struct iovec __user *lvec, | ||
256 | unsigned long liovcnt, | ||
257 | const struct iovec __user *rvec, | ||
258 | unsigned long riovcnt, | ||
259 | unsigned long flags, int vm_write) | ||
260 | { | ||
261 | struct iovec iovstack_l[UIO_FASTIOV]; | ||
262 | struct iovec iovstack_r[UIO_FASTIOV]; | ||
263 | struct iovec *iov_l = iovstack_l; | ||
264 | struct iovec *iov_r = iovstack_r; | ||
265 | struct iov_iter iter; | ||
266 | ssize_t rc; | ||
267 | int dir = vm_write ? WRITE : READ; | ||
268 | |||
269 | if (flags != 0) | ||
270 | return -EINVAL; | ||
271 | |||
272 | /* Check iovecs */ | ||
273 | rc = import_iovec(dir, lvec, liovcnt, UIO_FASTIOV, &iov_l, &iter); | ||
274 | if (rc < 0) | ||
275 | return rc; | ||
276 | if (!iov_iter_count(&iter)) | ||
277 | goto free_iov_l; | ||
278 | iov_r = iovec_from_user(rvec, riovcnt, UIO_FASTIOV, iovstack_r, | ||
279 | in_compat_syscall()); | ||
280 | if (IS_ERR(iov_r)) { | ||
281 | rc = PTR_ERR(iov_r); | ||
282 | goto free_iov_l; | ||
283 | } | ||
284 | rc = process_vm_rw_core(pid, &iter, iov_r, riovcnt, flags, vm_write); | ||
285 | if (iov_r != iovstack_r) | ||
286 | kfree(iov_r); | ||
287 | free_iov_l: | ||
288 | kfree(iov_l); | ||
289 | return rc; | ||
290 | } | ||
291 | |||
292 | SYSCALL_DEFINE6(process_vm_readv, pid_t, pid, const struct iovec __user *, lvec, | ||
293 | unsigned long, liovcnt, const struct iovec __user *, rvec, | ||
294 | unsigned long, riovcnt, unsigned long, flags) | ||
295 | { | ||
296 | return process_vm_rw(pid, lvec, liovcnt, rvec, riovcnt, flags, 0); | ||
297 | } | ||
298 | |||
299 | SYSCALL_DEFINE6(process_vm_writev, pid_t, pid, | ||
300 | const struct iovec __user *, lvec, | ||
301 | unsigned long, liovcnt, const struct iovec __user *, rvec, | ||
302 | unsigned long, riovcnt, unsigned long, flags) | ||
303 | { | ||
304 | return process_vm_rw(pid, lvec, liovcnt, rvec, riovcnt, flags, 1); | ||
305 | } | ||