1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : /*
8 : * NaCl local descriptor table manipulation support.
9 : */
10 :
11 : #include <asm/ldt.h>
12 : #include <stdio.h>
13 : #if NACL_ANDROID
14 : #include <sys/syscall.h>
15 : #endif
16 :
17 : #include "native_client/src/shared/platform/nacl_sync.h"
18 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
19 : #include "native_client/src/trusted/service_runtime/arch/x86/nacl_ldt_x86.h"
20 : #include "native_client/src/trusted/service_runtime/arch/x86/sel_ldr_x86.h"
21 :
22 : #if NACL_ANDROID
23 : /* Android doesn't have standard modify_ldt() function. */
24 : int modify_ldt(int func, void *ptr, unsigned long bytecount) {
25 : return syscall(__NR_modify_ldt, func, ptr, bytecount);
26 : }
27 : #endif
28 :
29 : /*
30 : * struct LdtEntry is a structure that is laid out exactly as the segment
31 : * descriptors described in the Intel reference manuals. This is needed
32 : * because Mac and Windows use this representation in the methods used to
33 : * set and get entries in the local descriptor table (LDT), but use different
34 : * names for the members. Linux uses a different representation to set, but
35 : * also reads entries back in this format. It needs to be laid out packed,
36 : * bitwise, little endian.
37 : */
38 : struct LdtEntry {
39 : uint16_t limit_00to15;
40 : uint16_t base_00to15;
41 :
42 : unsigned int base_16to23 : 8;
43 :
44 : unsigned int type : 5;
45 : unsigned int descriptor_privilege : 2;
46 : unsigned int present : 1;
47 :
48 : unsigned int limit_16to19 : 4;
49 : unsigned int available : 1;
50 : unsigned int code_64_bit : 1;
51 : unsigned int op_size_32 : 1;
52 : unsigned int granularity : 1;
53 :
54 : unsigned int base_24to31 : 8;
55 : };
56 :
57 : /*
58 : * The module initializer and finalizer set up a mutex used to guard LDT
59 : * manipulation.
60 : */
61 : static struct NaClMutex nacl_ldt_mutex;
62 :
63 294 : int NaClLdtInitPlatformSpecific(void) {
64 294 : if (!NaClMutexCtor(&nacl_ldt_mutex)) {
65 0 : return 0;
66 : }
67 :
68 : /*
69 : * Allocate the last LDT entry to force the LDT to grow to its maximum size.
70 : */
71 294 : return NaClLdtAllocateSelector(LDT_ENTRIES - 1, 0,
72 : NACL_LDT_DESCRIPTOR_DATA, 0, 0, 0);
73 : }
74 :
75 11 : void NaClLdtFiniPlatformSpecific(void) {
76 11 : NaClMutexDtor(&nacl_ldt_mutex);
77 11 : }
78 :
79 : /*
80 : * Find a free selector. Always invoked while holding nacl_ldt_mutex.
81 : */
82 29978 : static int NaClFindUnusedEntryNumber(void) {
83 29978 : int size = sizeof(struct LdtEntry) * LDT_ENTRIES;
84 29978 : struct LdtEntry *entries = malloc(size);
85 : int i;
86 :
87 29978 : int retval = modify_ldt(0, entries, size);
88 :
89 29978 : if (-1 != retval) {
90 29978 : retval = -1; /* In case we don't find any free entry */
91 33587801 : for (i = 0; i < LDT_ENTRIES; ++i) {
92 33587801 : if (!entries[i].present) {
93 29978 : retval = i;
94 29978 : break;
95 : }
96 : }
97 : }
98 :
99 29978 : free(entries);
100 29978 : return retval;
101 : }
102 :
103 : /*
104 : * Find and allocate an available selector, inserting an LDT entry with the
105 : * appropriate permissions.
106 : */
107 30272 : uint16_t NaClLdtAllocateSelector(int entry_number,
108 : int size_is_in_pages,
109 : NaClLdtDescriptorType type,
110 : int read_exec_only,
111 : void* base_addr,
112 : uint32_t size_minus_one) {
113 : struct user_desc ud;
114 : int retval;
115 :
116 30272 : NaClXMutexLock(&nacl_ldt_mutex);
117 :
118 30272 : if (-1 == entry_number) {
119 : /* -1 means caller did not specify -- allocate */
120 29978 : entry_number = NaClFindUnusedEntryNumber();
121 :
122 29978 : if (-1 == entry_number) {
123 : /*
124 : * No free entries were available.
125 : */
126 0 : goto alloc_error;
127 : }
128 : }
129 30272 : ud.entry_number = entry_number;
130 :
131 30272 : switch (type) {
132 : case NACL_LDT_DESCRIPTOR_DATA:
133 29993 : ud.contents = MODIFY_LDT_CONTENTS_DATA;
134 29993 : break;
135 : case NACL_LDT_DESCRIPTOR_CODE:
136 279 : ud.contents = MODIFY_LDT_CONTENTS_CODE;
137 279 : break;
138 : default:
139 0 : goto alloc_error;
140 : }
141 30272 : ud.read_exec_only = read_exec_only;
142 30272 : ud.seg_32bit = 1;
143 30272 : ud.seg_not_present = 0;
144 30272 : ud.useable = 1;
145 :
146 30272 : if (size_is_in_pages && ((unsigned long) base_addr & 0xfff)) {
147 : /*
148 : * The base address needs to be page aligned.
149 : */
150 0 : goto alloc_error;
151 : };
152 30272 : ud.base_addr = (unsigned long) base_addr;
153 :
154 30272 : if (size_minus_one > 0xfffff) {
155 : /*
156 : * If size is in pages, no more than 2**20 pages can be protected.
157 : * If size is in bytes, no more than 2**20 bytes can be protected.
158 : */
159 0 : goto alloc_error;
160 : }
161 30272 : ud.limit = size_minus_one;
162 30272 : ud.limit_in_pages = size_is_in_pages;
163 :
164 : /*
165 : * Install the LDT entry.
166 : */
167 30272 : retval = modify_ldt(1, &ud, sizeof ud);
168 30272 : if (-1 == retval) {
169 0 : goto alloc_error;
170 : }
171 :
172 : /*
173 : * Return an LDT selector with a requested privilege level of 3.
174 : */
175 30272 : NaClXMutexUnlock(&nacl_ldt_mutex);
176 30272 : return (ud.entry_number << 3) | 0x7;
177 :
178 : /*
179 : * All error returns go through this epilog.
180 : */
181 : alloc_error:
182 0 : NaClXMutexUnlock(&nacl_ldt_mutex);
183 0 : return 0;
184 : }
185 :
186 : /*
187 : * Allocates a selector whose size is specified in pages.
188 : * Page granular protection requires that the start address be page-aligned.
189 : */
190 558 : uint16_t NaClLdtAllocatePageSelector(NaClLdtDescriptorType type,
191 : int read_exec_only,
192 : void* base_addr,
193 : uint32_t size_in_pages) {
194 558 : return NaClLdtAllocateSelector(-1, 1, type, read_exec_only, base_addr,
195 : size_in_pages - 1);
196 : }
197 :
198 : /*
199 : * Allocates a selector whose size is specified in bytes.
200 : */
201 29420 : uint16_t NaClLdtAllocateByteSelector(NaClLdtDescriptorType type,
202 : int read_exec_only,
203 : void* base_addr,
204 : uint32_t size_in_bytes) {
205 29420 : return NaClLdtAllocateSelector(-1, 0, type, read_exec_only, base_addr,
206 : size_in_bytes - 1);
207 : }
208 :
209 : /*
210 : * Change a selector whose size is specified in pages.
211 : * Page granular protection requires that the start address be page-aligned.
212 : */
213 0 : uint16_t NaClLdtChangePageSelector(int32_t entry_number,
214 : NaClLdtDescriptorType type,
215 : int read_exec_only,
216 : void* base_addr,
217 : uint32_t size_in_pages) {
218 0 : if ((uint32_t) entry_number >= LDT_ENTRIES) {
219 0 : return 0;
220 : }
221 0 : return NaClLdtAllocateSelector(entry_number, 1, type, read_exec_only,
222 : base_addr, size_in_pages - 1);
223 : }
224 :
225 : /*
226 : * Change a selector whose size is specified in bytes.
227 : */
228 0 : uint16_t NaClLdtChangeByteSelector(int32_t entry_number,
229 : NaClLdtDescriptorType type,
230 : int read_exec_only,
231 : void* base_addr,
232 : uint32_t size_in_bytes) {
233 0 : if ((uint32_t) entry_number >= LDT_ENTRIES) {
234 0 : return 0;
235 : }
236 0 : return NaClLdtAllocateSelector(entry_number, 0, type, read_exec_only,
237 : base_addr, size_in_bytes - 1);
238 : }
239 :
240 :
241 0 : void NaClLdtPrintSelector(uint16_t selector) {
242 : /* type_name converts the segment type into print name */
243 : static const char* type_name[] = {
244 : "data read only",
245 : "data read_only accessed",
246 : "data read write",
247 : "data read write accessed",
248 : "data read expand",
249 : "data read expand accessed",
250 : "data read write expand",
251 : "data read write expand accessed",
252 : "code execute",
253 : "code execute accessed",
254 : "code execute read",
255 : "code execute read accessed",
256 : "code execute conforming",
257 : "code execute conforming accessed",
258 : "code execute read conforming",
259 : "code execute read conforming accessed"
260 : };
261 :
262 : struct LdtEntry entries[LDT_ENTRIES];
263 : struct LdtEntry entry;
264 0 : int retval = modify_ldt(0, entries, sizeof(entries));
265 0 : if (-1 == retval) {
266 0 : return;
267 : }
268 0 : entry = entries[selector >> 3];
269 0 : printf("DESCRIPTOR for selector %04x\n", selector);
270 : /* create functions to do base, limit, and type */
271 0 : printf(" base %08x\n", (entry.base_24to31 << 24)
272 0 : | (entry.base_16to23 << 16) | entry.base_00to15);
273 0 : printf(" limit %08x%s\n",
274 0 : (((entry.limit_16to19 << 16) | entry.limit_00to15)
275 0 : << (entry.granularity ? 12 : 0)),
276 0 : (entry.granularity ? " (page granularity)" : ""));
277 0 : printf(" type %s, %s\n", ((entry.type & 0x10) ? "user" : "system"),
278 0 : type_name[entry.type & 0xf]);
279 0 : printf(" privilege %d\n", entry.descriptor_privilege);
280 0 : printf(" present %s\n", (entry.present ? "yes" : "no"));
281 0 : printf(" available %s\n", (entry.available ? "yes" : "no"));
282 0 : printf(" 64-bit code %s\n", (entry.code_64_bit ? "yes" : "no"));
283 0 : printf(" op size %s\n", (entry.op_size_32 ? "32" : "16"));
284 : }
285 :
286 : /*
287 : * Mark a selector as available for future reuse.
288 : */
289 29365 : void NaClLdtDeleteSelector(uint16_t selector) {
290 : struct user_desc ud;
291 29365 : ud.entry_number = selector >> 3;
292 29365 : ud.seg_not_present = 1;
293 29365 : ud.base_addr = 0;
294 29365 : ud.limit = 0;
295 29365 : ud.limit_in_pages = 0;
296 29365 : ud.read_exec_only = 0;
297 29365 : ud.seg_32bit = 0;
298 29365 : ud.useable = 0;
299 29365 : ud.contents = MODIFY_LDT_CONTENTS_DATA;
300 29365 : NaClXMutexLock(&nacl_ldt_mutex);
301 29365 : modify_ldt(1, &ud, sizeof ud);
302 29365 : NaClXMutexUnlock(&nacl_ldt_mutex);
303 29360 : }
|