diff options
author | Patrick Mooney <pmooney@pfmooney.com> | 2018-01-22 21:29:22 +0000 |
---|---|---|
committer | Patrick Mooney <pmooney@pfmooney.com> | 2018-01-26 16:15:00 +0000 |
commit | b8fef42ed91e3b916b058421d1783c5cba2a73bf (patch) | |
tree | a24d83f65f1f08285f5213f8c4e2f5a4d6234533 | |
parent | 65ca2e35d394f5308fbe6b2b05c65e86e9ca098b (diff) | |
download | illumos-joyent-b8fef42ed91e3b916b058421d1783c5cba2a73bf.tar.gz |
OS-6564 segvn concatenation causes use-after-free
Reviewed by: Mike Gerdts <mike.gerdts@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r-- | usr/src/uts/common/fs/ufs/ufs_vnops.c | 3 | ||||
-rw-r--r-- | usr/src/uts/common/vm/as.h | 10 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_dev.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_dev.h | 3 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_hole.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_hole.h | 6 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_spt.c | 10 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_spt.h | 5 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_umap.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_umap.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_vn.c | 43 | ||||
-rw-r--r-- | usr/src/uts/common/vm/seg_vn.h | 8 | ||||
-rw-r--r-- | usr/src/uts/common/vm/vm_as.c | 107 | ||||
-rw-r--r-- | usr/src/uts/i86xpv/vm/seg_mf.c | 4 | ||||
-rw-r--r-- | usr/src/uts/i86xpv/vm/seg_mf.h | 3 |
15 files changed, 132 insertions, 88 deletions
diff --git a/usr/src/uts/common/fs/ufs/ufs_vnops.c b/usr/src/uts/common/fs/ufs/ufs_vnops.c index 48ad30d3db..10039536eb 100644 --- a/usr/src/uts/common/fs/ufs/ufs_vnops.c +++ b/usr/src/uts/common/fs/ufs/ufs_vnops.c @@ -21,7 +21,7 @@ /* * Copyright (c) 1984, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017 Joyent, Inc. + * Copyright 2018 Joyent, Inc. * Copyright (c) 2016 by Delphix. All rights reserved. */ @@ -183,7 +183,6 @@ static int ufs_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *, static int ufs_priv_access(void *, int, struct cred *); static int ufs_eventlookup(struct vnode *, char *, struct cred *, struct vnode **); -extern int as_map_locked(struct as *, caddr_t, size_t, int ((*)()), void *); /* * For lockfs: ulockfs begin/end is now inlined in the ufs_xxx functions. diff --git a/usr/src/uts/common/vm/as.h b/usr/src/uts/common/vm/as.h index e910db1ddc..83bd7b52ba 100644 --- a/usr/src/uts/common/vm/as.h +++ b/usr/src/uts/common/vm/as.h @@ -24,7 +24,7 @@ */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2018 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -256,6 +256,8 @@ extern struct as kas; /* kernel's address space */ #define AS_SEGNEXT(as, seg) AVL_NEXT(&(as)->a_segtree, (seg)) #define AS_SEGPREV(as, seg) AVL_PREV(&(as)->a_segtree, (seg)) +typedef int (*segcreate_func_t)(struct seg **, void *); + void as_init(void); void as_avlinit(struct as *); struct seg *as_segat(struct as *as, caddr_t addr); @@ -273,8 +275,10 @@ faultcode_t as_faulta(struct as *as, caddr_t addr, size_t size); int as_setprot(struct as *as, caddr_t addr, size_t size, uint_t prot); int as_checkprot(struct as *as, caddr_t addr, size_t size, uint_t prot); int as_unmap(struct as *as, caddr_t addr, size_t size); -int as_map(struct as *as, caddr_t addr, size_t size, int ((*crfp)()), - void *argsp); +int as_map(struct as *as, caddr_t addr, size_t size, segcreate_func_t crfp, + void *argsp); +int as_map_locked(struct as *as, caddr_t addr, size_t size, + segcreate_func_t crfp, void *argsp); void as_purge(struct as *as); int as_gap(struct as *as, size_t minlen, caddr_t *basep, size_t *lenp, uint_t flags, caddr_t addr); diff --git a/usr/src/uts/common/vm/seg_dev.c b/usr/src/uts/common/vm/seg_dev.c index f43a288cec..89e6461bbf 100644 --- a/usr/src/uts/common/vm/seg_dev.c +++ b/usr/src/uts/common/vm/seg_dev.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2018 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -357,8 +358,9 @@ devmap_ctxto(void *data) * Create a device segment. */ int -segdev_create(struct seg *seg, void *argsp) +segdev_create(struct seg **segpp, void *argsp) { + struct seg *seg = *segpp; struct segdev_data *sdp; struct segdev_crargs *a = (struct segdev_crargs *)argsp; devmap_handle_t *dhp = (devmap_handle_t *)a->devmap_data; diff --git a/usr/src/uts/common/vm/seg_dev.h b/usr/src/uts/common/vm/seg_dev.h index 6240125489..07a15afc6b 100644 --- a/usr/src/uts/common/vm/seg_dev.h +++ b/usr/src/uts/common/vm/seg_dev.h @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2018 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -122,7 +123,7 @@ struct devmap_pmem_cookie { extern void segdev_init(void); -extern int segdev_create(struct seg *, void *); +extern int segdev_create(struct seg **, void *); extern int segdev_copyto(struct seg *, caddr_t, const void *, void *, size_t); extern int segdev_copyfrom(struct seg *, caddr_t, const void *, void *, size_t); diff --git a/usr/src/uts/common/vm/seg_hole.c b/usr/src/uts/common/vm/seg_hole.c index a716c270cf..e0dbee34e6 100644 --- a/usr/src/uts/common/vm/seg_hole.c +++ b/usr/src/uts/common/vm/seg_hole.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2017 Joyent, Inc. + * Copyright 2018 Joyent, Inc. */ @@ -82,8 +82,9 @@ static struct seg_ops seghole_ops = { * Create a hole in the AS. */ int -seghole_create(struct seg *seg, void *argsp) +seghole_create(struct seg **segpp, void *argsp) { + struct seg *seg = *segpp; seghole_crargs_t *crargs = argsp; seghole_data_t *data; diff --git a/usr/src/uts/common/vm/seg_hole.h b/usr/src/uts/common/vm/seg_hole.h index fb48a057e0..2bff880f4f 100644 --- a/usr/src/uts/common/vm/seg_hole.h +++ b/usr/src/uts/common/vm/seg_hole.h @@ -10,7 +10,7 @@ */ /* - * Copyright 2017 Joyent, Inc. + * Copyright 2018 Joyent, Inc. */ #ifndef _VM_SEG_HOLE_H @@ -28,10 +28,10 @@ typedef struct seghole_data { const char *shd_name; } seghole_data_t; -extern int seghole_create(struct seg *, void *); +extern int seghole_create(struct seg **, void *); #define AS_MAP_CHECK_SEGHOLE(crfp) \ - ((crfp) == (int (*)())seghole_create) + ((crfp) == (segcreate_func_t)seghole_create) #ifdef __cplusplus } diff --git a/usr/src/uts/common/vm/seg_spt.c b/usr/src/uts/common/vm/seg_spt.c index b0f992b7a6..cc00e16333 100644 --- a/usr/src/uts/common/vm/seg_spt.c +++ b/usr/src/uts/common/vm/seg_spt.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015, Joyent, Inc. All rights reserved. + * Copyright 2018 Joyent, Inc. * Copyright (c) 2016 by Delphix. All rights reserved. */ @@ -72,7 +72,7 @@ size_t spt_used; */ pgcnt_t segspt_minfree = 0; -static int segspt_create(struct seg *seg, caddr_t argsp); +static int segspt_create(struct seg **segpp, void *argsp); static int segspt_unmap(struct seg *seg, caddr_t raddr, size_t ssize); static void segspt_free(struct seg *seg); static void segspt_free_pages(struct seg *seg, caddr_t addr, size_t len); @@ -369,8 +369,9 @@ segspt_unmap(struct seg *seg, caddr_t raddr, size_t ssize) } int -segspt_create(struct seg *seg, caddr_t argsp) +segspt_create(struct seg **segpp, void *argsp) { + struct seg *seg = *segpp; int err; caddr_t addr = seg->s_base; struct spt_data *sptd; @@ -1671,8 +1672,9 @@ softlock_decrement: } int -segspt_shmattach(struct seg *seg, caddr_t *argsp) +segspt_shmattach(struct seg **segpp, void *argsp) { + struct seg *seg = *segpp; struct shm_data *shmd_arg = (struct shm_data *)argsp; struct shm_data *shmd; struct anon_map *shm_amp = shmd_arg->shm_amp; diff --git a/usr/src/uts/common/vm/seg_spt.h b/usr/src/uts/common/vm/seg_spt.h index ebc2ebf465..d8cea0a4d0 100644 --- a/usr/src/uts/common/vm/seg_spt.h +++ b/usr/src/uts/common/vm/seg_spt.h @@ -21,13 +21,12 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2018 Joyent, Inc. */ #ifndef _VM_SEG_SPT_H #define _VM_SEG_SPT_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -101,7 +100,7 @@ typedef struct shm_data { int sptcreate(size_t size, struct seg **sptseg, struct anon_map *amp, uint_t prot, uint_t flags, uint_t szc); void sptdestroy(struct as *, struct anon_map *); -int segspt_shmattach(struct seg *, caddr_t *); +int segspt_shmattach(struct seg **, void *); #define isspt(sp) ((sp)->shm_sptinfo ? (sp)->shm_sptinfo->sptas : NULL) #define spt_locked(a) ((a) & SHM_SHARE_MMU) diff --git a/usr/src/uts/common/vm/seg_umap.c b/usr/src/uts/common/vm/seg_umap.c index 3b4bb04f69..985cb51759 100644 --- a/usr/src/uts/common/vm/seg_umap.c +++ b/usr/src/uts/common/vm/seg_umap.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2016 Joyent, Inc. + * Copyright 2018 Joyent, Inc. */ /* @@ -99,8 +99,9 @@ static struct seg_ops segumap_ops = { * Create a kernel/user-mapped segment. */ int -segumap_create(struct seg *seg, void *argsp) +segumap_create(struct seg **segpp, void *argsp) { + struct seg *seg = *segpp; segumap_crargs_t *a = (struct segumap_crargs *)argsp; segumap_data_t *data; diff --git a/usr/src/uts/common/vm/seg_umap.h b/usr/src/uts/common/vm/seg_umap.h index 8db23723ed..c348bf471a 100644 --- a/usr/src/uts/common/vm/seg_umap.h +++ b/usr/src/uts/common/vm/seg_umap.h @@ -10,7 +10,7 @@ */ /* - * Copyright 2016 Joyent, Inc. + * Copyright 2018 Joyent, Inc. */ #ifndef _VM_SEG_UMAP_H @@ -33,7 +33,7 @@ typedef struct segumap_data { size_t sud_softlockcnt; } segumap_data_t; -extern int segumap_create(struct seg *, void *); +extern int segumap_create(struct seg **, void *); #ifdef __cplusplus } diff --git a/usr/src/uts/common/vm/seg_vn.c b/usr/src/uts/common/vm/seg_vn.c index f143c1e464..6d01642e4c 100644 --- a/usr/src/uts/common/vm/seg_vn.c +++ b/usr/src/uts/common/vm/seg_vn.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015, Joyent, Inc. All rights reserved. + * Copyright 2018 Joyent, Inc. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ @@ -538,8 +538,9 @@ segvn_setvnode_mpss(vnode_t *vp) } int -segvn_create(struct seg *seg, void *argsp) +segvn_create(struct seg **segpp, void *argsp) { + struct seg *seg = *segpp; extern lgrp_mem_policy_t lgrp_mem_default_policy; struct segvn_crargs *a = (struct segvn_crargs *)argsp; struct segvn_data *svd; @@ -758,6 +759,11 @@ segvn_create(struct seg *seg, void *argsp) (a->szc == pseg->s_szc && IS_P2ALIGNED(pseg->s_base, pgsz) && IS_P2ALIGNED(pseg->s_size, pgsz))); + /* + * Communicate out the newly concatenated + * segment as part of the result. + */ + *segpp = pseg; return (0); } } @@ -797,6 +803,11 @@ segvn_create(struct seg *seg, void *argsp) (a->szc == nseg->s_szc && IS_P2ALIGNED(nseg->s_base, pgsz) && IS_P2ALIGNED(nseg->s_size, pgsz))); + /* + * Communicate out the newly concatenated + * segment as part of the result. + */ + *segpp = nseg; return (0); } } @@ -1253,10 +1264,8 @@ segvn_concat(struct seg *seg1, struct seg *seg2, int amp_cat) * Return 0 on success. */ static int -segvn_extend_prev(seg1, seg2, a, swresv) - struct seg *seg1, *seg2; - struct segvn_crargs *a; - size_t swresv; +segvn_extend_prev(struct seg *seg1, struct seg *seg2, struct segvn_crargs *a, + size_t swresv) { struct segvn_data *svd1 = (struct segvn_data *)seg1->s_data; size_t size; @@ -1333,7 +1342,7 @@ segvn_extend_prev(seg1, seg2, a, swresv) struct vpage *vp, *evp; new_vpage = kmem_zalloc(vpgtob(seg_pages(seg1) + seg_pages(seg2)), - KM_NOSLEEP); + KM_NOSLEEP); if (new_vpage == NULL) return (-1); bcopy(svd1->vpage, new_vpage, vpgtob(seg_pages(seg1))); @@ -1373,11 +1382,8 @@ segvn_extend_prev(seg1, seg2, a, swresv) * Return 0 on success. */ static int -segvn_extend_next( - struct seg *seg1, - struct seg *seg2, - struct segvn_crargs *a, - size_t swresv) +segvn_extend_next(struct seg *seg1, struct seg *seg2, struct segvn_crargs *a, + size_t swresv) { struct segvn_data *svd2 = (struct segvn_data *)seg2->s_data; size_t size; @@ -3357,7 +3363,6 @@ static int segvn_fill_vp_pages(struct segvn_data *svd, vnode_t *vp, u_offset_t off, uint_t szc, page_t **ppa, page_t **ppplist, uint_t *ret_pszc, int *downsize) - { page_t *pplist = *ppplist; size_t pgsz = page_get_pagesize(szc); @@ -3498,7 +3503,7 @@ segvn_fill_vp_pages(struct segvn_data *svd, vnode_t *vp, u_offset_t off, goto out; } io_err = VOP_PAGEIO(vp, io_pplist, io_off, io_len, - B_READ, svd->cred, NULL); + B_READ, svd->cred, NULL); if (io_err) { VM_STAT_ADD(segvnvmstats.fill_vp_pages[8]); page_unlock(targpp); @@ -9457,7 +9462,7 @@ segvn_purge(struct seg *seg) /*ARGSUSED*/ static int segvn_reclaim(void *ptag, caddr_t addr, size_t len, struct page **pplist, - enum seg_rw rw, int async) + enum seg_rw rw, int async) { struct seg *seg = (struct seg *)ptag; struct segvn_data *svd = (struct segvn_data *)seg->s_data; @@ -9534,7 +9539,7 @@ segvn_reclaim(void *ptag, caddr_t addr, size_t len, struct page **pplist, /*ARGSUSED*/ static int shamp_reclaim(void *ptag, caddr_t addr, size_t len, struct page **pplist, - enum seg_rw rw, int async) + enum seg_rw rw, int async) { amp_t *amp = (amp_t *)ptag; pgcnt_t np, npages; @@ -10207,10 +10212,8 @@ segvn_trupdate(void) } static void -segvn_trupdate_seg(struct seg *seg, - segvn_data_t *svd, - svntr_t *svntrp, - ulong_t hash) +segvn_trupdate_seg(struct seg *seg, segvn_data_t *svd, svntr_t *svntrp, + ulong_t hash) { proc_t *p; lgrp_id_t lgrp_id; diff --git a/usr/src/uts/common/vm/seg_vn.h b/usr/src/uts/common/vm/seg_vn.h index 6fef7d678d..97a0db012d 100644 --- a/usr/src/uts/common/vm/seg_vn.h +++ b/usr/src/uts/common/vm/seg_vn.h @@ -21,7 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright (c) 2015, Joyent, Inc. All rights reserved. + * Copyright 2018 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -162,14 +162,14 @@ typedef struct segvn_data { { NULL, NULL, 0, MAP_PRIVATE, prot, max, 0, NULL, 0, 0 } #define AS_MAP_CHECK_VNODE_LPOOB(crfp, argsp) \ - ((crfp) == (int (*)())segvn_create && \ + ((crfp) == (segcreate_func_t)segvn_create && \ (((struct segvn_crargs *)(argsp))->flags & \ (MAP_TEXT | MAP_INITDATA)) && \ ((struct segvn_crargs *)(argsp))->szc == 0 && \ ((struct segvn_crargs *)(argsp))->vp != NULL) #define AS_MAP_CHECK_ANON_LPOOB(crfp, argsp) \ - ((crfp) == (int (*)())segvn_create && \ + ((crfp) == (segcreate_func_t)segvn_create && \ (((struct segvn_crargs *)(argsp))->szc == 0 || \ ((struct segvn_crargs *)(argsp))->szc == AS_MAP_HEAP || \ ((struct segvn_crargs *)(argsp))->szc == AS_MAP_STACK) && \ @@ -228,7 +228,7 @@ typedef struct svntr_stats { } svntr_stats_t; extern void segvn_init(void); -extern int segvn_create(struct seg *, void *); +extern int segvn_create(struct seg **, void *); extern struct seg_ops segvn_ops; diff --git a/usr/src/uts/common/vm/vm_as.c b/usr/src/uts/common/vm/vm_as.c index b09d9b4bae..ec6d2b8920 100644 --- a/usr/src/uts/common/vm/vm_as.c +++ b/usr/src/uts/common/vm/vm_as.c @@ -21,7 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2017 Joyent, Inc. + * Copyright 2018 Joyent, Inc. * Copyright (c) 2016 by Delphix. All rights reserved. */ @@ -79,7 +79,6 @@ static struct kmem_cache *as_cache; static void as_setwatchprot(struct as *, caddr_t, size_t, uint_t); static void as_clearwatchprot(struct as *, caddr_t, size_t); -int as_map_locked(struct as *, caddr_t, size_t, int ((*)()), void *); /* @@ -1435,35 +1434,34 @@ retry: static int as_map_segvn_segs(struct as *as, caddr_t addr, size_t size, uint_t szcvec, - int (*crfp)(), struct segvn_crargs *vn_a, int *segcreated) + segcreate_func_t crfp, struct segvn_crargs *vn_a, boolean_t *segcreated) { - uint_t szc; - uint_t nszc; + uint_t szc, nszc, save_szcvec; int error; - caddr_t a; - caddr_t eaddr; - size_t segsize; - struct seg *seg; + caddr_t a, eaddr; size_t pgsz; - int do_off = (vn_a->vp != NULL || vn_a->amp != NULL); - uint_t save_szcvec; + const boolean_t do_off = (vn_a->vp != NULL || vn_a->amp != NULL); ASSERT(AS_WRITE_HELD(as)); ASSERT(IS_P2ALIGNED(addr, PAGESIZE)); ASSERT(IS_P2ALIGNED(size, PAGESIZE)); ASSERT(vn_a->vp == NULL || vn_a->amp == NULL); + if (!do_off) { vn_a->offset = 0; } if (szcvec <= 1) { - seg = seg_alloc(as, addr, size); + struct seg *seg, *segref; + + seg = segref = seg_alloc(as, addr, size); if (seg == NULL) { return (ENOMEM); } vn_a->szc = 0; - error = (*crfp)(seg, vn_a); + error = (*crfp)(&seg, vn_a); if (error != 0) { + VERIFY3P(seg, ==, segref); seg_free(seg); } else { as->a_size += size; @@ -1487,21 +1485,26 @@ as_map_segvn_segs(struct as *as, caddr_t addr, size_t size, uint_t szcvec, pgsz = page_get_pagesize(nszc); a = (caddr_t)P2ROUNDUP((uintptr_t)addr, pgsz); if (a != addr) { + struct seg *seg, *segref; + size_t segsize; + ASSERT(a < eaddr); + segsize = a - addr; - seg = seg_alloc(as, addr, segsize); + seg = segref = seg_alloc(as, addr, segsize); if (seg == NULL) { return (ENOMEM); } vn_a->szc = szc; - error = (*crfp)(seg, vn_a); + error = (*crfp)(&seg, vn_a); if (error != 0) { + VERIFY3P(seg, ==, segref); seg_free(seg); return (error); } as->a_size += segsize; as->a_resvsize += segsize; - *segcreated = 1; + *segcreated = B_TRUE; if (do_off) { vn_a->offset += segsize; } @@ -1517,20 +1520,24 @@ as_map_segvn_segs(struct as *as, caddr_t addr, size_t size, uint_t szcvec, a = (caddr_t)P2ALIGN((uintptr_t)eaddr, pgsz); ASSERT(a >= addr); if (a != addr) { + struct seg *seg, *segref; + size_t segsize; + segsize = a - addr; - seg = seg_alloc(as, addr, segsize); + seg = segref = seg_alloc(as, addr, segsize); if (seg == NULL) { return (ENOMEM); } vn_a->szc = szc; - error = (*crfp)(seg, vn_a); + error = (*crfp)(&seg, vn_a); if (error != 0) { + VERIFY3P(seg, ==, segref); seg_free(seg); return (error); } as->a_size += segsize; as->a_resvsize += segsize; - *segcreated = 1; + *segcreated = B_TRUE; if (do_off) { vn_a->offset += segsize; } @@ -1549,14 +1556,13 @@ as_map_segvn_segs(struct as *as, caddr_t addr, size_t size, uint_t szcvec, static int as_map_vnsegs(struct as *as, caddr_t addr, size_t size, - int (*crfp)(), struct segvn_crargs *vn_a, int *segcreated) + segcreate_func_t crfp, struct segvn_crargs *vn_a, boolean_t *segcreated) { uint_t mapflags = vn_a->flags & (MAP_TEXT | MAP_INITDATA); int type = (vn_a->type == MAP_SHARED) ? MAPPGSZC_SHM : MAPPGSZC_PRIVM; uint_t szcvec = map_pgszcvec(addr, size, (uintptr_t)addr, mapflags, type, 0); int error; - struct seg *seg; struct vattr va; u_offset_t eoff; size_t save_size = 0; @@ -1570,13 +1576,16 @@ as_map_vnsegs(struct as *as, caddr_t addr, size_t size, again: if (szcvec <= 1) { - seg = seg_alloc(as, addr, size); + struct seg *seg, *segref; + + seg = segref = seg_alloc(as, addr, size); if (seg == NULL) { return (ENOMEM); } vn_a->szc = 0; - error = (*crfp)(seg, vn_a); + error = (*crfp)(&seg, vn_a); if (error != 0) { + VERIFY3P(seg, ==, segref); seg_free(seg); } else { as->a_size += size; @@ -1631,7 +1640,7 @@ again: */ static int as_map_ansegs(struct as *as, caddr_t addr, size_t size, - int (*crfp)(), struct segvn_crargs *vn_a, int *segcreated) + segcreate_func_t crfp, struct segvn_crargs *vn_a, boolean_t *segcreated) { uint_t szcvec; uchar_t type; @@ -1661,21 +1670,20 @@ as_map_ansegs(struct as *as, caddr_t addr, size_t size, } int -as_map(struct as *as, caddr_t addr, size_t size, int (*crfp)(), void *argsp) +as_map(struct as *as, caddr_t addr, size_t size, segcreate_func_t crfp, + void *argsp) { AS_LOCK_ENTER(as, RW_WRITER); return (as_map_locked(as, addr, size, crfp, argsp)); } int -as_map_locked(struct as *as, caddr_t addr, size_t size, int (*crfp)(), +as_map_locked(struct as *as, caddr_t addr, size_t size, segcreate_func_t crfp, void *argsp) { - struct seg *seg = NULL; caddr_t raddr; /* rounded down addr */ size_t rsize; /* rounded up size */ int error; - int unmap = 0; boolean_t is_hole = B_FALSE; /* * The use of a_proc is preferred to handle the case where curproc is @@ -1734,47 +1742,68 @@ as_map_locked(struct as *as, caddr_t addr, size_t size, int (*crfp)(), } if (AS_MAP_CHECK_VNODE_LPOOB(crfp, argsp)) { + boolean_t do_unmap = B_FALSE; + crargs = *(struct segvn_crargs *)argsp; - error = as_map_vnsegs(as, raddr, rsize, crfp, &crargs, &unmap); + error = as_map_vnsegs(as, raddr, rsize, crfp, &crargs, + &do_unmap); if (error != 0) { AS_LOCK_EXIT(as); - if (unmap) { + if (do_unmap) { (void) as_unmap(as, addr, size); } return (error); } } else if (AS_MAP_CHECK_ANON_LPOOB(crfp, argsp)) { + boolean_t do_unmap = B_FALSE; + crargs = *(struct segvn_crargs *)argsp; - error = as_map_ansegs(as, raddr, rsize, crfp, &crargs, &unmap); + error = as_map_ansegs(as, raddr, rsize, crfp, &crargs, + &do_unmap); if (error != 0) { AS_LOCK_EXIT(as); - if (unmap) { + if (do_unmap) { (void) as_unmap(as, addr, size); } return (error); } } else { - seg = seg_alloc(as, addr, size); + struct seg *seg, *segref; + + seg = segref = seg_alloc(as, addr, size); if (seg == NULL) { AS_LOCK_EXIT(as); return (ENOMEM); } - error = (*crfp)(seg, argsp); + /* + * It is possible that the segment creation routine will free + * 'seg' as part of a more advanced operation, such as when + * segvn concatenates adjacent segments together. When this + * occurs, the seg*_create routine must communicate the + * resulting segment out via the 'struct seg **' parameter. + * + * If segment creation fails, it must not free the passed-in + * segment, nor alter the argument pointer. + */ + error = (*crfp)(&seg, argsp); if (error != 0) { + VERIFY3P(seg, ==, segref); seg_free(seg); AS_LOCK_EXIT(as); return (error); } + /* - * Add size now so as_unmap will work if as_ctl fails. - * Not applicable to explicit hole segments. + * Check if the resulting segment represents a hole in the + * address space, rather than contributing to the AS size. */ - if ((seg->s_flags & S_HOLE) == 0) { + is_hole = ((seg->s_flags & S_HOLE) != 0); + + /* Add size now so as_unmap will work if as_ctl fails. */ + if (!is_hole) { as->a_size += rsize; as->a_resvsize += rsize; - } else { - is_hole = B_TRUE; } } diff --git a/usr/src/uts/i86xpv/vm/seg_mf.c b/usr/src/uts/i86xpv/vm/seg_mf.c index 081ee85b15..4ce5f3777a 100644 --- a/usr/src/uts/i86xpv/vm/seg_mf.c +++ b/usr/src/uts/i86xpv/vm/seg_mf.c @@ -22,6 +22,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2018 Joyent, Inc. */ /* @@ -115,8 +116,9 @@ segmf_data_zalloc(struct seg *seg) } int -segmf_create(struct seg *seg, void *args) +segmf_create(struct seg **segpp, void *args) { + struct seg *seg = *segpp; struct segmf_crargs *a = args; struct segmf_data *data; struct as *as = seg->s_as; diff --git a/usr/src/uts/i86xpv/vm/seg_mf.h b/usr/src/uts/i86xpv/vm/seg_mf.h index 316a1f51bd..bc6aaf425d 100644 --- a/usr/src/uts/i86xpv/vm/seg_mf.h +++ b/usr/src/uts/i86xpv/vm/seg_mf.h @@ -22,6 +22,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2018 Joyent, Inc. */ #ifndef _VM_SEG_MF_H @@ -42,7 +43,7 @@ struct segmf_crargs { uchar_t maxprot; }; -extern int segmf_create(struct seg *, void *); +extern int segmf_create(struct seg **, void *); extern int segmf_add_mfns(struct seg *, caddr_t, mfn_t, pgcnt_t, domid_t); |