summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2018-01-22 21:29:22 +0000
committerPatrick Mooney <pmooney@pfmooney.com>2018-01-26 16:15:00 +0000
commitb8fef42ed91e3b916b058421d1783c5cba2a73bf (patch)
treea24d83f65f1f08285f5213f8c4e2f5a4d6234533
parent65ca2e35d394f5308fbe6b2b05c65e86e9ca098b (diff)
downloadillumos-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.c3
-rw-r--r--usr/src/uts/common/vm/as.h10
-rw-r--r--usr/src/uts/common/vm/seg_dev.c4
-rw-r--r--usr/src/uts/common/vm/seg_dev.h3
-rw-r--r--usr/src/uts/common/vm/seg_hole.c5
-rw-r--r--usr/src/uts/common/vm/seg_hole.h6
-rw-r--r--usr/src/uts/common/vm/seg_spt.c10
-rw-r--r--usr/src/uts/common/vm/seg_spt.h5
-rw-r--r--usr/src/uts/common/vm/seg_umap.c5
-rw-r--r--usr/src/uts/common/vm/seg_umap.h4
-rw-r--r--usr/src/uts/common/vm/seg_vn.c43
-rw-r--r--usr/src/uts/common/vm/seg_vn.h8
-rw-r--r--usr/src/uts/common/vm/vm_as.c107
-rw-r--r--usr/src/uts/i86xpv/vm/seg_mf.c4
-rw-r--r--usr/src/uts/i86xpv/vm/seg_mf.h3
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);