diff options
-rw-r--r-- | lib/libc/gen/sysctl.3 | 13 | ||||
-rw-r--r-- | sys/kern/kern_sysctl.c | 102 | ||||
-rw-r--r-- | sys/sys/sysctl.h | 50 | ||||
-rw-r--r-- | sys/uvm/uvm_extern.h | 7 | ||||
-rw-r--r-- | sys/uvm/uvm_glue.c | 17 | ||||
-rw-r--r-- | sys/uvm/uvm_map.c | 58 | ||||
-rw-r--r-- | sys/uvm/uvm_map.h | 7 |
7 files changed, 245 insertions, 9 deletions
diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index 62d8e7c698a..eb6a2acedcb 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sysctl.3,v 1.243 2014/11/19 18:04:54 tedu Exp $ +.\" $OpenBSD: sysctl.3,v 1.244 2014/12/05 04:12:48 uebayasi Exp $ .\" .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: November 19 2014 $ +.Dd $Mdocdate: December 5 2014 $ .Dt SYSCTL 3 .Os .Sh NAME @@ -443,6 +443,7 @@ information. .It Dv KERN_PROC_ARGS Ta "node" Ta "not applicable" .It Dv KERN_PROC_CWD Ta "string" Ta "not applicable" .It Dv KERN_PROC_NOBROADKILL Ta "string" Ta "not applicable" +.It Dv KERN_PROC_VMMAP Ta "struct kinfo_vmentry" Ta "no" .It Dv KERN_PROF Ta "node" Ta "not applicable" .It Dv KERN_RAWPARTITION Ta "integer" Ta "no" .It Dv KERN_RND Ta "struct rndstats" Ta "no" @@ -739,6 +740,14 @@ A NUL-terminated string is returned. .It Dv KERN_PROC_NOBROADKILL When set, a process will no longer be signaled when sending broadcast signals. The third level name is the target process ID. +.It Dv KERN_PROC_VMMAP +Return the entire process VM map entries. +An array of +.Li struct kinfo_vmentry +structures is returned, +whose size depends on the current number of VM map entries of the selected process. +Iteration is possible by setting the base address in the first element of +.Li struct kinfo_vmentry . .It Dv KERN_PROF Return profiling information about the kernel. If the kernel is not compiled for profiling, diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index 44d90ad07d3..b0bb84e35cd 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sysctl.c,v 1.272 2014/11/19 21:17:37 tedu Exp $ */ +/* $OpenBSD: kern_sysctl.c,v 1.273 2014/12/05 04:12:48 uebayasi Exp $ */ /* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */ /*- @@ -77,6 +77,8 @@ #include <sys/mount.h> #include <sys/syscallargs.h> +#include <uvm/uvm_extern.h> + #include <dev/cons.h> #include <dev/rndvar.h> #include <dev/systrace.h> @@ -117,6 +119,7 @@ int sysctl_proc_args(int *, u_int, void *, size_t *, struct proc *); int sysctl_proc_cwd(int *, u_int, void *, size_t *, struct proc *); int sysctl_proc_nobroadcastkill(int *, u_int, void *, size_t, void *, size_t *, struct proc *); +int sysctl_proc_vmmap(int *, u_int, void *, size_t *, struct proc *); int sysctl_intrcnt(int *, u_int, void *, size_t *); int sysctl_sensors(int *, u_int, void *, size_t *, void *, size_t); int sysctl_emul(int *, u_int, void *, size_t *, void *, size_t); @@ -276,6 +279,7 @@ kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, case KERN_PROC_ARGS: case KERN_PROC_CWD: case KERN_PROC_NOBROADCASTKILL: + case KERN_PROC_VMMAP: case KERN_SYSVIPC_INFO: case KERN_SEMINFO: case KERN_SHMINFO: @@ -365,6 +369,9 @@ kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, case KERN_PROC_NOBROADCASTKILL: return (sysctl_proc_nobroadcastkill(name + 1, namelen - 1, newp, newlen, oldp, oldlenp, p)); + case KERN_PROC_VMMAP: + return (sysctl_proc_vmmap(name + 1, namelen - 1, oldp, oldlenp, + p)); case KERN_FILE: return (sysctl_file(name + 1, namelen - 1, oldp, oldlenp, p)); #endif @@ -1838,6 +1845,99 @@ sysctl_proc_nobroadcastkill(int *name, u_int namelen, void *newp, size_t newlen, return (error); } + +/* Arbitrary but reasonable limit for one iteration. */ +#define VMMAP_MAXLEN MAXPHYS + +int +sysctl_proc_vmmap(int *name, u_int namelen, void *oldp, size_t *oldlenp, + struct proc *cp) +{ + struct process *findpr; + pid_t pid; + int error; + size_t oldlen, len; + struct kinfo_vmentry *kve, *ukve; + u_long *ustart, start; + + if (namelen > 1) + return (ENOTDIR); + if (namelen < 1) + return (EINVAL); + + /* Provide max buffer length as hint. */ + if (oldp == NULL) { + if (oldlenp == NULL) + return (EINVAL); + else { + *oldlenp = VMMAP_MAXLEN; + return (0); + } + } + + pid = name[0]; + if (pid > 0) { + if ((findpr = prfind(pid)) == NULL) + return (ESRCH); + + /* Either system process or exiting/zombie */ + if (findpr->ps_flags & (PS_SYSTEM | PS_EXITING)) + return (EINVAL); + + /* Only owner or root can get vmmap */ + if (findpr->ps_ucred->cr_uid != cp->p_ucred->cr_uid && + (error = suser(cp, 0)) != 0) + return (error); + } else { + /* Only root can get kernel_map */ + if ((error = suser(cp, 0)) != 0) + return (error); + findpr = NULL; + } + + /* Check the given size. */ + oldlen = *oldlenp; + if (oldlen == 0 || oldlen % sizeof(*kve) != 0) + return (EINVAL); + + /* Deny huge allocation. */ + if (oldlen > VMMAP_MAXLEN) + return (EINVAL); + + /* + * Iterate from the given address passed as the first element's + * kve_start via oldp. + */ + ukve = (struct kinfo_vmentry *)oldp; + ustart = &ukve->kve_start; + error = copyin(ustart, &start, sizeof(start)); + if (error != 0) + return (error); + + /* Allocate wired memory to not block. */ + kve = malloc(oldlen, M_TEMP, M_WAITOK); + + /* Set the base address and read entries. */ + kve[0].kve_start = start; + len = oldlen; + error = fill_vmmap(findpr, kve, &len); + if (error != 0 && error != ENOMEM) + goto done; + if (len == 0) + goto done; + + KASSERT(len <= oldlen); + KASSERT((len % sizeof(struct kinfo_vmentry)) == 0); + + error = copyout(kve, oldp, len); + +done: + *oldlenp = len; + + free(kve, M_TEMP, oldlen); + + return (error); +} #endif /* diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 52b3d2ca096..3b824fa7531 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sysctl.h,v 1.151 2014/11/19 18:04:54 tedu Exp $ */ +/* $OpenBSD: sysctl.h,v 1.152 2014/12/05 04:12:48 uebayasi Exp $ */ /* $NetBSD: sysctl.h,v 1.16 1996/04/09 20:55:36 cgd Exp $ */ /* @@ -180,7 +180,8 @@ struct ctlname { #define KERN_POOL_DEBUG 77 /* int: enable pool_debug */ #define KERN_PROC_CWD 78 /* node: proc cwd */ #define KERN_PROC_NOBROADCASTKILL 79 /* node: proc no broadcast kill */ -#define KERN_MAXID 80 /* number of valid kern ids */ +#define KERN_PROC_VMMAP 80 /* node: proc vmmap */ +#define KERN_MAXID 81 /* number of valid kern ids */ #define CTL_KERN_NAMES { \ { 0, 0 }, \ @@ -263,6 +264,7 @@ struct ctlname { { "pool_debug", CTLTYPE_INT }, \ { "proc_cwd", CTLTYPE_NODE }, \ { "proc_nobroadcastkill", CTLTYPE_NODE }, \ + { "proc_vmmap", CTLTYPE_NODE }, \ } /* @@ -437,6 +439,50 @@ struct kinfo_proc { u_int32_t p_rtableid; /* U_INT: Routing table identifier. */ }; +/* + * VM address range entry, matching struct vm_map_entry. Useful for + * debuggers to know process's addresses. + * + * To iterate entries, set the last kve_end as the base address into + * kve_start. + */ +struct kinfo_vmentry { + u_long kve_start; /* vaddr_t */ + u_long kve_end; /* vaddr_t */ + u_long kve_guard; /* vsize_t */ + u_long kve_fspace; /* vsize_t */ + u_long kve_fspace_augment; /* vsize_t */ + u_int64_t kve_offset; /* voff_t */ + int kve_wired_count; + int kve_etype; + int kve_protection; + int kve_max_protection; + int kve_advice; + int kve_inheritance; + u_int8_t kve_flags; /* u_int8_t */ +}; + +#define KVE_ET_OBJ 0x00000001 +#define KVE_ET_SUBMAP 0x00000002 +#define KVE_ET_COPYONWRITE 0x00000004 +#define KVE_ET_NEEDSCOPY 0x00000008 +#define KVE_ET_HOLE 0x00000010 +#define KVE_ET_NOFAULT 0x00000020 +#define KVE_ET_FREEMAPPED 0x00000080 +#define KVE_PROT_NONE 0x00000000 +#define KVE_PROT_READ 0x00000001 +#define KVE_PROT_WRITE 0x00000002 +#define KVE_PROT_EXEC 0x00000004 +#define KVE_ADV_NORMAL 0x00000000 +#define KVE_ADV_RANDOM 0x00000001 +#define KVE_ADV_SEQUENTIAL 0x00000002 +#define KVE_INH_SHARE 0x00000000 +#define KVE_INH_COPY 0x00000010 +#define KVE_INH_NONE 0x00000020 +#define KVE_INH_ZERO 0x00000030 +#define KVE_F_STATIC 0x01 +#define KVE_F_KMEM 0x02 + #if defined(_KERNEL) || defined(_LIBKVM) /* diff --git a/sys/uvm/uvm_extern.h b/sys/uvm/uvm_extern.h index 3727296d325..8656b4cdad4 100644 --- a/sys/uvm/uvm_extern.h +++ b/sys/uvm/uvm_extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_extern.h,v 1.124 2014/11/21 06:40:40 deraadt Exp $ */ +/* $OpenBSD: uvm_extern.h,v 1.125 2014/12/05 04:12:48 uebayasi Exp $ */ /* $NetBSD: uvm_extern.h,v 1.57 2001/03/09 01:02:12 chs Exp $ */ /* @@ -477,6 +477,11 @@ void kmeminit_nkmempages(void); void kmeminit(void); extern u_int nkmempages; +struct process; +struct kinfo_vmentry; +int fill_vmmap(struct process *, struct kinfo_vmentry *, + size_t *); + #endif /* _KERNEL */ #endif /* _UVM_UVM_EXTERN_H_ */ diff --git a/sys/uvm/uvm_glue.c b/sys/uvm/uvm_glue.c index 703cdd82b9e..7a9618aa496 100644 --- a/sys/uvm/uvm_glue.c +++ b/sys/uvm/uvm_glue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_glue.c,v 1.67 2014/11/16 12:31:00 deraadt Exp $ */ +/* $OpenBSD: uvm_glue.c,v 1.68 2014/12/05 04:12:48 uebayasi Exp $ */ /* $NetBSD: uvm_glue.c,v 1.44 2001/02/06 19:54:44 eeh Exp $ */ /* @@ -472,3 +472,18 @@ uvm_pause(void) if (curcpu()->ci_schedstate.spc_schedflags & SPCF_SHOULDYIELD) preempt(NULL); } + +#ifndef SMALL_KERNEL +int +fill_vmmap(struct process *pr, struct kinfo_vmentry *kve, + size_t *lenp) +{ + struct vm_map *map; + + if (pr != NULL) + map = &pr->ps_vmspace->vm_map; + else + map = kernel_map; + return uvm_map_fill_vmmap(map, kve, lenp); +} +#endif diff --git a/sys/uvm/uvm_map.c b/sys/uvm/uvm_map.c index f715d8463c6..fe2ee760d5c 100644 --- a/sys/uvm/uvm_map.c +++ b/sys/uvm/uvm_map.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_map.c,v 1.180 2014/11/30 19:50:53 deraadt Exp $ */ +/* $OpenBSD: uvm_map.c,v 1.181 2014/12/05 04:12:48 uebayasi Exp $ */ /* $NetBSD: uvm_map.c,v 1.86 2000/11/27 08:40:03 chs Exp $ */ /* @@ -91,6 +91,7 @@ #include <sys/malloc.h> #include <sys/pool.h> #include <sys/kernel.h> +#include <sys/sysctl.h> #ifdef SYSVSHM #include <sys/shm.h> @@ -4880,6 +4881,61 @@ vm_map_unbusy_ln(struct vm_map *map, char *file, int line) wakeup(&map->flags); } +#ifndef SMALL_KERNEL +int +uvm_map_fill_vmmap(struct vm_map *map, struct kinfo_vmentry *kve, + size_t *lenp) +{ + struct vm_map_entry *entry; + vaddr_t start; + int cnt, maxcnt, error = 0; + + KASSERT(*lenp > 0); + KASSERT((*lenp % sizeof(*kve)) == 0); + cnt = 0; + maxcnt = *lenp / sizeof(*kve); + KASSERT(maxcnt > 0); + + /* + * Return only entries whose address is above the given base + * address. This allows userland to iterate without knowing the + * number of entries beforehand. + */ + start = (vaddr_t)kve[0].kve_start; + + vm_map_lock(map); + RB_FOREACH(entry, uvm_map_addr, &map->addr) { + if (cnt == maxcnt) { + error = ENOMEM; + break; + } + if (start != 0 && entry->start < start) + continue; + kve->kve_start = entry->start; + kve->kve_end = entry->end; + kve->kve_guard = entry->guard; + kve->kve_fspace = entry->fspace; + kve->kve_fspace_augment = entry->fspace_augment; + kve->kve_offset = entry->offset; + kve->kve_wired_count = entry->wired_count; + kve->kve_etype = entry->etype; + kve->kve_protection = entry->protection; + kve->kve_max_protection = entry->max_protection; + kve->kve_advice = entry->advice; + kve->kve_inheritance = entry->inheritance; + kve->kve_flags = entry->flags; + kve++; + cnt++; + } + vm_map_unlock(map); + + KASSERT(cnt <= maxcnt); + + *lenp = sizeof(*kve) * cnt; + return error; +} +#endif + #undef RB_AUGMENT #define RB_AUGMENT(x) uvm_map_addr_augment((x)) diff --git a/sys/uvm/uvm_map.h b/sys/uvm/uvm_map.h index c69c5f9e6d3..70c23e8c56b 100644 --- a/sys/uvm/uvm_map.h +++ b/sys/uvm/uvm_map.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_map.h,v 1.51 2014/07/11 16:35:40 jsg Exp $ */ +/* $OpenBSD: uvm_map.h,v 1.52 2014/12/05 04:12:48 uebayasi Exp $ */ /* $NetBSD: uvm_map.h,v 1.24 2001/02/18 21:19:08 chs Exp $ */ /* @@ -400,6 +400,11 @@ void uvm_unmap_detach(struct uvm_map_deadq*, int); void uvm_unmap_remove(struct vm_map*, vaddr_t, vaddr_t, struct uvm_map_deadq*, boolean_t, boolean_t); +struct kinfo_vmentry; + +int uvm_map_fill_vmmap(struct vm_map *, struct kinfo_vmentry *, + size_t *); + #endif /* _KERNEL */ /* |