int mprotect(caddr_t addr, size_t len, int prot) { struct as *as = curproc->p_as; uint_t uprot = prot | PROT_USER; int error; if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) return (set_errno(EINVAL)); switch (valid_usr_range(addr, len, prot, as, as->a_userlimit)) { case RANGE_OKAY: break; case RANGE_BADPROT: return (set_errno(ENOTSUP)); case RANGE_BADADDR: default: return (set_errno(ENOMEM)); } error = as_setprot(as, addr, len, uprot); if (error) return (set_errno(error)); return (0); }
/* * Used for MAP_ANON - fast way to get anonymous pages */ static int zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags, offset_t pos) { struct segvn_crargs vn_a; int error; if (((PROT_ALL & uprot) != uprot)) return (EACCES); if ((flags & MAP_FIXED) != 0) { caddr_t userlimit; /* * Use the user address. First verify that * the address to be used is page aligned. * Then make some simple bounds checks. */ if (((uintptr_t)*addrp & PAGEOFFSET) != 0) return (EINVAL); userlimit = flags & _MAP_LOW32 ? (caddr_t)USERLIMIT32 : as->a_userlimit; switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { case RANGE_OKAY: break; case RANGE_BADPROT: return (ENOTSUP); case RANGE_BADADDR: default: return (ENOMEM); } } /* * No need to worry about vac alignment for anonymous * pages since this is a "clone" object that doesn't * yet exist. */ error = choose_addr(as, addrp, len, pos, ADDR_NOVACALIGN, flags); if (error != 0) { return (error); } /* * Use the seg_vn segment driver; passing in the NULL amp * gives the desired "cloning" effect. */ vn_a.vp = NULL; vn_a.offset = 0; vn_a.type = flags & MAP_TYPE; vn_a.prot = uprot; vn_a.maxprot = PROT_ALL; vn_a.flags = flags & ~MAP_TYPE; vn_a.cred = CRED(); vn_a.amp = NULL; vn_a.szc = 0; vn_a.lgrp_mem_policy_flags = 0; return (as_map(as, *addrp, len, segvn_create, &vn_a)); }
int mincore(caddr_t addr, size_t len, char *vecp) { struct as *as = curproc->p_as; caddr_t ea; /* end address of loop */ size_t rl; /* inner result length */ char vec[MC_CACHE]; /* local vector cache */ int error; model_t model; long llen; model = get_udatamodel(); /* * Validate form of address parameters. */ if (model == DATAMODEL_NATIVE) { llen = (long)len; } else { llen = (int32_t)(size32_t)len; } if (((uintptr_t)addr & PAGEOFFSET) != 0 || llen <= 0) return (set_errno(EINVAL)); if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) return (set_errno(ENOMEM)); /* * Loop over subranges of interval [addr : addr + len), recovering * results internally and then copying them out to caller. Subrange * is based on the size of MC_CACHE, defined above. */ for (ea = addr + len; addr < ea; addr += MC_QUANTUM) { error = as_incore(as, addr, (size_t)MIN(MC_QUANTUM, ea - addr), vec, &rl); if (rl != 0) { rl = (rl + PAGESIZE - 1) / PAGESIZE; if (copyout(vec, vecp, rl) != 0) return (set_errno(EFAULT)); vecp += rl; } if (error != 0) return (set_errno(ENOMEM)); } return (0); }
int munmap(caddr_t addr, size_t len) { struct proc *p = curproc; struct as *as = p->p_as; if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) return (set_errno(EINVAL)); if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) return (set_errno(EINVAL)); /* * Discard lwpchan mappings. */ if (p->p_lcp != NULL) lwpchan_delete_mapping(p, addr, addr + len); if (as_unmap(as, addr, len) != 0) return (set_errno(EINVAL)); return (0); }
static int xmem_map(struct vnode *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, struct cred *cred) { struct seg *seg; struct segxmem_crargs xmem_a; struct xmemnode *xp = (struct xmemnode *)VTOXN(vp); struct xmount *xm = (struct xmount *)VTOXM(vp); uint_t blocknumber; int error; #ifdef lint maxprot = maxprot; #endif if (vp->v_flag & VNOMAP) return (ENOSYS); if (off < 0) return (EINVAL); /* offset, length and address has to all be block aligned */ if (off & (xm->xm_bsize - 1) || len & (xm->xm_bsize - 1) || ((ulong_t)*addrp) & (xm->xm_bsize - 1)) { return (EINVAL); } if (vp->v_type != VREG) return (ENODEV); if (flags & MAP_PRIVATE) return (EINVAL); /* XXX need to be handled */ /* * Don't allow mapping to locked file */ if (vn_has_mandatory_locks(vp, xp->xn_mode)) { return (EAGAIN); } if (error = xmem_fillpages(xp, vp, off, len, 1)) { return (error); } blocknumber = off >> xm->xm_bshift; if (flags & MAP_FIXED) { /* * User specified address - blow away any previous mappings */ AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); seg = as_findseg(as, *addrp, 0); /* * Fast path. segxmem_remap will fail if this is the wrong * segment or if the len is beyond end of seg. If it fails, * we do the regular stuff thru as_* routines. */ if (seg && (segxmem_remap(seg, vp, *addrp, len, &xp->xn_ppa[blocknumber], prot) == 0)) { AS_LOCK_EXIT(as, &as->a_lock); return (0); } AS_LOCK_EXIT(as, &as->a_lock); if (seg) (void) as_unmap(as, *addrp, len); as_rangelock(as); error = valid_usr_range(*addrp, len, prot, as, as->a_userlimit); if (error != RANGE_OKAY || as_gap(as, len, addrp, &len, AH_CONTAIN, *addrp)) { as_rangeunlock(as); return (EINVAL); } } else { as_rangelock(as); map_addr(addrp, len, (offset_t)off, 1, flags); } if (*addrp == NULL) { as_rangeunlock(as); return (ENOMEM); } xmem_a.xma_vp = vp; xmem_a.xma_offset = (u_offset_t)off; xmem_a.xma_prot = prot; xmem_a.xma_cred = cred; xmem_a.xma_ppa = &xp->xn_ppa[blocknumber]; xmem_a.xma_bshift = xm->xm_bshift; error = as_map(as, *addrp, len, segxmem_create, &xmem_a); as_rangeunlock(as); return (error); }
static int smmap_common(caddr_t *addrp, size_t len, int prot, int flags, struct file *fp, offset_t pos) { struct vnode *vp; struct as *as = curproc->p_as; uint_t uprot, maxprot, type; int error; int in_crit = 0; if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | _MAP_NEW | _MAP_LOW32 | MAP_NORESERVE | MAP_ANON | MAP_ALIGN | MAP_TEXT | MAP_INITDATA)) != 0) { /* | MAP_RENAME */ /* not implemented, let user know */ return (EINVAL); } if ((flags & MAP_TEXT) && !(prot & PROT_EXEC)) { return (EINVAL); } if ((flags & (MAP_TEXT | MAP_INITDATA)) == (MAP_TEXT | MAP_INITDATA)) { return (EINVAL); } #if defined(__sparc) /* * See if this is an "old mmap call". If so, remember this * fact and convert the flags value given to mmap to indicate * the specified address in the system call must be used. * _MAP_NEW is turned set by all new uses of mmap. */ if ((flags & _MAP_NEW) == 0) flags |= MAP_FIXED; #endif flags &= ~_MAP_NEW; type = flags & MAP_TYPE; if (type != MAP_PRIVATE && type != MAP_SHARED) return (EINVAL); if (flags & MAP_ALIGN) { if (flags & MAP_FIXED) return (EINVAL); /* alignment needs to be a power of 2 >= page size */ if (((uintptr_t)*addrp < PAGESIZE && (uintptr_t)*addrp != 0) || !ISP2((uintptr_t)*addrp)) return (EINVAL); } /* * Check for bad lengths and file position. * We let the VOP_MAP routine check for negative lengths * since on some vnode types this might be appropriate. */ if (len == 0 || (pos & (u_offset_t)PAGEOFFSET) != 0) return (EINVAL); maxprot = PROT_ALL; /* start out allowing all accesses */ uprot = prot | PROT_USER; if (fp == NULL) { ASSERT(flags & MAP_ANON); /* discard lwpchan mappings, like munmap() */ if ((flags & MAP_FIXED) && curproc->p_lcp != NULL) lwpchan_delete_mapping(curproc, *addrp, *addrp + len); as_rangelock(as); error = zmap(as, addrp, len, uprot, flags, pos); as_rangeunlock(as); /* * Tell machine specific code that lwp has mapped shared memory */ if (error == 0 && (flags & MAP_SHARED)) { /* EMPTY */ LWP_MMODEL_SHARED_AS(*addrp, len); } return (error); } else if ((flags & MAP_ANON) != 0) return (EINVAL); vp = fp->f_vnode; /* Can't execute code from "noexec" mounted filesystem. */ if ((vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) maxprot &= ~PROT_EXEC; /* * These checks were added as part of large files. * * Return ENXIO if the initial position is negative; return EOVERFLOW * if (offset + len) would overflow the maximum allowed offset for the * type of file descriptor being used. */ if (vp->v_type == VREG) { if (pos < 0) return (ENXIO); if ((offset_t)len > (OFFSET_MAX(fp) - pos)) return (EOVERFLOW); } if (type == MAP_SHARED && (fp->f_flag & FWRITE) == 0) { /* no write access allowed */ maxprot &= ~PROT_WRITE; } /* * XXX - Do we also adjust maxprot based on protections * of the vnode? E.g. if no execute permission is given * on the vnode for the current user, maxprot probably * should disallow PROT_EXEC also? This is different * from the write access as this would be a per vnode * test as opposed to a per fd test for writability. */ /* * Verify that the specified protections are not greater than * the maximum allowable protections. Also test to make sure * that the file descriptor does allows for read access since * "write only" mappings are hard to do since normally we do * the read from the file before the page can be written. */ if (((maxprot & uprot) != uprot) || (fp->f_flag & FREAD) == 0) return (EACCES); /* * If the user specified an address, do some simple checks here */ if ((flags & MAP_FIXED) != 0) { caddr_t userlimit; /* * Use the user address. First verify that * the address to be used is page aligned. * Then make some simple bounds checks. */ if (((uintptr_t)*addrp & PAGEOFFSET) != 0) return (EINVAL); userlimit = flags & _MAP_LOW32 ? (caddr_t)USERLIMIT32 : as->a_userlimit; switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { case RANGE_OKAY: break; case RANGE_BADPROT: return (ENOTSUP); case RANGE_BADADDR: default: return (ENOMEM); } } if ((prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) && nbl_need_check(vp)) { int svmand; nbl_op_t nop; nbl_start_crit(vp, RW_READER); in_crit = 1; error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto done; if ((prot & PROT_WRITE) && (type == MAP_SHARED)) { if (prot & (PROT_READ | PROT_EXEC)) { nop = NBL_READWRITE; } else { nop = NBL_WRITE; } } else { nop = NBL_READ; } if (nbl_conflict(vp, nop, 0, LONG_MAX, svmand, NULL)) { error = EACCES; goto done; } } /* discard lwpchan mappings, like munmap() */ if ((flags & MAP_FIXED) && curproc->p_lcp != NULL) lwpchan_delete_mapping(curproc, *addrp, *addrp + len); /* * Ok, now let the vnode map routine do its thing to set things up. */ error = VOP_MAP(vp, pos, as, addrp, len, uprot, maxprot, flags, fp->f_cred, NULL); if (error == 0) { /* * Tell machine specific code that lwp has mapped shared memory */ if (flags & MAP_SHARED) { /* EMPTY */ LWP_MMODEL_SHARED_AS(*addrp, len); } if (vp->v_type == VREG && (flags & (MAP_TEXT | MAP_INITDATA)) != 0) { /* * Mark this as an executable vnode */ mutex_enter(&vp->v_lock); vp->v_flag |= VVMEXEC; mutex_exit(&vp->v_lock); } } done: if (in_crit) nbl_end_crit(vp); return (error); }