xref: /xnu-8020.101.4/osfmk/i386/user_ldt.c (revision e7776783b89a353188416a9a346c6cdb4928faad)
1 /*
2  * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /*
29  * @OSF_COPYRIGHT@
30  */
31 /*
32  * Mach Operating System
33  * Copyright (c) 1991 Carnegie Mellon University
34  * All Rights Reserved.
35  *
36  * Permission to use, copy, modify and distribute this software and its
37  * documentation is hereby granted, provided that both the copyright
38  * notice and this permission notice appear in all copies of the
39  * software, derivative works or modified versions, and any portions
40  * thereof, and that both notices appear in supporting documentation.
41  *
42  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45  *
46  * Carnegie Mellon requests users of this software to return to
47  *
48  *  Software Distribution Coordinator  or  [email protected]
49  *  School of Computer Science
50  *  Carnegie Mellon University
51  *  Pittsburgh PA 15213-3890
52  *
53  * any improvements or extensions that they make and grant Carnegie Mellon
54  * the rights to redistribute these changes.
55  */
56 
57 /*
58  */
59 
60 /*
61  * User LDT management.
62  * Each task may have its own LDT.
63  */
64 
65 #include <kern/kalloc.h>
66 #include <kern/thread.h>
67 #include <kern/misc_protos.h>
68 
69 #include <vm/vm_kern.h>
70 
71 #include <i386/machdep_call.h>
72 #include <i386/user_ldt.h>
73 #include <i386/mp.h>
74 #include <i386/machine_routines.h>
75 #include <i386/proc_reg.h>
76 #include <i386/mp_desc.h>
77 #include <i386/seg.h>
78 #include <i386/thread.h>
79 
80 #include <sys/errno.h>
81 
82 static void user_ldt_set_action(void *);
83 static int i386_set_ldt_impl(uint32_t *retval, uint64_t start_sel, uint64_t descs,
84     uint64_t num_sels);
85 static int i386_get_ldt_impl(uint32_t *retval, uint64_t start_sel, uint64_t descs,
86     uint64_t num_sels);
87 
88 #define USER_LDT_SIZE(descriptors) \
89 	(sizeof(struct user_ldt) + (descriptors * sizeof(struct real_descriptor)))
90 
91 /*
92  * Add the descriptors to the LDT, starting with
93  * the descriptor for 'first_selector'.
94  */
95 
96 static int
i386_set_ldt_impl(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)97 i386_set_ldt_impl(
98 	uint32_t                *retval,
99 	uint64_t                start_sel,
100 	uint64_t                descs,  /* out */
101 	uint64_t                num_sels)
102 {
103 	user_ldt_t      new_ldt, old_ldt;
104 	struct real_descriptor *dp;
105 	unsigned int    i;
106 	unsigned int    min_selector = LDTSZ_MIN;       /* do not allow the system selectors to be changed */
107 	task_t          task = current_task();
108 	unsigned int    ldt_count;
109 	kern_return_t err;
110 
111 	if (start_sel != LDT_AUTO_ALLOC
112 	    && (start_sel != 0 || num_sels != 0)
113 	    && (start_sel < min_selector || start_sel >= LDTSZ || num_sels > LDTSZ)) {
114 		return EINVAL;
115 	}
116 	if (start_sel != LDT_AUTO_ALLOC && start_sel + num_sels > LDTSZ) {
117 		return EINVAL;
118 	}
119 
120 	task_lock(task);
121 
122 	old_ldt = task->i386_ldt;
123 
124 	if (start_sel == LDT_AUTO_ALLOC) {
125 		if (old_ldt) {
126 			unsigned int null_count;
127 			struct real_descriptor null_ldt;
128 
129 			bzero(&null_ldt, sizeof(null_ldt));
130 
131 			/*
132 			 * Look for null selectors among the already-allocated
133 			 * entries.
134 			 */
135 			null_count = 0;
136 			i = 0;
137 			while (i < old_ldt->count) {
138 				if (!memcmp(&old_ldt->ldt[i++], &null_ldt, sizeof(null_ldt))) {
139 					null_count++;
140 					if (null_count == num_sels) {
141 						break; /* break out of while loop */
142 					}
143 				} else {
144 					null_count = 0;
145 				}
146 			}
147 
148 			/*
149 			 * If we broke out of the while loop, i points to the selector
150 			 * after num_sels null selectors.  Otherwise it points to the end
151 			 * of the old LDTs, and null_count is the number of null selectors
152 			 * at the end.
153 			 *
154 			 * Either way, there are null_count null selectors just prior to
155 			 * the i-indexed selector, and either null_count >= num_sels,
156 			 * or we're at the end, so we can extend.
157 			 */
158 			start_sel = old_ldt->start + i - null_count;
159 		} else {
160 			start_sel = LDTSZ_MIN;
161 		}
162 
163 		if (start_sel + num_sels > LDTSZ) {
164 			task_unlock(task);
165 			return ENOMEM;
166 		}
167 	}
168 
169 	if (start_sel == 0 && num_sels == 0) {
170 		new_ldt = NULL;
171 	} else {
172 		/*
173 		 * Allocate new LDT
174 		 */
175 
176 		unsigned int    begin_sel = (unsigned int)start_sel;
177 		unsigned int    end_sel = (unsigned int)begin_sel +
178 		    (unsigned int)num_sels;
179 
180 		if (old_ldt != NULL) {
181 			if (old_ldt->start < begin_sel) {
182 				begin_sel = old_ldt->start;
183 			}
184 			if (old_ldt->start + old_ldt->count > end_sel) {
185 				end_sel = old_ldt->start + old_ldt->count;
186 			}
187 		}
188 
189 		ldt_count = end_sel - begin_sel;
190 		/* XXX allocation under task lock */
191 		new_ldt = (user_ldt_t)kalloc_data(USER_LDT_SIZE(ldt_count), Z_WAITOK);
192 		if (new_ldt == NULL) {
193 			task_unlock(task);
194 			return ENOMEM;
195 		}
196 
197 		new_ldt->start = begin_sel;
198 		new_ldt->count = ldt_count;
199 
200 		/*
201 		 * Have new LDT.  If there was a an old ldt, copy descriptors
202 		 * from old to new.
203 		 */
204 		if (old_ldt) {
205 			bcopy(&old_ldt->ldt[0],
206 			    &new_ldt->ldt[old_ldt->start - begin_sel],
207 			    old_ldt->count * sizeof(struct real_descriptor));
208 
209 			/*
210 			 * If the old and new LDTs are non-overlapping, fill the
211 			 * center in with null selectors.
212 			 */
213 
214 			if (old_ldt->start + old_ldt->count < start_sel) {
215 				bzero(&new_ldt->ldt[old_ldt->count],
216 				    (start_sel - (old_ldt->start + old_ldt->count)) * sizeof(struct real_descriptor));
217 			} else if (old_ldt->start > start_sel + num_sels) {
218 				bzero(&new_ldt->ldt[num_sels],
219 				    (old_ldt->start - (start_sel + num_sels)) * sizeof(struct real_descriptor));
220 			}
221 		}
222 
223 		/*
224 		 * Install new descriptors.
225 		 */
226 		if (descs != 0) {
227 			/* XXX copyin under task lock */
228 			err = copyin(descs, (char *)&new_ldt->ldt[start_sel - begin_sel],
229 			    num_sels * sizeof(struct real_descriptor));
230 			if (err != 0) {
231 				task_unlock(task);
232 				user_ldt_free(new_ldt);
233 				return err;
234 			}
235 		} else {
236 			bzero(&new_ldt->ldt[start_sel - begin_sel], num_sels * sizeof(struct real_descriptor));
237 		}
238 		/*
239 		 * Validate descriptors.
240 		 * Only allow descriptors with user privileges.
241 		 */
242 		for (i = 0, dp = (struct real_descriptor *) &new_ldt->ldt[start_sel - begin_sel];
243 		    i < num_sels;
244 		    i++, dp++) {
245 			switch (dp->access & ~ACC_A) {
246 			case 0:
247 			case ACC_P:
248 				/* valid empty descriptor, clear Present preemptively */
249 				dp->access &= (~ACC_P & 0xff);
250 				break;
251 			case ACC_P | ACC_PL_U | ACC_DATA:
252 			case ACC_P | ACC_PL_U | ACC_DATA_W:
253 			case ACC_P | ACC_PL_U | ACC_DATA_E:
254 			case ACC_P | ACC_PL_U | ACC_DATA_EW:
255 			case ACC_P | ACC_PL_U | ACC_CODE:
256 			case ACC_P | ACC_PL_U | ACC_CODE_R:
257 			case ACC_P | ACC_PL_U | ACC_CODE_C:
258 			case ACC_P | ACC_PL_U | ACC_CODE_CR:
259 				break;
260 			default:
261 				task_unlock(task);
262 				user_ldt_free(new_ldt);
263 				return EACCES;
264 			}
265 			/* Reject attempts to create segments with 64-bit granules */
266 			/* Note this restriction is still correct, even when
267 			 * executing as a 64-bit process (we want to maintain a single
268 			 * 64-bit selector (located in the GDT)).
269 			 */
270 			if (dp->granularity & SZ_64) {
271 				task_unlock(task);
272 				user_ldt_free(new_ldt);
273 				return EACCES;
274 			}
275 		}
276 	}
277 
278 	task->i386_ldt = new_ldt; /* new LDT for task */
279 
280 	/*
281 	 * Switch to new LDT.  We need to do this on all CPUs, since
282 	 * another thread in this same task may be currently running,
283 	 * and we need to make sure the new LDT is in place
284 	 * throughout the task before returning to the user.
285 	 */
286 	mp_broadcast(user_ldt_set_action, task);
287 
288 	task_unlock(task);
289 
290 	/* free old LDT.  We can't do this until after we've
291 	 * rendezvoused with all CPUs, in case another thread
292 	 * in this task was in the process of context switching.
293 	 */
294 	if (old_ldt) {
295 		user_ldt_free(old_ldt);
296 	}
297 
298 	*retval = (uint32_t)start_sel;
299 
300 	return 0;
301 }
302 
303 static int
i386_get_ldt_impl(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)304 i386_get_ldt_impl(
305 	uint32_t                *retval,
306 	uint64_t                start_sel,
307 	uint64_t                descs,  /* out */
308 	uint64_t                num_sels)
309 {
310 	user_ldt_t      user_ldt;
311 	task_t          task = current_task();
312 	unsigned int    ldt_count;
313 	kern_return_t   err;
314 
315 	if (start_sel >= LDTSZ || num_sels > LDTSZ) {
316 		return EINVAL;
317 	}
318 	if (start_sel + num_sels > LDTSZ) {
319 		return EINVAL;
320 	}
321 	if (descs == 0) {
322 		return EINVAL;
323 	}
324 
325 	task_lock(task);
326 
327 	user_ldt = task->i386_ldt;
328 	err = 0;
329 
330 	/*
331 	 * copy out the descriptors
332 	 */
333 
334 	if (user_ldt != 0) {
335 		ldt_count = user_ldt->start + user_ldt->count;
336 	} else {
337 		ldt_count = LDTSZ_MIN;
338 	}
339 
340 
341 	if (start_sel < ldt_count) {
342 		unsigned int copy_sels = (unsigned int)num_sels;
343 
344 		if (start_sel + num_sels > ldt_count) {
345 			copy_sels = ldt_count - (unsigned int)start_sel;
346 		}
347 
348 		err = copyout((char *)(current_ldt() + start_sel),
349 		    descs, copy_sels * sizeof(struct real_descriptor));
350 	}
351 
352 	task_unlock(task);
353 
354 	*retval = ldt_count;
355 
356 	return err;
357 }
358 
359 void
user_ldt_free(user_ldt_t user_ldt)360 user_ldt_free(
361 	user_ldt_t      user_ldt)
362 {
363 	kfree_data(user_ldt, USER_LDT_SIZE(user_ldt->count));
364 }
365 
366 user_ldt_t
user_ldt_copy(user_ldt_t user_ldt)367 user_ldt_copy(
368 	user_ldt_t      user_ldt)
369 {
370 	if (user_ldt != NULL) {
371 		size_t      size = USER_LDT_SIZE(user_ldt->count);
372 		user_ldt_t  new_ldt = (user_ldt_t)kalloc_data(size, Z_WAITOK);
373 		if (new_ldt != NULL) {
374 			bcopy(user_ldt, new_ldt, size);
375 		}
376 		return new_ldt;
377 	}
378 
379 	return 0;
380 }
381 
382 void
user_ldt_set_action(void * arg)383 user_ldt_set_action(
384 	void *arg)
385 {
386 	task_t          arg_task = (task_t)arg;
387 
388 	if (arg_task == current_task()) {
389 		user_ldt_set(current_thread());
390 	}
391 }
392 
393 /*
394  * Set the LDT for the given thread on the current CPU.  Should be invoked
395  * with interrupts disabled.
396  */
397 void
user_ldt_set(thread_t thread)398 user_ldt_set(
399 	thread_t thread)
400 {
401 	task_t          task = get_threadtask(thread);
402 	user_ldt_t      user_ldt;
403 
404 	user_ldt = task->i386_ldt;
405 
406 	if (user_ldt != 0) {
407 		struct real_descriptor *ldtp = current_ldt();
408 
409 		if (user_ldt->start > LDTSZ_MIN) {
410 			bzero(&ldtp[LDTSZ_MIN],
411 			    sizeof(struct real_descriptor) * (user_ldt->start - LDTSZ_MIN));
412 		}
413 
414 		bcopy(user_ldt->ldt, &ldtp[user_ldt->start],
415 		    sizeof(struct real_descriptor) * (user_ldt->count));
416 
417 		gdt_desc_p(USER_LDT)->limit_low = (uint16_t)((sizeof(struct real_descriptor) * (user_ldt->start + user_ldt->count)) - 1);
418 
419 		ml_cpu_set_ldt(USER_LDT);
420 	} else {
421 		ml_cpu_set_ldt(KERNEL_LDT);
422 	}
423 }
424 
425 /* For 32-bit processes, called via machdep_syscall() */
426 int
i386_set_ldt(uint32_t * retval,uint32_t start_sel,uint32_t descs,uint32_t num_sels)427 i386_set_ldt(
428 	uint32_t                *retval,
429 	uint32_t                start_sel,
430 	uint32_t                descs,  /* out */
431 	uint32_t                num_sels)
432 {
433 	return i386_set_ldt_impl(retval, (uint64_t)start_sel, (uint64_t)descs,
434 	           (uint64_t)num_sels);
435 }
436 
437 /* For 64-bit processes, called via machdep_syscall64() */
438 int
i386_set_ldt64(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)439 i386_set_ldt64(
440 	uint32_t                *retval,
441 	uint64_t                start_sel,
442 	uint64_t                descs,  /* out */
443 	uint64_t                num_sels)
444 {
445 	return i386_set_ldt_impl(retval, start_sel, descs, num_sels);
446 }
447 
448 /* For 32-bit processes, called via machdep_syscall() */
449 int
i386_get_ldt(uint32_t * retval,uint32_t start_sel,uint32_t descs,uint32_t num_sels)450 i386_get_ldt(
451 	uint32_t                *retval,
452 	uint32_t                start_sel,
453 	uint32_t                descs,  /* out */
454 	uint32_t                num_sels)
455 {
456 	return i386_get_ldt_impl(retval, (uint64_t)start_sel, (uint64_t)descs,
457 	           (uint64_t)num_sels);
458 }
459 
460 /* For 64-bit processes, called via machdep_syscall64() */
461 int
i386_get_ldt64(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)462 i386_get_ldt64(
463 	uint32_t                *retval,
464 	uint64_t                start_sel,
465 	uint64_t                descs,  /* out */
466 	uint64_t                num_sels)
467 {
468 	return i386_get_ldt_impl(retval, start_sel, descs, num_sels);
469 }
470