summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordillon <dillon@FreeBSD.org>2000-02-16 21:11:33 +0000
committerdillon <dillon@FreeBSD.org>2000-02-16 21:11:33 +0000
commit7a2987cf9410d129917e89db27af78911bd0fb60 (patch)
treee9fa93a9df696f57e919e1e8f97b841d07227b0b
parente8cfdc976f1f31ae86f30a656797e2820c9f439b (diff)
downloadFreeBSD-src-7a2987cf9410d129917e89db27af78911bd0fb60.zip
FreeBSD-src-7a2987cf9410d129917e89db27af78911bd0fb60.tar.gz
Fix null-pointer dereference crash when the system is intentionally
run out of KVM through a mmap()/fork() bomb that allocates hundreds of thousands of vm_map_entry structures. Add panic to make null-pointer dereference crash a little more verbose. Add a new sysctl, vm.max_proc_mmap, which specifies the maximum number of mmap()'d spaces (discrete vm_map_entry's in the process). The value defaults to around 9000 for a 128MB machine. The test is scaled for the number of processes sharing a vmspace (aka linux threads). Setting the value to 0 disables the feature. PR: kern/16573 Approved by: jkh
-rw-r--r--lib/libc/sys/mmap.23
-rw-r--r--sys/kern/kern_malloc.c3
-rw-r--r--sys/sys/kernel.h1
-rw-r--r--sys/vm/vm_kern.h1
-rw-r--r--sys/vm/vm_map.c8
-rw-r--r--sys/vm/vm_mmap.c46
6 files changed, 55 insertions, 7 deletions
diff --git a/lib/libc/sys/mmap.2 b/lib/libc/sys/mmap.2
index 1906e53..433f8b7 100644
--- a/lib/libc/sys/mmap.2
+++ b/lib/libc/sys/mmap.2
@@ -253,7 +253,8 @@ was not page-aligned. (See BUGS below.)
.Dv MAP_FIXED
was specified and the
.Fa addr
-parameter wasn't available.
+parameter wasn't available, or the system has reached the per-process mmap
+limit specified in the vm.max_proc_mmap sysctl.
.Dv MAP_ANON
was specified and insufficient memory was available.
.Sh "SEE ALSO"
diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c
index 8852859..8f9da1f 100644
--- a/sys/kern/kern_malloc.c
+++ b/sys/kern/kern_malloc.c
@@ -72,7 +72,8 @@ static struct kmembuckets bucket[MINBUCKET + 16];
static struct kmemusage *kmemusage;
static char *kmembase;
static char *kmemlimit;
-static u_int vm_kmem_size;
+
+u_int vm_kmem_size;
#ifdef INVARIANTS
/*
diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h
index eac0fe7..a93de9c 100644
--- a/sys/sys/kernel.h
+++ b/sys/sys/kernel.h
@@ -112,6 +112,7 @@ enum sysinit_sub_id {
SI_SUB_TUNABLES = 0x0700000, /* establish tunable values */
SI_SUB_VM = 0x1000000, /* virtual memory system init*/
SI_SUB_KMEM = 0x1800000, /* kernel memory*/
+ SI_SUB_KVM_RSRC = 0x1A00000, /* kvm operational limits*/
SI_SUB_CPU = 0x2000000, /* CPU resource(s)*/
SI_SUB_KLD = 0x2100000, /* KLD and module setup */
SI_SUB_INTRINSIC = 0x2200000, /* proc 0*/
diff --git a/sys/vm/vm_kern.h b/sys/vm/vm_kern.h
index 2f1fe76..a962c17 100644
--- a/sys/vm/vm_kern.h
+++ b/sys/vm/vm_kern.h
@@ -75,6 +75,7 @@ extern vm_map_t mb_map;
extern int mb_map_full;
extern vm_map_t clean_map;
extern vm_map_t exec_map;
+extern u_int vm_kmem_size;
extern vm_offset_t kernel_vm_end;
/* XXX - elsewhere? */
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index 3f5382e..7acaa51 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -284,7 +284,13 @@ static vm_map_entry_t
vm_map_entry_create(map)
vm_map_t map;
{
- return zalloc((map->system_map || !mapentzone) ? kmapentzone : mapentzone);
+ vm_map_entry_t new_entry;
+
+ new_entry = zalloc((map->system_map || !mapentzone) ?
+ kmapentzone : mapentzone);
+ if (new_entry == NULL)
+ panic("vm_map_entry_create: kernel resources exhausted");
+ return(new_entry);
}
/*
diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c
index bb106f3..b42239c 100644
--- a/sys/vm/vm_mmap.c
+++ b/sys/vm/vm_mmap.c
@@ -49,6 +49,7 @@
#include "opt_rlimit.h"
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/filedesc.h>
@@ -60,6 +61,7 @@
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/vmmeter.h>
+#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
@@ -72,6 +74,7 @@
#include <vm/vm_pageout.h>
#include <vm/vm_extern.h>
#include <vm/vm_page.h>
+#include <vm/vm_kern.h>
#ifndef _SYS_SYSPROTO_H_
struct sbrk_args {
@@ -79,6 +82,29 @@ struct sbrk_args {
};
#endif
+static int max_proc_mmap;
+SYSCTL_INT(_vm, OID_AUTO, max_proc_mmap, CTLFLAG_RW, &max_proc_mmap, 0, "");
+
+/*
+ * Set the maximum number of vm_map_entry structures per process. Roughly
+ * speaking vm_map_entry structures are tiny, so allowing them to eat 1/100
+ * of our KVM malloc space still results in generous limits. We want a
+ * default that is good enough to prevent the kernel running out of resources
+ * if attacked from compromised user account but generous enough such that
+ * multi-threaded processes are not unduly inconvenienced.
+ */
+
+static void vmmapentry_rsrc_init __P((void *));
+SYSINIT(vmmersrc, SI_SUB_KVM_RSRC, SI_ORDER_FIRST, vmmapentry_rsrc_init, NULL)
+
+static void
+vmmapentry_rsrc_init(dummy)
+ void *dummy;
+{
+ max_proc_mmap = vm_kmem_size / sizeof(struct vm_map_entry);
+ max_proc_mmap /= 100;
+}
+
/* ARGSUSED */
int
sbrk(p, uap)
@@ -171,6 +197,7 @@ mmap(p, uap)
int flags, error;
int disablexworkaround;
off_t pos;
+ struct vmspace *vms = p->p_vmspace;
addr = (vm_offset_t) uap->addr;
size = uap->len;
@@ -234,9 +261,9 @@ mmap(p, uap)
* location.
*/
else if (addr == 0 ||
- (addr >= round_page((vm_offset_t)p->p_vmspace->vm_taddr) &&
- addr < round_page((vm_offset_t)p->p_vmspace->vm_daddr + MAXDSIZ)))
- addr = round_page((vm_offset_t)p->p_vmspace->vm_daddr + MAXDSIZ);
+ (addr >= round_page((vm_offset_t)vms->vm_taddr) &&
+ addr < round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ)))
+ addr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
if (flags & MAP_ANON) {
/*
@@ -332,7 +359,18 @@ mmap(p, uap)
handle = (void *)vp;
}
}
- error = vm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot,
+
+ /*
+ * Do not allow more then a certain number of vm_map_entry structures
+ * per process. Scale with the number of rforks sharing the map
+ * to make the limit reasonable for threads.
+ */
+ if (max_proc_mmap &&
+ vms->vm_map.nentries >= max_proc_mmap * vms->vm_refcnt) {
+ return (ENOMEM);
+ }
+
+ error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot,
flags, handle, pos);
if (error == 0)
p->p_retval[0] = (register_t) (addr + pageoff);
OpenPOWER on IntegriCloud