diff options
author | dillon <dillon@FreeBSD.org> | 2000-02-16 21:11:33 +0000 |
---|---|---|
committer | dillon <dillon@FreeBSD.org> | 2000-02-16 21:11:33 +0000 |
commit | 7a2987cf9410d129917e89db27af78911bd0fb60 (patch) | |
tree | e9fa93a9df696f57e919e1e8f97b841d07227b0b | |
parent | e8cfdc976f1f31ae86f30a656797e2820c9f439b (diff) | |
download | FreeBSD-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.2 | 3 | ||||
-rw-r--r-- | sys/kern/kern_malloc.c | 3 | ||||
-rw-r--r-- | sys/sys/kernel.h | 1 | ||||
-rw-r--r-- | sys/vm/vm_kern.h | 1 | ||||
-rw-r--r-- | sys/vm/vm_map.c | 8 | ||||
-rw-r--r-- | sys/vm/vm_mmap.c | 46 |
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); |