xref: /illumos-gate/usr/src/uts/common/syscall/mmapobjsys.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/errno.h>
28 #include <sys/mman.h>
29 #include <sys/cred.h>
30 #include <sys/model.h>
31 #include <sys/vnode.h>
32 #include <sys/systm.h>
33 #include <sys/kmem.h>
34 #include <sys/file.h>
35 #include <sys/vfs.h>
36 #include <sys/sysmacros.h>
37 #include <sys/mmapobj.h>
38 
39 /*
40  * We will "allocate" this many mmapobj_result_t segments on the stack
41  * in an attempt to avoid the need to call kmem_alloc. This value should
42  * cover 99% of the known ELF libraries as well as AOUT (4.x) libraries.
43  */
44 #define	MOBJ_STACK_SEGS	6
45 
46 static void
47 mmapobj_copy_64to32(mmapobj_result_t *source, mmapobj_result32_t *dest, int num)
48 {
49 	int i;
50 
51 	for (i = 0; i < num; i++) {
52 		dest[i].mr_addr = (caddr32_t)(uintptr_t)source[i].mr_addr;
53 		dest[i].mr_msize = (size32_t)source[i].mr_msize;
54 		dest[i].mr_fsize = (size32_t)source[i].mr_fsize;
55 		dest[i].mr_offset = (size32_t)source[i].mr_offset;
56 		dest[i].mr_prot = source[i].mr_prot;
57 		dest[i].mr_flags = source[i].mr_flags;
58 	}
59 }
60 
61 int
62 mmapobjsys(int fd, uint_t flags, mmapobj_result_t *storage,
63     uint_t *elements, void *arg)
64 {
65 	uint_t num_mapped;
66 	uint_t num_in;
67 	int error;
68 	int old_error;
69 	size_t padding = 0;
70 	mmapobj_result_t stack_mr[MOBJ_STACK_SEGS];
71 	mmapobj_result_t *mrp = stack_mr;
72 	struct file *fp;
73 	struct vnode *vp;
74 	model_t model;
75 	int convert_64to32 = 0;
76 	uint_t alloc_num = 0;
77 
78 
79 	/* Verify flags */
80 	if ((flags & ~MMOBJ_ALL_FLAGS) != 0) {
81 		return (set_errno(EINVAL));
82 	}
83 
84 	if (((flags & MMOBJ_PADDING) == 0) && arg != NULL) {
85 		return (set_errno(EINVAL));
86 	}
87 
88 	fp = getf(fd);
89 	if (fp == NULL) {
90 		return (set_errno(EBADF));
91 	}
92 	vp = fp->f_vnode;
93 
94 	if ((fp->f_flag & FREAD) == 0) {
95 		error = EACCES;
96 		goto out;
97 	}
98 
99 	error = copyin(elements, &num_mapped, sizeof (uint_t));
100 	if (error) {
101 		error = EFAULT;
102 		goto out;
103 	}
104 
105 	num_in = num_mapped;
106 	model = get_udatamodel();
107 	if (model != DATAMODEL_NATIVE) {
108 		ASSERT(model == DATAMODEL_ILP32);
109 		convert_64to32 = 1;
110 	}
111 
112 	if (flags & MMOBJ_PADDING) {
113 		if (convert_64to32) {
114 			size32_t padding32;
115 			error = copyin(arg, &padding32, sizeof (padding32));
116 			padding = padding32;
117 		} else {
118 			error = copyin(arg, &padding, sizeof (padding));
119 		}
120 		if (error) {
121 			error = EFAULT;
122 			goto out;
123 		}
124 
125 		/*
126 		 * Need to catch overflow here for the 64 bit case.  For the
127 		 * 32 bit case, overflow would round up to 4G which would
128 		 * not be able to fit in any address space and thus ENOMEM
129 		 * would be returned after calling into mmapobj.
130 		 */
131 		if (padding) {
132 			padding = P2ROUNDUP(padding, PAGESIZE);
133 			if (padding == 0) {
134 				error = ENOMEM;
135 				goto out;
136 			}
137 		}
138 		/* turn off padding if no bytes were requested */
139 		if (padding == 0) {
140 			flags = flags & (~MMOBJ_PADDING);
141 		}
142 	}
143 
144 	if (num_mapped > MOBJ_STACK_SEGS) {
145 		num_mapped = MOBJ_STACK_SEGS;
146 	}
147 retry:
148 	error = mmapobj(vp, flags, mrp, &num_mapped, padding, fp->f_cred);
149 
150 	if (error == E2BIG && alloc_num == 0) {
151 		if (num_mapped > MOBJ_STACK_SEGS && num_mapped <= num_in) {
152 			mrp = kmem_alloc(sizeof (mmapobj_result_t) * num_mapped,
153 			    KM_SLEEP);
154 			alloc_num = num_mapped;
155 			goto retry;
156 		}
157 	}
158 
159 	old_error = error;
160 	if (error == 0 || error == E2BIG) {
161 		error = copyout(&num_mapped, elements, sizeof (uint_t));
162 		if (error) {
163 			error = EFAULT;
164 			/*
165 			 * We only mapped in segments if the mmapobj call
166 			 * succeeded, so only unmap for that case.
167 			 */
168 			if (old_error == 0) {
169 				mmapobj_unmap(mrp, num_mapped, num_mapped, 0);
170 			}
171 		} else if (num_in < num_mapped) {
172 			ASSERT(old_error == E2BIG);
173 			error = E2BIG;
174 		} else {
175 			if (convert_64to32) {
176 				mmapobj_result32_t *mrp32;
177 				/* Need to translate from 64bit to 32bit */
178 				mrp32 = kmem_alloc(num_mapped * sizeof (*mrp32),
179 				    KM_SLEEP);
180 				mmapobj_copy_64to32(mrp, mrp32, num_mapped);
181 				error = copyout(mrp32, (void *)storage,
182 				    num_mapped * sizeof (mmapobj_result32_t));
183 				kmem_free(mrp32, num_mapped * sizeof (*mrp32));
184 			} else {
185 				error = copyout(mrp, (void *)storage,
186 				    num_mapped * sizeof (mmapobj_result_t));
187 			}
188 			if (error) {
189 				error = EFAULT;
190 				mmapobj_unmap(mrp, num_mapped, num_mapped, 0);
191 			}
192 		}
193 	}
194 
195 	/*
196 	 * If stack_mr was not large enough, then we had to allocate
197 	 * a larger piece of memory to hold the mmapobj_result array.
198 	 */
199 	if (alloc_num != 0) {
200 		ASSERT(mrp != stack_mr);
201 		ASSERT(num_mapped > MOBJ_STACK_SEGS);
202 		kmem_free(mrp,
203 		    alloc_num * sizeof (mmapobj_result_t));
204 	}
205 
206 out:
207 	releasef(fd);
208 	if (error) {
209 		return (set_errno(error));
210 	} else {
211 		return (0);
212 	}
213 }
214