diff --git a/lib/libc/sys/mprotect.2 b/lib/libc/sys/mprotect.2 index 3be81c6582a..ca4d6307c44 100644 --- a/lib/libc/sys/mprotect.2 +++ b/lib/libc/sys/mprotect.2 @@ -28,7 +28,7 @@ .\" @(#)mprotect.2 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd August 3, 2016 +.Dd June 24, 2017 .Dt MPROTECT 2 .Os .Sh NAME @@ -80,10 +80,24 @@ and arguments is not valid. .It Bq Er EACCES The calling process was not allowed to change -the protection to the value specified by -the +the protection to the value specified by the .Fa prot argument. +.It Bq Er ENOMEM +The virtual address range specified by the +.Fa addr +and +.Fa len +arguments specify one or more pages which are not mapped. +.It Bq Er ENOMEM +The +.Fa prot +argument specifies +.Dv PROT_WRITE +on a +.Dv MAP_PRIVATE +mapping, and it would require more swap space than the system is able to +supply for the private pages. .El .Sh SEE ALSO .Xr madvise 2 , diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index 39cf303eaa0..706102a95bb 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -1490,11 +1490,11 @@ vm_map_fixed(vm_map_t map, vm_object_t object, vm_ooffset_t offset, * If object is non-NULL, ref count must be bumped by caller * prior to making call to account for the new entry. */ -int -vm_map_find(vm_map_t map, vm_object_t object, vm_ooffset_t offset, - vm_offset_t *addr, /* IN/OUT */ - vm_size_t length, vm_offset_t max_addr, int find_space, - vm_prot_t prot, vm_prot_t max, int cow) +static int +vm_map_find1(vm_map_t map, vm_object_t object, vm_ooffset_t offset, + vm_offset_t *addr, /* IN/OUT */ + vm_size_t length, vm_offset_t max_addr, int find_space, vm_prot_t prot, + vm_prot_t max, int cow) { vm_offset_t alignment, initial_addr, start; int result; @@ -1556,6 +1556,26 @@ again: return (result); } +int +vm_map_find(vm_map_t map, vm_object_t object, vm_ooffset_t offset, + vm_offset_t *addr, /* IN/OUT */ + vm_size_t length, vm_offset_t max_addr, int find_space, vm_prot_t prot, + vm_prot_t max, int cow) +{ + int rv; + bool hinted; + + hinted = *addr != 0; + for (;;) { + rv = vm_map_find1(map, object, offset, addr, length, max_addr, + find_space, prot, max, cow); + if (!hinted || rv == KERN_SUCCESS) + return (rv); + *addr = 0; + hinted = false; + } +} + /* * vm_map_simplify_entry: * @@ -1970,7 +1990,7 @@ vm_map_pmap_enter(vm_map_t map, vm_offset_t addr, vm_prot_t prot, */ int vm_map_protect(vm_map_t map, vm_offset_t start, vm_offset_t end, - vm_prot_t new_prot, boolean_t set_max) + vm_prot_t new_prot, boolean_t set_max) { vm_map_entry_t current, entry; vm_object_t obj; @@ -1995,7 +2015,8 @@ vm_map_protect(vm_map_t map, vm_offset_t start, vm_offset_t end, if (vm_map_lookup_entry(map, start, &entry)) { vm_map_clip_start(map, entry, start); } else { - entry = entry->next; + vm_map_unlock(map); + return (KERN_INVALID_ADDRESS); } /* @@ -2003,6 +2024,8 @@ vm_map_protect(vm_map_t map, vm_offset_t start, vm_offset_t end, */ for (current = entry; current != &map->header && current->start < end; current = current->next) { + if ((current->eflags & MAP_ENTRY_GUARD) != 0) + continue; if (current->eflags & MAP_ENTRY_IS_SUB_MAP) { vm_map_unlock(map); return (KERN_INVALID_ARGUMENT); @@ -2011,6 +2034,11 @@ vm_map_protect(vm_map_t map, vm_offset_t start, vm_offset_t end, vm_map_unlock(map); return (KERN_PROTECTION_FAILURE); } + if (current->end < end && (current->next == &map->header || + current->next->start > current->end)) { + vm_map_unlock(map); + return (KERN_INVALID_ADDRESS); + } } /* @@ -2712,9 +2740,9 @@ vm_map_wire(vm_map_t map, vm_offset_t start, vm_offset_t end, * If VM_MAP_WIRE_HOLESOK was specified, skip this check. */ next_entry: - if (((flags & VM_MAP_WIRE_HOLESOK) == 0) && - (entry->end < end && (entry->next == &map->header || - entry->next->start > entry->end))) { + if ((flags & VM_MAP_WIRE_HOLESOK) == 0 && + entry->end < end && (entry->next == &map->header || + entry->next->start > entry->end)) { end = entry->end; rv = KERN_INVALID_ADDRESS; goto done; @@ -3567,6 +3595,11 @@ out: return (rv); } +static int stack_guard_page = 1; +SYSCTL_INT(_security_bsd, OID_AUTO, stack_guard_page, CTLFLAG_RWTUN, + &stack_guard_page, 0, + "Specifies the number of guard pages for a stack that grows."); + static int vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize, vm_size_t growsize, vm_prot_t prot, vm_prot_t max, int cow) @@ -3574,7 +3607,7 @@ vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize, vm_map_entry_t new_entry, prev_entry; vm_offset_t bot, gap_bot, gap_top, top; vm_size_t init_ssize; - int orient, rv; + int orient, rv, sgp; /* * The stack orientation is piggybacked with the cow argument. @@ -3586,9 +3619,11 @@ vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize, KASSERT(orient != (MAP_STACK_GROWS_DOWN | MAP_STACK_GROWS_UP), ("bi-dir stack")); + sgp = stack_guard_page; if (addrbos < vm_map_min(map) || addrbos > vm_map_max(map) || - addrbos + max_ssize < addrbos) + addrbos + max_ssize < addrbos || + sgp * PAGE_SIZE >= max_ssize) return (KERN_NO_SPACE); init_ssize = (max_ssize < growsize) ? max_ssize : growsize; @@ -3599,12 +3634,6 @@ vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize, /* * If we can't accommodate max_ssize in the current mapping, no go. - * However, we need to be aware that subsequent user mappings might - * map into the space we have reserved for stack, and currently this - * space is not protected. - * - * Hopefully we will at least detect this condition when we try to - * grow the stack. */ if ((prev_entry->next != &map->header) && (prev_entry->next->start < addrbos + max_ssize)) @@ -3625,11 +3654,15 @@ vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize, top = bot + init_ssize; gap_bot = addrbos; gap_top = bot; + if (gap_top < gap_bot + sgp * PAGE_SIZE) + bot = gap_top = gap_bot + sgp * PAGE_SIZE; } else /* if (orient == MAP_STACK_GROWS_UP) */ { bot = addrbos; top = bot + init_ssize; gap_bot = top; gap_top = addrbos + max_ssize; + if (gap_top < gap_bot + sgp * PAGE_SIZE) + top = gap_bot = gap_top - sgp * PAGE_SIZE; } rv = vm_map_insert(map, NULL, 0, bot, top, prot, max, cow); if (rv != KERN_SUCCESS) @@ -3651,11 +3684,6 @@ vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize, return (rv); } -static int stack_guard_page = 1; -SYSCTL_INT(_security_bsd, OID_AUTO, stack_guard_page, CTLFLAG_RWTUN, - &stack_guard_page, 0, - "Specifies the number of guard pages for a stack that grows."); - /* * Attempts to grow a vm stack entry. Returns KERN_SUCCESS if we * successfully grow the stack. diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index 4d8f6ad9ed7..09912250d52 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -607,6 +607,7 @@ kern_mprotect(struct thread *td, uintptr_t addr0, size_t size, int prot) case KERN_PROTECTION_FAILURE: return (EACCES); case KERN_RESOURCE_SHORTAGE: + case KERN_INVALID_ADDRESS: return (ENOMEM); } return (EINVAL);