/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */ /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */ /* Copyright (C) 2014 Stony Brook University This file is part of Graphene Library OS. Graphene Library OS is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Graphene Library OS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /* * shim_mmap.c * * Implementation of system call "mmap", "munmap" and "mprotect". */ #include #include #include #include #include #include #include #include #include #include DEFINE_PROFILE_OCCURENCE(mmap, memory); void * shim_do_mmap (void * addr, size_t length, int prot, int flags, int fd, off_t offset) { struct shim_handle * hdl = NULL; long ret = 0; /* * According to the manpage, both addr and offset have to be page-aligned, * but not the length. mmap() will automatically round up the length. */ if (addr && !ALIGNED(addr)) return (void *) -EINVAL; if (fd >= 0 && !ALIGNED(offset)) return (void *) -EINVAL; if (!ALIGNED(length)) length = ALIGN_UP(length); if (addr + length < addr) return (void *) -EINVAL; /* ignore MAP_32BIT when MAP_FIXED is set */ if ((flags & (MAP_32BIT|MAP_FIXED)) == (MAP_32BIT|MAP_FIXED)) flags &= ~MAP_32BIT; assert(!(flags & (VMA_UNMAPPED|VMA_TAINTED))); int pal_alloc_type = 0; if ((flags & MAP_FIXED) || addr) { struct shim_vma_val tmp; if (!lookup_overlap_vma(addr, length, &tmp)) { debug("mmap: allowing overlapping MAP_FIXED allocation at %p with length %lu\n", addr, length); if (!(flags & MAP_FIXED)) addr = NULL; } } if ((flags & (MAP_ANONYMOUS|MAP_FILE)) == MAP_FILE) { if (fd < 0) return (void *) -EINVAL; hdl = get_fd_handle(fd, NULL, NULL); if (!hdl) return (void *) -EBADF; if (!hdl->fs || !hdl->fs->fs_ops || !hdl->fs->fs_ops->mmap) { put_handle(hdl); return (void *) -ENODEV; } } if (addr) { bkeep_mmap(addr, length, prot, flags, hdl, offset, NULL); } else { addr = bkeep_unmapped_heap(length, prot, flags, hdl, offset, NULL); /* * Let the library OS manages the address space. If we can't find * proper space to allocate the memory, simply return failure. */ if (!addr) return (void *) -ENOMEM; } // Approximate check only, to help root out bugs. void * cur_stack = current_stack(); assert(cur_stack < addr || cur_stack > addr + length); if (!hdl) { addr = (void *) DkVirtualMemoryAlloc(addr, length, pal_alloc_type, PAL_PROT(prot, 0)); if (!addr) { if (PAL_NATIVE_ERRNO == PAL_ERROR_DENIED) ret = -EPERM; else ret = -PAL_ERRNO; } } else { ret = hdl->fs->fs_ops->mmap(hdl, &addr, length, PAL_PROT(prot, flags), flags, offset); } if (hdl) put_handle(hdl); if (ret < 0) { bkeep_munmap(addr, length, flags); return (void *) ret; } ADD_PROFILE_OCCURENCE(mmap, length); return addr; } int shim_do_mprotect (void * addr, size_t length, int prot) { /* * According to the manpage, addr has to be page-aligned, but not the * length. mprotect() will automatically round up the length. */ if (!addr || !ALIGNED(addr)) return -EINVAL; if (!ALIGNED(length)) length = ALIGN_UP(length); if (bkeep_mprotect(addr, length, prot, 0) < 0) return -EPERM; if (!DkVirtualMemoryProtect(addr, length, prot)) return -PAL_ERRNO; return 0; } int shim_do_munmap (void * addr, size_t length) { /* * According to the manpage, addr has to be page-aligned, but not the * length. munmap() will automatically round up the length. */ if (!addr || !ALIGNED(addr)) return -EINVAL; if (!ALIGNED(length)) length = ALIGN_UP(length); struct shim_vma_val vma; if (lookup_overlap_vma(addr, length, &vma) < 0) { debug("can't find addr %p - %p in map, quit unmapping\n", addr, addr + length); /* Really not an error */ return -EFAULT; } /* Protect first to make sure no overlapping with internal * mappings */ if (bkeep_mprotect(addr, length, PROT_NONE, 0) < 0) return -EPERM; DkVirtualMemoryFree(addr, length); if (bkeep_munmap(addr, length, 0) < 0) bug(); return 0; }