summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorMasao Uebayashi <uebayasi@cvs.openbsd.org>2014-12-05 04:12:49 +0000
committerMasao Uebayashi <uebayasi@cvs.openbsd.org>2014-12-05 04:12:49 +0000
commitc8525d7f58e30cc7e582a014d2ed096379ccbe9e (patch)
treeec1d9e9fd25511db56d8684473978444d773b6fb /sys/kern
parent70eb0676761b32588c52742bb357451ea0d0c95d (diff)
Introduce a new sysctl to retrieve VM map entries
This adds a new sysctl KERN_PROC_VMMAP, which returns an array of VM map entries of a specified process. This prevents debuggers from iterating vm_map_entry RB tree via kvm(3). The name KERN_PROC_VMMAP and struct kinfo_vmentry are chosen from the same function in FreeBSD. struct kinfo_vmentry is revised to reduce size, because OpenBSD does not keep track of filepaths. The semantic is also changed to return max buffer size as a hint, and start iteration at the specified base address. Much valuable input from deraadt@, guenther@, tedu@ OK tedu@ deraadt@
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_sysctl.c102
1 files changed, 101 insertions, 1 deletions
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
/*