Date: Mon, 15 Dec 2014 12:01:43 +0000 (UTC) From: Konstantin Belousov <kib@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r275800 - in head: lib/libc/sys sys/compat/freebsd32 sys/conf sys/kern sys/sys Message-ID: <201412151201.sBFC1hDH039340@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: kib Date: Mon Dec 15 12:01:42 2014 New Revision: 275800 URL: https://svnweb.freebsd.org/changeset/base/275800 Log: Add a facility for non-init process to declare itself the reaper of the orphaned descendants. Base of the API is modelled after the same feature from the DragonFlyBSD. Requested by: bapt Reviewed by: jilles (previous version) Tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 3 weeks Added: head/sys/kern/kern_procctl.c - copied, changed from r275798, head/sys/kern/sys_process.c Modified: head/lib/libc/sys/procctl.2 head/sys/compat/freebsd32/freebsd32.h head/sys/compat/freebsd32/freebsd32_misc.c head/sys/conf/files head/sys/kern/init_main.c head/sys/kern/kern_exit.c head/sys/kern/kern_fork.c head/sys/kern/sys_process.c head/sys/sys/proc.h head/sys/sys/procctl.h Modified: head/lib/libc/sys/procctl.2 ============================================================================== --- head/lib/libc/sys/procctl.2 Mon Dec 15 11:57:39 2014 (r275799) +++ head/lib/libc/sys/procctl.2 Mon Dec 15 12:01:42 2014 (r275800) @@ -2,6 +2,10 @@ .\" Written by: John H. Baldwin <jhb@FreeBSD.org> .\" All rights reserved. .\" +.\" Copyright (c) 2014 The FreeBSD Foundation +.\" Portions of this documentation were written by Konstantin Belousov +.\" under sponsorship from the FreeBSD Foundation. +.\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: @@ -25,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 19, 2013 +.Dd December 15, 2014 .Dt PROCCTL 2 .Os .Sh NAME @@ -67,7 +71,7 @@ The control request to perform is specif .Fa cmd argument. The following commands are supported: -.Bl -tag -width "Dv PROC_SPROTECT" +.Bl -tag -width "Dv PROC_REAP_GETPIDS" .It Dv PROC_SPROTECT Set process protection state. This is used to mark a process as protected from being killed if the system @@ -95,6 +99,174 @@ When used with mark all future child processes of each selected process as protected. Future child processes will also mark all of their future child processes. .El +.It Dv PROC_REAP_ACQUIRE +Acquires the reaper status for the current process. +The status means that orphaned children by the reaper descendants, +forked after the acquisition of the status, are reparented to the +reaper. +After the system initialization, +.Xr init 8 +is the default reaper. +.Pp +.It Dv PROC_REAP_RELEASE +Releases the reaper state fpr the current process. +The reaper of the current process becomes the new reaper of the +current process descendants. +.It Dv PROC_REAP_STATUS +Provides the information about the reaper of the specified process, +or the process itself, in case it is a reaper. +The +.Fa data +argument must point to the +.Vt "struct procctl_reaper_status" , +which if filled by the syscall on successfull return. +.Bd -literal +struct procctl_reaper_status { + u_int rs_flags; + u_int rs_children; + u_int rs_descendants; + pid_t rs_reaper; + pid_t rs_pid; +}; +.Ed +The +.Fa rs_flags +may have the following flags returned: +.Bl -tag -width "Dv REAPER_STATUS_REALINIT" +.It Dv REAPER_STATUS_OWNED +The specified process has acquired the reaper status and did not +released it. +When the flag is returned, the +.Fa id +pid identifies reaper, otherwise the +.Fa rs_reaper +field of the structure is the pid of the reaper for passed process id. +.It Dv REAPER_STATUS_REALINIT +The specified process is the root of the reaper tree, i.e. +.Xr init 8. +.El +The +.Fa rs_children +returns the number of the children of the reaper. +The +.Fa rs_descendants +returns the total number of descendants of the reaper, +not counting descendants of the reapers in the subtree. +The +.Fa rs_reaper +returns the reaper pid. +The +.Fa rs_pid +returns pid of some reaper child if there is any descendant. +.It Dv PROC_REAP_GETPIDS +Queries the list of descendants of the reaper of the specified process. +The request takes the pointer to +.Vt "struct procctl_reaper_pids" +as +.Fa data . +.Bd -literal +struct procctl_reaper_pids { + u_int rp_count; + struct procctl_reaper_pidinfo *rp_pids; +}; +.Ed +On call, the +.Fa rp_pids +must point to the array of +.Vt procctl_reaper_pidinfo +structures, to be filled on return, +and the +.Fa rp_count +must specify the size of the array, +no more than rp_count elements is filled by kernel. +.Pp +The +.Vt "struct procctl_reaper_pidinfo" +structure provides some information about one reaper' descendant. +Note that for the descendant which is not child, it is the subject +of usual race with process exiting and pid reuse. +.Bd -literal +struct procctl_reaper_pidinfo { + pid_t pi_pid; + pid_t pi_subtree; + u_int pi_flags; +}; +.Ed +The +.Fa pi_pid +is the process id of the descendant. +The +.Fa pi_subtree +provides the pid of the child of the reaper, which is (grand-)parent +of the process. +The +.Fa pi_flags +returns the following flags, further describing the descendant: +.Bl -tag -width "Dv REAPER_PIDINFO_VALID" +.It Dv REAPER_PIDINFO_VALID +Set for the +.Vt procctl_reaper_pidinfo +structure, which was filled by kernel. +Zero-filling the +.Fa rp_pids +array and testing the flag allows the caller to detect the end +of returned array. +.It Dv REAPER_PIDINFO_CHILD +The +.Fa pi_pid +is the direct child of the reaper. +.El +.It Dv PROC_REAP_KILL +Request to deliver a signal to some subset of descendants of the reaper. +The +.Fa data +must point to +.Vt procctl_reaper_kill +structure, which is used both for parameters and status return. +.Bd -literal +struct procctl_reaper_kill { + int rk_sig; + u_int rk_flags; + pid_t rk_subtree; + u_int rk_killed; + pid_t rk_fpid; +}; +.Ed +The +.Fa rk_sig +specifies the signal to be delivered. +Zero is not a valid signal number, unlike +.Xr kill 2 . +The +.Fa rk_flags +further directs the operation. +It is or-ed from the following flags: +.Bl -tag -width "Dv REAPER_KILL_CHILDREN" +.It Dv REAPER_KILL_CHILDREN +Deliver the specified signal only to direct children of the reaper. +.It Dv REAPER_KILL_SUBTREE +Deliver the specified signal only to descendants which were forked by +the direct child with pid specified in +.Fa rk_subtree . +.El +If no +.Dv REAPER_KILL_CHILDREN +and +.Dv REAPER_KILL_SUBTREE +flags are specified, all current descendants of the reaper are signalled. +.Pp +If signal was delivered to any process, the return value from the request +is zero. +In this case, +.Fa rk_killed +field is filled with the count of processes signalled. +The +.Fa rk_fpid +field is set to the pid of the first process for which signal +delivery failed, e.g. due to the permission problems. +If no such process exist, the +.Fa rk_fpid +is set to -1. .El .Sh RETURN VALUES If an error occurs, a value of -1 is returned and @@ -132,11 +304,48 @@ An invalid operation or flag was passed for a .Dv PROC_SPROTECT command. +.It Bq Er EPERM +The +.Fa idtype +argument is not equal to +.Dv P_PID , +or +.Fa id +is not equal to the pid of the calling process, for +.Dv PROC_REAP_ACQUIRE +or +.Dv PROC_REAP_RELEASE +requests. +.It Bq Er EINVAL +Invalid or undefined flags were passed to +.Dv PROC_REAP_KILL +request. +.It Bq Er EINVAL +Invalid or zero signal number was requested for +.Dv PROC_REAP_KILL +request. +.It Bq Er EINVAL +The +.Dv PROC_REAP_RELEASE +request was issued by the +.Xr init 8 +process. +.It Bq Er EBUSY +The +.Dv PROC_REAP_ACQUIRE +request was issued by the process which already acquired reaper status +and did not released it. .El .Sh SEE ALSO -.Xr ptrace 2 +.Xr kill 2 , +.Xr ptrace 2 , +.Xr wait 2 , +.Xr init 8 .Sh HISTORY The .Fn procctl function appeared in .Fx 10.0 . +Reaper facility was created based on the similar feature of Linux and +DragonflyBSD, and first appeared in +.Fx 10.2 . Modified: head/sys/compat/freebsd32/freebsd32.h ============================================================================== --- head/sys/compat/freebsd32/freebsd32.h Mon Dec 15 11:57:39 2014 (r275799) +++ head/sys/compat/freebsd32/freebsd32.h Mon Dec 15 12:01:42 2014 (r275800) @@ -390,4 +390,10 @@ struct kld32_file_stat { char pathname[MAXPATHLEN]; }; +struct procctl_reaper_pids32 { + u_int rp_count; + u_int rp_pad0[15]; + uint32_t rp_pids; +}; + #endif /* !_COMPAT_FREEBSD32_FREEBSD32_H_ */ Modified: head/sys/compat/freebsd32/freebsd32_misc.c ============================================================================== --- head/sys/compat/freebsd32/freebsd32_misc.c Mon Dec 15 11:57:39 2014 (r275799) +++ head/sys/compat/freebsd32/freebsd32_misc.c Mon Dec 15 12:01:42 2014 (r275800) @@ -2957,20 +2957,63 @@ int freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap) { void *data; - int error, flags; + union { + struct procctl_reaper_status rs; + struct procctl_reaper_pids rp; + struct procctl_reaper_kill rk; + } x; + union { + struct procctl_reaper_pids32 rp; + } x32; + int error, error1, flags; switch (uap->com) { case PROC_SPROTECT: error = copyin(PTRIN(uap->data), &flags, sizeof(flags)); - if (error) + if (error != 0) return (error); data = &flags; break; + case PROC_REAP_ACQUIRE: + case PROC_REAP_RELEASE: + if (uap->data != NULL) + return (EINVAL); + data = NULL; + break; + case PROC_REAP_STATUS: + data = &x.rs; + break; + case PROC_REAP_GETPIDS: + error = copyin(uap->data, &x32.rp, sizeof(x32.rp)); + if (error != 0) + return (error); + CP(x32.rp, x.rp, rp_count); + PTRIN_CP(x32.rp, x.rp, rp_pids); + data = &x.rp; + break; + case PROC_REAP_KILL: + error = copyin(uap->data, &x.rk, sizeof(x.rk)); + if (error != 0) + return (error); + data = &x.rk; + break; default: return (EINVAL); } - return (kern_procctl(td, uap->idtype, PAIR32TO64(id_t, uap->id), - uap->com, data)); + error = kern_procctl(td, uap->idtype, PAIR32TO64(id_t, uap->id), + uap->com, data); + switch (uap->com) { + case PROC_REAP_STATUS: + if (error == 0) + error = copyout(&x.rs, uap->data, sizeof(x.rs)); + break; + case PROC_REAP_KILL: + error1 = copyout(&x.rk, uap->data, sizeof(x.rk)); + if (error == 0) + error = error1; + break; + } + return (error); } int Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Mon Dec 15 11:57:39 2014 (r275799) +++ head/sys/conf/files Mon Dec 15 12:01:42 2014 (r275800) @@ -2987,6 +2987,7 @@ kern/kern_pmc.c standard kern/kern_poll.c optional device_polling kern/kern_priv.c standard kern/kern_proc.c standard +kern/kern_procctl.c standard kern/kern_prot.c standard kern/kern_racct.c standard kern/kern_rangelock.c standard Modified: head/sys/kern/init_main.c ============================================================================== --- head/sys/kern/init_main.c Mon Dec 15 11:57:39 2014 (r275799) +++ head/sys/kern/init_main.c Mon Dec 15 12:01:42 2014 (r275800) @@ -496,7 +496,8 @@ proc0_init(void *dummy __unused) prison0.pr_cpuset = cpuset_ref(td->td_cpuset); p->p_peers = 0; p->p_leader = p; - + p->p_reaper = p; + LIST_INIT(&p->p_reaplist); strncpy(p->p_comm, "kernel", sizeof (p->p_comm)); strncpy(td->td_name, "swapper", sizeof (td->td_name)); @@ -821,8 +822,11 @@ create_init(const void *udata __unused) KASSERT(initproc->p_pid == 1, ("create_init: initproc->p_pid != 1")); /* divorce init's credentials from the kernel's */ newcred = crget(); + sx_xlock(&proctree_lock); PROC_LOCK(initproc); initproc->p_flag |= P_SYSTEM | P_INMEM; + initproc->p_treeflag |= P_TREE_REAPER; + LIST_INSERT_HEAD(&initproc->p_reaplist, &proc0, p_reapsibling); oldcred = initproc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC @@ -833,6 +837,7 @@ create_init(const void *udata __unused) #endif initproc->p_ucred = newcred; PROC_UNLOCK(initproc); + sx_xunlock(&proctree_lock); crfree(oldcred); cred_update_thread(FIRST_THREAD_IN_PROC(initproc)); cpu_set_fork_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL); Modified: head/sys/kern/kern_exit.c ============================================================================== --- head/sys/kern/kern_exit.c Mon Dec 15 11:57:39 2014 (r275799) +++ head/sys/kern/kern_exit.c Mon Dec 15 12:01:42 2014 (r275800) @@ -123,6 +123,31 @@ proc_realparent(struct proc *child) return (parent); } +void +reaper_abandon_children(struct proc *p, bool exiting) +{ + struct proc *p1, *p2, *ptmp; + + sx_assert(&proctree_lock, SX_LOCKED); + KASSERT(p != initproc, ("reaper_abandon_children for initproc")); + if ((p->p_treeflag & P_TREE_REAPER) == 0) + return; + p1 = p->p_reaper; + LIST_FOREACH_SAFE(p2, &p->p_reaplist, p_reapsibling, ptmp) { + LIST_REMOVE(p2, p_reapsibling); + p2->p_reaper = p1; + p2->p_reapsubtree = p->p_reapsubtree; + LIST_INSERT_HEAD(&p1->p_reaplist, p2, p_reapsibling); + if (exiting && p2->p_pptr == p) { + PROC_LOCK(p2); + proc_reparent(p2, p1); + PROC_UNLOCK(p2); + } + } + KASSERT(LIST_EMPTY(&p->p_reaplist), ("p_reaplist not empty")); + p->p_treeflag &= ~P_TREE_REAPER; +} + static void clear_orphan(struct proc *p) { @@ -458,14 +483,14 @@ exit1(struct thread *td, int rv) sx_xlock(&proctree_lock); q = LIST_FIRST(&p->p_children); if (q != NULL) /* only need this if any child is S_ZOMB */ - wakeup(initproc); + wakeup(q->p_reaper); for (; q != NULL; q = nq) { nq = LIST_NEXT(q, p_sibling); PROC_LOCK(q); q->p_sigparent = SIGCHLD; if (!(q->p_flag & P_TRACED)) { - proc_reparent(q, initproc); + proc_reparent(q, q->p_reaper); } else { /* * Traced processes are killed since their existence @@ -473,7 +498,7 @@ exit1(struct thread *td, int rv) */ t = proc_realparent(q); if (t == p) { - proc_reparent(q, initproc); + proc_reparent(q, q->p_reaper); } else { PROC_LOCK(t); proc_reparent(q, t); @@ -562,7 +587,7 @@ exit1(struct thread *td, int rv) mtx_unlock(&p->p_pptr->p_sigacts->ps_mtx); pp = p->p_pptr; PROC_UNLOCK(pp); - proc_reparent(p, initproc); + proc_reparent(p, p->p_reaper); p->p_sigparent = SIGCHLD; PROC_LOCK(p->p_pptr); @@ -575,8 +600,8 @@ exit1(struct thread *td, int rv) } else mtx_unlock(&p->p_pptr->p_sigacts->ps_mtx); - if (p->p_pptr == initproc) - kern_psignal(p->p_pptr, SIGCHLD); + if (p->p_pptr == p->p_reaper || p->p_pptr == initproc) + childproc_exited(p); else if (p->p_sigparent != 0) { if (p->p_sigparent == SIGCHLD) childproc_exited(p); @@ -849,6 +874,8 @@ proc_reap(struct thread *td, struct proc LIST_REMOVE(p, p_list); /* off zombproc */ sx_xunlock(&allproc_lock); LIST_REMOVE(p, p_sibling); + reaper_abandon_children(p, true); + LIST_REMOVE(p, p_reapsibling); PROC_LOCK(p); clear_orphan(p); PROC_UNLOCK(p); Modified: head/sys/kern/kern_fork.c ============================================================================== --- head/sys/kern/kern_fork.c Mon Dec 15 11:57:39 2014 (r275799) +++ head/sys/kern/kern_fork.c Mon Dec 15 12:01:42 2014 (r275800) @@ -261,11 +261,21 @@ retry: * Scan the active and zombie procs to check whether this pid * is in use. Remember the lowest pid that's greater * than trypid, so we can avoid checking for a while. + * + * Avoid reuse of the process group id, session id or + * the reaper subtree id. Note that for process group + * and sessions, the amount of reserved pids is + * limited by process limit. For the subtree ids, the + * id is kept reserved only while there is a + * non-reaped process in the subtree, so amount of + * reserved pids is limited by process limit times + * two. */ p = LIST_FIRST(&allproc); again: for (; p != NULL; p = LIST_NEXT(p, p_list)) { while (p->p_pid == trypid || + p->p_reapsubtree == trypid || (p->p_pgrp != NULL && (p->p_pgrp->pg_id == trypid || (p->p_session != NULL && @@ -611,12 +621,20 @@ do_fork(struct thread *td, int flags, st * of init. This effectively disassociates the child from the * parent. */ - if (flags & RFNOWAIT) - pptr = initproc; - else + if ((flags & RFNOWAIT) != 0) { + pptr = p1->p_reaper; + p2->p_reaper = pptr; + } else { + p2->p_reaper = (p1->p_treeflag & P_TREE_REAPER) != 0 ? + p1 : p1->p_reaper; pptr = p1; + } p2->p_pptr = pptr; LIST_INSERT_HEAD(&pptr->p_children, p2, p_sibling); + LIST_INIT(&p2->p_reaplist); + LIST_INSERT_HEAD(&p2->p_reaper->p_reaplist, p2, p_reapsibling); + if (p2->p_reaper == p1) + p2->p_reapsubtree = p2->p_pid; sx_xunlock(&proctree_lock); /* Inform accounting that we have forked. */ Copied and modified: head/sys/kern/kern_procctl.c (from r275798, head/sys/kern/sys_process.c) ============================================================================== --- head/sys/kern/sys_process.c Mon Dec 15 11:05:53 2014 (r275798, copy source) +++ head/sys/kern/kern_procctl.c Mon Dec 15 12:01:42 2014 (r275800) @@ -1,6 +1,9 @@ /*- - * Copyright (c) 1994, Sean Eric Fagan - * All rights reserved. + * Copyright (c) 2014 John Baldwin + * Copyright (c) 2014 The FreeBSD Foundation + * + * Portions of this software were developed by Konstantin Belousov + * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,11 +13,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Sean Eric Fagan. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -32,1208 +30,18 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include "opt_compat.h" - #include <sys/param.h> #include <sys/systm.h> +#include <sys/capsicum.h> #include <sys/lock.h> #include <sys/mutex.h> -#include <sys/syscallsubr.h> -#include <sys/sysent.h> -#include <sys/sysproto.h> #include <sys/priv.h> #include <sys/proc.h> #include <sys/procctl.h> -#include <sys/vnode.h> -#include <sys/ptrace.h> -#include <sys/rwlock.h> #include <sys/sx.h> -#include <sys/malloc.h> -#include <sys/signalvar.h> - -#include <machine/reg.h> - -#include <security/audit/audit.h> - -#include <vm/vm.h> -#include <vm/pmap.h> -#include <vm/vm_extern.h> -#include <vm/vm_map.h> -#include <vm/vm_kern.h> -#include <vm/vm_object.h> -#include <vm/vm_page.h> -#include <vm/vm_param.h> - -#ifdef COMPAT_FREEBSD32 -#include <sys/procfs.h> -#include <compat/freebsd32/freebsd32_signal.h> - -struct ptrace_io_desc32 { - int piod_op; - uint32_t piod_offs; - uint32_t piod_addr; - uint32_t piod_len; -}; - -struct ptrace_vm_entry32 { - int pve_entry; - int pve_timestamp; - uint32_t pve_start; - uint32_t pve_end; - uint32_t pve_offset; - u_int pve_prot; - u_int pve_pathlen; - int32_t pve_fileid; - u_int pve_fsid; - uint32_t pve_path; -}; - -struct ptrace_lwpinfo32 { - lwpid_t pl_lwpid; /* LWP described. */ - int pl_event; /* Event that stopped the LWP. */ - int pl_flags; /* LWP flags. */ - sigset_t pl_sigmask; /* LWP signal mask */ - sigset_t pl_siglist; /* LWP pending signal */ - struct siginfo32 pl_siginfo; /* siginfo for signal */ - char pl_tdname[MAXCOMLEN + 1]; /* LWP name. */ - int pl_child_pid; /* New child pid */ -}; - -#endif - -/* - * Functions implemented using PROC_ACTION(): - * - * proc_read_regs(proc, regs) - * Get the current user-visible register set from the process - * and copy it into the regs structure (<machine/reg.h>). - * The process is stopped at the time read_regs is called. - * - * proc_write_regs(proc, regs) - * Update the current register set from the passed in regs - * structure. Take care to avoid clobbering special CPU - * registers or privileged bits in the PSL. - * Depending on the architecture this may have fix-up work to do, - * especially if the IAR or PCW are modified. - * The process is stopped at the time write_regs is called. - * - * proc_read_fpregs, proc_write_fpregs - * deal with the floating point register set, otherwise as above. - * - * proc_read_dbregs, proc_write_dbregs - * deal with the processor debug register set, otherwise as above. - * - * proc_sstep(proc) - * Arrange for the process to trap after executing a single instruction. - */ - -#define PROC_ACTION(action) do { \ - int error; \ - \ - PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); \ - if ((td->td_proc->p_flag & P_INMEM) == 0) \ - error = EIO; \ - else \ - error = (action); \ - return (error); \ -} while(0) - -int -proc_read_regs(struct thread *td, struct reg *regs) -{ - - PROC_ACTION(fill_regs(td, regs)); -} - -int -proc_write_regs(struct thread *td, struct reg *regs) -{ - - PROC_ACTION(set_regs(td, regs)); -} - -int -proc_read_dbregs(struct thread *td, struct dbreg *dbregs) -{ - - PROC_ACTION(fill_dbregs(td, dbregs)); -} - -int -proc_write_dbregs(struct thread *td, struct dbreg *dbregs) -{ - - PROC_ACTION(set_dbregs(td, dbregs)); -} - -/* - * Ptrace doesn't support fpregs at all, and there are no security holes - * or translations for fpregs, so we can just copy them. - */ -int -proc_read_fpregs(struct thread *td, struct fpreg *fpregs) -{ - - PROC_ACTION(fill_fpregs(td, fpregs)); -} - -int -proc_write_fpregs(struct thread *td, struct fpreg *fpregs) -{ - - PROC_ACTION(set_fpregs(td, fpregs)); -} - -#ifdef COMPAT_FREEBSD32 -/* For 32 bit binaries, we need to expose the 32 bit regs layouts. */ -int -proc_read_regs32(struct thread *td, struct reg32 *regs32) -{ - - PROC_ACTION(fill_regs32(td, regs32)); -} - -int -proc_write_regs32(struct thread *td, struct reg32 *regs32) -{ - - PROC_ACTION(set_regs32(td, regs32)); -} - -int -proc_read_dbregs32(struct thread *td, struct dbreg32 *dbregs32) -{ - - PROC_ACTION(fill_dbregs32(td, dbregs32)); -} - -int -proc_write_dbregs32(struct thread *td, struct dbreg32 *dbregs32) -{ - - PROC_ACTION(set_dbregs32(td, dbregs32)); -} - -int -proc_read_fpregs32(struct thread *td, struct fpreg32 *fpregs32) -{ - - PROC_ACTION(fill_fpregs32(td, fpregs32)); -} - -int -proc_write_fpregs32(struct thread *td, struct fpreg32 *fpregs32) -{ - - PROC_ACTION(set_fpregs32(td, fpregs32)); -} -#endif - -int -proc_sstep(struct thread *td) -{ - - PROC_ACTION(ptrace_single_step(td)); -} - -int -proc_rwmem(struct proc *p, struct uio *uio) -{ - vm_map_t map; - vm_offset_t pageno; /* page number */ - vm_prot_t reqprot; - int error, fault_flags, page_offset, writing; - - /* - * Assert that someone has locked this vmspace. (Should be - * curthread but we can't assert that.) This keeps the process - * from exiting out from under us until this operation completes. - */ - KASSERT(p->p_lock >= 1, ("%s: process %p (pid %d) not held", __func__, - p, p->p_pid)); - - /* - * The map we want... - */ - map = &p->p_vmspace->vm_map; - - /* - * If we are writing, then we request vm_fault() to create a private - * copy of each page. Since these copies will not be writeable by the - * process, we must explicity request that they be dirtied. - */ - writing = uio->uio_rw == UIO_WRITE; - reqprot = writing ? VM_PROT_COPY | VM_PROT_READ : VM_PROT_READ; - fault_flags = writing ? VM_FAULT_DIRTY : VM_FAULT_NORMAL; - - /* - * Only map in one page at a time. We don't have to, but it - * makes things easier. This way is trivial - right? - */ - do { - vm_offset_t uva; - u_int len; - vm_page_t m; - - uva = (vm_offset_t)uio->uio_offset; - - /* - * Get the page number of this segment. - */ - pageno = trunc_page(uva); - page_offset = uva - pageno; - - /* - * How many bytes to copy - */ - len = min(PAGE_SIZE - page_offset, uio->uio_resid); - - /* - * Fault and hold the page on behalf of the process. - */ - error = vm_fault_hold(map, pageno, reqprot, fault_flags, &m); - if (error != KERN_SUCCESS) { - if (error == KERN_RESOURCE_SHORTAGE) - error = ENOMEM; - else - error = EFAULT; - break; - } - - /* - * Now do the i/o move. - */ - error = uiomove_fromphys(&m, page_offset, len, uio); - - /* Make the I-cache coherent for breakpoints. */ - if (writing && error == 0) { - vm_map_lock_read(map); - if (vm_map_check_protection(map, pageno, pageno + - PAGE_SIZE, VM_PROT_EXECUTE)) - vm_sync_icache(map, uva, len); - vm_map_unlock_read(map); - } - - /* - * Release the page. - */ - vm_page_lock(m); - vm_page_unhold(m); - vm_page_unlock(m); - - } while (error == 0 && uio->uio_resid > 0); - - return (error); -} - -static int -ptrace_vm_entry(struct thread *td, struct proc *p, struct ptrace_vm_entry *pve) -{ - struct vattr vattr; - vm_map_t map; - vm_map_entry_t entry; - vm_object_t obj, tobj, lobj; - struct vmspace *vm; - struct vnode *vp; - char *freepath, *fullpath; - u_int pathlen; - int error, index; - - error = 0; - obj = NULL; - - vm = vmspace_acquire_ref(p); - map = &vm->vm_map; - vm_map_lock_read(map); - - do { - entry = map->header.next; - index = 0; - while (index < pve->pve_entry && entry != &map->header) { - entry = entry->next; - index++; - } - if (index != pve->pve_entry) { - error = EINVAL; - break; - } - while (entry != &map->header && - (entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0) { - entry = entry->next; - index++; - } - if (entry == &map->header) { - error = ENOENT; - break; - } - - /* We got an entry. */ - pve->pve_entry = index + 1; - pve->pve_timestamp = map->timestamp; - pve->pve_start = entry->start; - pve->pve_end = entry->end - 1; - pve->pve_offset = entry->offset; - pve->pve_prot = entry->protection; - - /* Backing object's path needed? */ - if (pve->pve_pathlen == 0) - break; - - pathlen = pve->pve_pathlen; - pve->pve_pathlen = 0; - - obj = entry->object.vm_object; - if (obj != NULL) - VM_OBJECT_RLOCK(obj); - } while (0); - - vm_map_unlock_read(map); - vmspace_free(vm); - - pve->pve_fsid = VNOVAL; - pve->pve_fileid = VNOVAL; - - if (error == 0 && obj != NULL) { - lobj = obj; - for (tobj = obj; tobj != NULL; tobj = tobj->backing_object) { - if (tobj != obj) - VM_OBJECT_RLOCK(tobj); - if (lobj != obj) - VM_OBJECT_RUNLOCK(lobj); - lobj = tobj; - pve->pve_offset += tobj->backing_object_offset; - } - vp = (lobj->type == OBJT_VNODE) ? lobj->handle : NULL; - if (vp != NULL) - vref(vp); - if (lobj != obj) - VM_OBJECT_RUNLOCK(lobj); - VM_OBJECT_RUNLOCK(obj); - - if (vp != NULL) { - freepath = NULL; - fullpath = NULL; - vn_fullpath(td, vp, &fullpath, &freepath); - vn_lock(vp, LK_SHARED | LK_RETRY); - if (VOP_GETATTR(vp, &vattr, td->td_ucred) == 0) { - pve->pve_fileid = vattr.va_fileid; - pve->pve_fsid = vattr.va_fsid; - } - vput(vp); - - if (fullpath != NULL) { - pve->pve_pathlen = strlen(fullpath) + 1; - if (pve->pve_pathlen <= pathlen) { - error = copyout(fullpath, pve->pve_path, - pve->pve_pathlen); - } else - error = ENAMETOOLONG; - } - if (freepath != NULL) - free(freepath, M_TEMP); - } - } - - return (error); -} - -#ifdef COMPAT_FREEBSD32 -static int -ptrace_vm_entry32(struct thread *td, struct proc *p, - struct ptrace_vm_entry32 *pve32) -{ - struct ptrace_vm_entry pve; - int error; - - pve.pve_entry = pve32->pve_entry; - pve.pve_pathlen = pve32->pve_pathlen; - pve.pve_path = (void *)(uintptr_t)pve32->pve_path; - - error = ptrace_vm_entry(td, p, &pve); - if (error == 0) { - pve32->pve_entry = pve.pve_entry; - pve32->pve_timestamp = pve.pve_timestamp; - pve32->pve_start = pve.pve_start; - pve32->pve_end = pve.pve_end; - pve32->pve_offset = pve.pve_offset; - pve32->pve_prot = pve.pve_prot; - pve32->pve_fileid = pve.pve_fileid; - pve32->pve_fsid = pve.pve_fsid; - } - - pve32->pve_pathlen = pve.pve_pathlen; - return (error); -} - -static void -ptrace_lwpinfo_to32(const struct ptrace_lwpinfo *pl, - struct ptrace_lwpinfo32 *pl32) -{ - - pl32->pl_lwpid = pl->pl_lwpid; - pl32->pl_event = pl->pl_event; - pl32->pl_flags = pl->pl_flags; - pl32->pl_sigmask = pl->pl_sigmask; - pl32->pl_siglist = pl->pl_siglist; - siginfo_to_siginfo32(&pl->pl_siginfo, &pl32->pl_siginfo); - strcpy(pl32->pl_tdname, pl->pl_tdname); - pl32->pl_child_pid = pl->pl_child_pid; -} -#endif /* COMPAT_FREEBSD32 */ - -/* - * Process debugging system call. *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201412151201.sBFC1hDH039340>