procfs_mem.c revision 79224
1127474Stjr/*
2127474Stjr * Copyright (c) 1993 Jan-Simon Pendry
3127474Stjr * Copyright (c) 1993 Sean Eric Fagan
4127474Stjr * Copyright (c) 1993
5127474Stjr *	The Regents of the University of California.  All rights reserved.
6174990Sache *
7127474Stjr * This code is derived from software contributed to Berkeley by
8127474Stjr * Jan-Simon Pendry and Sean Eric Fagan.
9127474Stjr *
10127474Stjr * Redistribution and use in source and binary forms, with or without
11127474Stjr * modification, are permitted provided that the following conditions
12127474Stjr * are met:
13127474Stjr * 1. Redistributions of source code must retain the above copyright
14127474Stjr *    notice, this list of conditions and the following disclaimer.
15127474Stjr * 2. Redistributions in binary form must reproduce the above copyright
16127474Stjr *    notice, this list of conditions and the following disclaimer in the
17127474Stjr *    documentation and/or other materials provided with the distribution.
18127474Stjr * 3. All advertising materials mentioning features or use of this software
19127474Stjr *    must display the following acknowledgement:
20127474Stjr *	This product includes software developed by the University of
21174990Sache *	California, Berkeley and its contributors.
22127474Stjr * 4. Neither the name of the University nor the names of its contributors
23127474Stjr *    may be used to endorse or promote products derived from this software
24127474Stjr *    without specific prior written permission.
25127474Stjr *
26127474Stjr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27127474Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28127474Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29127474Stjr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30127474Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31127474Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32127474Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33127474Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34127474Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35127474Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36174990Sache * SUCH DAMAGE.
37127474Stjr *
38127474Stjr *	@(#)procfs_mem.c	8.5 (Berkeley) 6/15/94
39127474Stjr *
40127474Stjr * $FreeBSD: head/sys/fs/procfs/procfs_mem.c 79224 2001-07-04 16:20:28Z dillon $
41127474Stjr */
42127474Stjr
43127474Stjr/*
44127474Stjr * This is a lightly hacked and merged version
45127474Stjr * of sef's pread/pwrite functions
46174990Sache */
47127474Stjr
48127474Stjr#include <sys/param.h>
49127474Stjr#include <sys/systm.h>
50127474Stjr#include <sys/lock.h>
51127474Stjr#include <sys/mutex.h>
52127474Stjr#include <sys/proc.h>
53127474Stjr#include <sys/ptrace.h>
54127474Stjr#include <sys/user.h>
55127474Stjr#include <sys/vnode.h>
56127474Stjr
57127474Stjr#include <fs/procfs/procfs.h>
58127474Stjr
59127474Stjr#include <vm/vm.h>
60127474Stjr#include <vm/vm_param.h>
61127474Stjr#include <vm/pmap.h>
62127474Stjr#include <vm/vm_extern.h>
63127474Stjr#include <vm/vm_map.h>
64127474Stjr#include <vm/vm_kern.h>
65127474Stjr#include <vm/vm_object.h>
66127474Stjr#include <vm/vm_page.h>
67127474Stjr
68127474Stjrstatic int	procfs_rwmem __P((struct proc *curp,
69127474Stjr				  struct proc *p, struct uio *uio));
70127474Stjr
71127474Stjrstatic int
72127474Stjrprocfs_rwmem(curp, p, uio)
73127474Stjr	struct proc *curp;
74127474Stjr	struct proc *p;
75127474Stjr	struct uio *uio;
76127474Stjr{
77127474Stjr	int error;
78127474Stjr	int writing;
79127474Stjr	struct vmspace *vm;
80174990Sache	vm_map_t map;
81127474Stjr	vm_object_t object = NULL;
82127474Stjr	vm_offset_t pageno = 0;		/* page number */
83127474Stjr	vm_prot_t reqprot;
84127474Stjr	vm_offset_t kva;
85127474Stjr
86127474Stjr	GIANT_REQUIRED;
87127474Stjr
88127474Stjr	/*
89127474Stjr	 * if the vmspace is in the midst of being deallocated or the
90127474Stjr	 * process is exiting, don't try to grab anything.  The page table
91127474Stjr	 * usage in that process can be messed up.
92127474Stjr	 */
93127474Stjr	vm = p->p_vmspace;
94127474Stjr	if ((p->p_flag & P_WEXIT))
95127474Stjr		return EFAULT;
96127474Stjr	if (vm->vm_refcnt < 1)
97127474Stjr		return EFAULT;
98127474Stjr	++vm->vm_refcnt;
99127474Stjr	/*
100127474Stjr	 * The map we want...
101127474Stjr	 */
102127474Stjr	map = &vm->vm_map;
103
104	writing = uio->uio_rw == UIO_WRITE;
105	reqprot = writing ? (VM_PROT_WRITE | VM_PROT_OVERRIDE_WRITE) : VM_PROT_READ;
106
107	kva = kmem_alloc_pageable(kernel_map, PAGE_SIZE);
108
109	/*
110	 * Only map in one page at a time.  We don't have to, but it
111	 * makes things easier.  This way is trivial - right?
112	 */
113	do {
114		vm_map_t tmap;
115		vm_offset_t uva;
116		int page_offset;		/* offset into page */
117		vm_map_entry_t out_entry;
118		vm_prot_t out_prot;
119		boolean_t wired;
120		vm_pindex_t pindex;
121		u_int len;
122		vm_page_t m;
123
124		object = NULL;
125
126		uva = (vm_offset_t) uio->uio_offset;
127
128		/*
129		 * Get the page number of this segment.
130		 */
131		pageno = trunc_page(uva);
132		page_offset = uva - pageno;
133
134		/*
135		 * How many bytes to copy
136		 */
137		len = min(PAGE_SIZE - page_offset, uio->uio_resid);
138
139		/*
140		 * Fault the page on behalf of the process
141		 */
142		error = vm_fault(map, pageno, reqprot, VM_FAULT_NORMAL);
143		if (error) {
144			error = EFAULT;
145			break;
146		}
147
148		/*
149		 * Now we need to get the page.  out_entry, out_prot, wired,
150		 * and single_use aren't used.  One would think the vm code
151		 * would be a *bit* nicer...  We use tmap because
152		 * vm_map_lookup() can change the map argument.
153		 */
154		tmap = map;
155		error = vm_map_lookup(&tmap, pageno, reqprot,
156			      &out_entry, &object, &pindex, &out_prot,
157			      &wired);
158
159		if (error) {
160			error = EFAULT;
161
162			/*
163			 * Make sure that there is no residue in 'object' from
164			 * an error return on vm_map_lookup.
165			 */
166			object = NULL;
167
168			break;
169		}
170
171		m = vm_page_lookup(object, pindex);
172
173		/* Allow fallback to backing objects if we are reading */
174
175		while (m == NULL && !writing && object->backing_object) {
176
177		  pindex += OFF_TO_IDX(object->backing_object_offset);
178		  object = object->backing_object;
179
180		  m = vm_page_lookup(object, pindex);
181		}
182
183		if (m == NULL) {
184			error = EFAULT;
185
186			/*
187			 * Make sure that there is no residue in 'object' from
188			 * an error return on vm_map_lookup.
189			 */
190			object = NULL;
191
192			vm_map_lookup_done(tmap, out_entry);
193
194			break;
195		}
196
197		/*
198		 * Wire the page into memory
199		 */
200		vm_page_wire(m);
201
202		/*
203		 * We're done with tmap now.
204		 * But reference the object first, so that we won't loose
205		 * it.
206		 */
207		vm_object_reference(object);
208		vm_map_lookup_done(tmap, out_entry);
209
210		pmap_kenter(kva, VM_PAGE_TO_PHYS(m));
211
212		/*
213		 * Now do the i/o move.
214		 */
215		error = uiomove((caddr_t)(kva + page_offset), len, uio);
216
217		pmap_kremove(kva);
218
219		/*
220		 * release the page and the object
221		 */
222		vm_page_unwire(m, 1);
223		vm_object_deallocate(object);
224
225		object = NULL;
226
227	} while (error == 0 && uio->uio_resid > 0);
228
229	if (object)
230		vm_object_deallocate(object);
231
232	kmem_free(kernel_map, kva, PAGE_SIZE);
233	vmspace_free(vm);
234	return (error);
235}
236
237/*
238 * Copy data in and out of the target process.
239 * We do this by mapping the process's page into
240 * the kernel and then doing a uiomove direct
241 * from the kernel address space.
242 */
243int
244procfs_domem(curp, p, pfs, uio)
245	struct proc *curp;
246	struct proc *p;
247	struct pfsnode *pfs;
248	struct uio *uio;
249{
250
251	if (uio->uio_resid == 0)
252		return (0);
253
254 	/*
255 	 * XXX
256 	 * We need to check for KMEM_GROUP because ps is sgid kmem;
257 	 * not allowing it here causes ps to not work properly.  Arguably,
258 	 * this is a bug with what ps does.  We only need to do this
259 	 * for Pmem nodes, and only if it's reading.  This is still not
260 	 * good, as it may still be possible to grab illicit data if
261 	 * a process somehow gets to be KMEM_GROUP.  Note that this also
262 	 * means that KMEM_GROUP can't change without editing procfs.h!
263 	 * All in all, quite yucky.
264 	 */
265
266 	if (p_can(curp, p, P_CAN_DEBUG, NULL) &&
267	    !(uio->uio_rw == UIO_READ &&
268	      procfs_kmemaccess(curp)))
269 		return EPERM;
270
271	return (procfs_rwmem(curp, p, uio));
272}
273
274/*
275 * Given process (p), find the vnode from which
276 * its text segment is being executed.
277 *
278 * It would be nice to grab this information from
279 * the VM system, however, there is no sure-fire
280 * way of doing that.  Instead, fork(), exec() and
281 * wait() all maintain the p_textvp field in the
282 * process proc structure which contains a held
283 * reference to the exec'ed vnode.
284 *
285 * XXX - Currently, this is not not used, as the
286 * /proc/pid/file object exposes an information leak
287 * that shouldn't happen.  Using a mount option would
288 * make it configurable on a per-system (or, at least,
289 * per-mount) basis; however, that's not really best.
290 * The best way to do it, I think, would be as an
291 * ioctl; this would restrict it to the uid running
292 * program, or root, which seems a reasonable compromise.
293 * However, the number of applications for this is
294 * minimal, if it can't be seen in the filesytem space,
295 * and doint it as an ioctl makes it somewhat less
296 * useful due to the, well, inelegance.
297 *
298 */
299struct vnode *
300procfs_findtextvp(p)
301	struct proc *p;
302{
303
304	return (p->p_textvp);
305}
306
307int procfs_kmemaccess(curp)
308	struct proc *curp;
309{
310	int i;
311	struct ucred *cred;
312
313	cred = curp->p_ucred;
314	if (suser(curp))
315		return 1;
316
317	/* XXX: Why isn't this done with file-perms ??? */
318	for (i = 0; i < cred->cr_ngroups; i++)
319		if (cred->cr_groups[i] == KMEM_GROUP)
320			return 1;
321
322	return 0;
323}
324