kern/59912: mremap() implementation lacking
Zachary Amsden
zach at mirapoint.com
Tue Dec 2 16:00:22 PST 2003
>Number: 59912
>Category: kern
>Synopsis: mremap() implementation lacking
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: update
>Submitter-Id: current-users
>Arrival-Date: Tue Dec 02 16:00:17 PST 2003
>Closed-Date:
>Last-Modified:
>Originator: Zachary Amsden
>Release: FreeBSD 4.2-RELEASE i386
>Organization:
Mirapoint
>Environment:
System: FreeBSD amsden.mirapoint.com 4.2-RELEASE FreeBSD 4.2-RELEASE #0:
Mon Nov 20 13:02:55 GMT 2000
jkh at bento.FreeBSD.org:/usr/src/sys/compile/GENERIC i386
>Description:
mremap() kernel implementation missing, may present Linux emulation
problems.
>How-To-Repeat:
>Fix:
Differences ...
--- sys/sys/mman.h Thu Mar 20 15:34:49 2003
+++ sys/sys/mman.h Mon Dec 16 17:29:04 2002
@@ -123,6 +123,11 @@
#define MINCORE_REFERENCED_OTHER 0x8 /* Page has been referenced */
#define MINCORE_MODIFIED_OTHER 0x10 /* Page has been modified */
+/*
+ * Flags for mremap
+ */
+#define MREMAP_MAYMOVE 0x100 /* Region may be moved in memory */
+
#ifndef _KERNEL
#include <sys/cdefs.h>
@@ -148,6 +153,7 @@
int mincore __P((const void *, size_t, char *));
int minherit __P((void *, size_t, int));
#endif
+caddr_t mremap __P((void *, size_t, size_t, unsigned long));
__END_DECLS
#endif /* !_KERNEL */
--- sys/kern/syscalls.master
+++ sys/kern/syscalls.master
@@ -522,3 +522,5 @@
struct kevent *eventlist, int nevents, \
const struct timespec *timeout); }
364 STD BSD { int settaskgroup (int group); }
+365 STD BSD { caddr_t mremap(void *old_address, size_t old_size, size_t new_size, \
+ unsigned long flags); }
--- sys/vm/vm_map.c
+++ sys/vm/vm_map.c
@@ -1011,6 +1011,219 @@
}
/*
+ * vm_map_extend:
+ *
+ * Attempt to extend a specified address range
+ *
+ */
+int
+vm_map_extend(map, start, end, newend, flags)
+ vm_map_t map;
+ vm_offset_t *start; /* IN/OUT */
+ vm_offset_t end;
+ vm_offset_t newend;
+ int flags;
+{
+ vm_map_entry_t new_entry;
+ vm_map_entry_t prev_entry;
+ vm_ooffset_t offset;
+ vm_offset_t addr;
+ vm_size_t len;
+ int result;
+ int cow;
+ vm_object_t object;
+
+ if (map == kmem_map || map == mb_map)
+ return (KERN_INVALID_ARGUMENT);
+
+ vm_map_lock(map);
+ addr = *start;
+
+ /*
+ * Check that the start and end points are not bogus.
+ */
+
+ if ((addr < map->min_offset) || (newend > map->max_offset) ||
+ (addr >= end) || (end > newend)) {
+ result = KERN_INVALID_ADDRESS;
+ goto err;
+ }
+
+ /*
+ * Find the entry based on the start address
+ */
+ if (!vm_map_lookup_entry(map, addr, &prev_entry))
+ prev_entry = prev_entry->next;
+
+ /*
+ * Ensure that the start and end occurs in the entry
+ */
+ if ((prev_entry == &map->header) || (prev_entry->end < end) ||
+ (prev_entry->start > addr)) {
+ result = KERN_INVALID_ADDRESS;
+ goto err;
+ }
+ object = prev_entry->object.vm_object;
+
+
+ /*
+ * Assert that the next entry doesn't overlap the new end point,
+ * and that the current entry ends at the specified region.
+ */
+ if (((prev_entry->next != &map->header) &&
+ (prev_entry->next->start < newend)) ||
+ (prev_entry->end > end)) {
+ /*
+ * If we are not allowed to move the range, fail
+ */
+ if ((flags & MREMAP_MAYMOVE) == 0) {
+ result = KERN_NO_SPACE;
+ goto err;
+ }
+
+ /*
+ * Reverse the eflags to COW arguments. Ugh.
+ */
+ cow = 0;
+ if ((prev_entry->eflags & MAP_ENTRY_COW) &&
+ (prev_entry->eflags & MAP_ENTRY_NEEDS_COPY))
+ cow |= MAP_COPY_ON_WRITE;
+ if (prev_entry->eflags & MAP_ENTRY_NOFAULT)
+ cow |= MAP_NOFAULT;
+ if (prev_entry->eflags & MAP_ENTRY_NOSYNC)
+ cow |= MAP_DISABLE_SYNCER;
+ if (prev_entry->eflags & MAP_ENTRY_NOCOREDUMP)
+ cow |= MAP_DISABLE_COREDUMP;
+
+ /*
+ * Search for a new range using the old address as a
+ * hint. Return address in start.
+ */
+ len = newend - addr;
+ *start = pmap_addr_hint(object, addr, len);
+ if (vm_map_findspace(map, *start, len, start)) {
+ result = KERN_NO_SPACE;
+ goto err;
+ }
+ result = vm_map_insert(map, object, prev_entry->offset,
+ *start, *start + len, prev_entry->protection,
+ prev_entry->max_protection, cow);
+ if (result == 0) {
+ vm_map_lookup_entry(map, *start + len, &new_entry);
+ if (!new_entry) {
+ /* Impossible */
+ vm_map_remove(map, *start, *start + len);
+ result = KERN_INVALID_ADDRESS;
+ goto err;
+ }
+ if (object)
+ vm_object_reference(object);
+ /*
+ * Found a new region to place this block. Copy
+ * the page map or fault the pages into place.
+ * We do this ourselves, since we don't want to
+ * trigger COW protection on the page - we are just
+ * relocating prev_entry. Deallocating the old map
+ * also must be done by hand.
+ *
+ * First, clip the old region out of the possible
+ * coalesced entry.
+ */
+ vm_map_clip_start(map, prev_entry, addr);
+ vm_map_clip_end(map, prev_entry, end);
+ if (prev_entry->wired_count == 0)
+ pmap_copy(map->pmap, map->pmap, new_entry->start,
+ len, prev_entry->start);
+ else {
+ vm_fault_copy_entry(map, map, new_entry, prev_entry);
+ vm_map_entry_unwire(map, prev_entry);
+ }
+ if ((object != kernel_object) &&
+ (object != kmem_object))
+ pmap_remove(map->pmap, prev_entry->start, prev_entry->end);
+ vm_map_entry_delete(map, prev_entry);
+ vm_map_simplify_entry(map, new_entry);
+ result = KERN_SUCCESS;
+ goto err;
+ } else {
+ result = KERN_NO_SPACE;
+ }
+ goto err;
+ }
+
+ offset = prev_entry->offset;
+ if ((prev_entry->wired_count == 0) &&
+ ((object == NULL) ||
+ vm_object_coalesce(object,
+ OFF_TO_IDX(prev_entry->offset),
+ (vm_size_t)(prev_entry->end - prev_entry->start),
+ (vm_size_t)(newend - prev_entry->end)))) {
+ /*
+ * We were able to extend the object. Determine if we
+ * can extend the previous map entry to include the
+ * new range as well.
+ */
+ if (prev_entry->inheritance == VM_INHERIT_DEFAULT) {
+ map->size += (newend - prev_entry->end);
+ prev_entry->end = newend;
+ result = KERN_SUCCESS;
+ goto err;
+ }
+ offset = prev_entry->offset +
+ (prev_entry->end - prev_entry->start);
+ }
+
+ /*
+ * If we couldn't extend the object or map for any reason,
+ * we are going to reuse the vm_object from the previous map
+ * entry, so refcount it.
+ */
+ if (object) {
+ vm_object_reference(object);
+ vm_object_clear_flag(object, OBJ_ONEMAPPING);
+ }
+
+ /*
+ * Create a new map entry
+ */
+
+ new_entry = vm_map_entry_create(map);
+ new_entry->start = end;
+ new_entry->end = newend;
+
+ new_entry->eflags = prev_entry->eflags;
+ new_entry->object.vm_object = prev_entry->object.vm_object;
+ new_entry->offset = offset;
+ new_entry->avail_ssize = 0;
+
+ new_entry->inheritance = VM_INHERIT_DEFAULT;
+ new_entry->protection = prev_entry->protection;
+ new_entry->max_protection = prev_entry->max_protection;
+ new_entry->wired_count = 0;
+
+ /*
+ * Insert the new entry into the list
+ */
+
+ vm_map_entry_link(map, prev_entry, new_entry);
+ map->size += new_entry->end - new_entry->start;
+
+ /*
+ * Update the free space hint
+ */
+ if ((map->first_free == prev_entry) &&
+ (prev_entry->end >= new_entry->start)) {
+ map->first_free = new_entry;
+ }
+ result = KERN_SUCCESS;
+
+err:
+ vm_map_unlock(map);
+
+ return (result);
+}
+
+/*
* vm_map_madvise:
*
* This routine traverses a processes map handling the madvise
--- sys/vm/vm_mmap.c
+++ sys/vm/vm_mmap.c
@@ -152,6 +152,93 @@
}
#endif /* COMPAT_43 || COMPAT_SUNOS */
+/*
+ * Memory remap (mremap) system call. Old address must be page
+ * aligned. If the MREMAP_MAYMOVE flag is specified, the pages
+ * may be automatically moved to a new location.
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct mremap_args {
+ void *old_address;
+ size_t old_size;
+ size_t new_size;
+ int flags;
+};
+#endif
+
+int
+mremap(p, uap)
+ struct proc *p;
+ register struct mremap_args *uap;
+{
+ vm_offset_t addr;
+ vm_size_t osize, nsize;
+ vm_map_t map;
+ int error;
+
+ addr = (vm_offset_t) uap->old_address;
+ /*
+ * Must be page aligned
+ */
+ if (trunc_page(addr) != addr)
+ return (EINVAL);
+
+ if (uap->flags & ~MREMAP_MAYMOVE)
+ return (EINVAL);
+
+ osize = round_page((vm_offset_t)uap->old_size);
+ nsize = round_page((vm_offset_t)uap->new_size);
+ if (osize == 0)
+ return (EINVAL);
+
+ /*
+ * Check for illegal addresses. Watch out for address wrap... Note
+ * that VM_*_ADDRESS are not constants due to casts (argh).
+ */
+ if (VM_MAXUSER_ADDRESS > 0 && addr + nsize > VM_MAXUSER_ADDRESS)
+ return (EINVAL);
+#ifndef i386
+ if (VM_MIN_ADDRESS > 0 && addr < VM_MIN_ADDRESS)
+ return (EINVAL);
+#endif
+
+ /*
+ * nothing to do
+ */
+ if (nsize == osize)
+ return (0);
+
+ map = &p->p_vmspace->vm_map;
+
+ /*
+ * Shrink case
+ */
+ if (nsize < osize) {
+ /*
+ * Make sure entire range is allocated.
+ */
+ if (!vm_map_check_protection(map, addr, addr + osize, VM_PROT_NONE))
+ return (EINVAL);
+ /* returns nothing but KERN_SUCCESS anyway */
+ (void) vm_map_remove(map, addr + nsize, addr + osize);
+ p->p_retval[0] = nsize ? (register_t) addr : 0;
+ return (0);
+ }
+
+ error = vm_map_extend(map, &addr, addr + osize, addr + nsize, uap->flags);
+ switch (error) {
+ case KERN_SUCCESS:
+ p->p_retval[0] = addr;
+ return (0);
+ case KERN_NO_SPACE:
+ return (ENOMEM);
+ case KERN_PROTECTION_FAILURE:
+ return (EACCES);
+ case KERN_INVALID_ADDRESS:
+ default:
+ return (EINVAL);
+ }
+}
/*
* Memory Map (mmap) system call. Note that the file offset
--- sys/vm/vm_map.h
+++ sys/vm/vm_map.h
@@ -356,6 +356,7 @@
int vm_map_inherit __P((vm_map_t, vm_offset_t, vm_offset_t, vm_inherit_t));
void vm_map_init __P((struct vm_map *, vm_offset_t, vm_offset_t));
int vm_map_insert __P((vm_map_t, vm_object_t, vm_ooffset_t,
vm_offset_t, vm_offset_t, vm_prot_t, vm
_prot_t, int));
+int vm_map_extend __P((vm_map_t, vm_offset_t *, vm_offset_t,
vm_offset_t, int));
int vm_map_lookup __P((vm_map_t *, vm_offset_t, vm_prot_t,
vm_map_entry_t *, vm_object_t *,
vm_pindex_t *, vm_prot_t *, boolean_t *));
void vm_map_lookup_done __P((vm_map_t, vm_map_entry_t));
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list