1 static char rcsid[] = "$Id: sunprotect.c,v 1.6 1997/06/18 19:28:47 paul Exp $";
2
3 /*
4 * This file contains the majority of the code for handling smx memory
5 * protection, and also deals with mapping and unmapping of user processes
6 * to and from the low memory addresses at which they have been linked to
7 * execute.
8 *
9 * There are three levels of protection: level 0 ('none') - no protection;
10 * level 1 ('half') - r-x text segments for kernel, mm, fs and init
11 * in "physical" memory, and for the mapped text segment of the current
12 * user process; level 2 ('full') - full protection. With full protection,
13 * a process may not access smx memory outside its own address space,
14 * with the exception of a small part of the kernel (eg. the layer 1
15 * interrupt stack). Level 1 is the default. Level 2 slows execution down
16 * a lot, but is valuable in tracking down certain type of memory
17 * corruption bugs.
18 *
19 * The main entry points are:
20 * protect_init: initialise protection info
21 * mem_released: called when a process' addr space is released
22 * set_protect: set protection on a region
23 *
24 * entering_kernel: called when switching to kernel
25 * leaving_kernel: called when switching from kernel.
26 * Associated functions are mprotect, kernel_on, kernel_off,
27 * set_process_prot, map_process, unmap_process
28 *
29 * zeroclicks: wrapper for the real zeroclicks
30 * copyclicks: wrapper for the real copyclicks
31 * phys_copy: wrapper for the real phys_copy
32 *
33 * These wrapper functions look after enabling access before the copy
34 * and disabling access after the copy.
35 */
36
37 #include "kernel.h"
38 #include "const.h"
39 #include <minix/com.h>
40 #include <sun/syscall.h>
41 #include "assert.h"
42
43 #include "proc.h"
44
45 /*
46 * For protection purposes, the size of the kernel stack must be a
47 * multiple of the click size.
48 */
49 #if K_STACK_BYTES % CLICK_SIZE != 0
50 error "The kernel stack is not a multiple of the click size"
51 #endif
52
53 #define max(x,y) ((x) > (y) ? (x) : (y))
54
55 /* SunOS's protection codes for mprotect(2) */
56
57 #define PROT_READ 0x1 /* page can be read */
58 #define PROT_WRITE 0x2 /* page can be written */
59 #define PROT_EXEC 0x4 /* page can be executed */
60 #define PROT_NONE 0x0 /* page can not be accessed */
61 #define PROT_ALL (PROT_READ | PROT_WRITE | PROT_EXEC)
62 #define PROT_READ_EXEC (PROT_READ | PROT_EXEC)
63
64
65 /*
66 * The information in (see below) is used to partition the kernel address
67 * space into separate areas, each of which has some protection mode
68 * when execution is outside the kernel and full protection is enabled.
69 * Details of each area are held in a prot_info struct.
70 */
71 struct prot_info {
72 phys_bytes begin; /* Beginning of area */
73 int len; /* Length of area */
74 int prot; /* Permissions on the area */
75 };
76
77 /*
78 * In the worst case, we need 2 * NUM_SEGS + 1 prot_info structs.
79 * This occurs when there are gaps before and after each of the
80 * areas that is to remain accessible.
81 */
82 #define SEGMENTS (11)
83
84
85 /*
86 * The protect_info structure contains all information needed
87 * to protect and de-protect the kernel. It must remain
88 * readable when execution is outside the kernel so that when
89 * a signal occurs the information needed to turn on access to the
90 * kernel is available. prot_info specifies what the kernel
91 * protections should be with execution outside the kernel.
92 */
93 struct protect_info {
94 int prot_lev;
95 phys_bytes kernel_base;
96 phys_bytes kernel_text;
97 phys_bytes kernel_data;
98 struct prot_info prot_info[SEGMENTS];
99 int numsegs; /* number of prot_info entries in use */
100 int in_kernel; /* is execution in the kernel? */
101 } pinfo;
102
103 /*
104 * Current user proc mapped into low virtual memory.
105 * Used to avoid mapping a process in that is already mapped in.
106 */
107 static struct proc *mapped_proc = 0;
108
109
110 /*
111 * We need to know about these function addresses to set kernel protections
112 * when execution is outside the kernel.
113 */
114 extern int SunOS();
115 extern void SunOSend(), mpx_start(), mpx_end();
116 static void protect_start(void);
117 static void protect_end(void);
118
119 /*
120 * A seg_info struct records protection information for an address
121 * range. The segs array specifies all areas that must remain
122 * accessible in some way when execution is outside the kernel. All
123 * stack, data and text needed to get to the point where kernel_on has
124 * finished its work must be available. This means that the following
125 * must be accessible: the interrupt stack, the interrupt handler
126 * (SunOSsig), the SunOS function that performs system calls, the
127 * relevant code in this file, and the pinfo structure. The fewer of
128 * these segments the faster context switching in full protection will
129 * be. About the only thing that would be quite easy to do would be
130 * to have the relevant functions from mpx.c and this file in a single
131 * .c file.
132 */
133 struct seg_info {
134 phys_bytes begin, end;
135 int prot;
136 };
137
138 struct seg_info segs[] = {
139 {0, 0, PROT_ALL}, /* entry for kernel stack */
140 {(phys_bytes)protect_start, (phys_bytes)protect_end, PROT_READ_EXEC},
141 {(phys_bytes)mpx_start, (phys_bytes)mpx_end, PROT_READ_EXEC},
142 {(phys_bytes)SunOS, (phys_bytes)SunOSend, PROT_READ_EXEC},
143 {(phys_bytes)&pinfo, (phys_bytes)&pinfo + sizeof(pinfo), PROT_READ},
144 };
145
146 #define SEGS_SIZE (sizeof(segs) / sizeof(struct seg_info))
147
148
149 /*
150 * Local functions.
151 */
152 static void mprotect(phys_bytes addr, int len, int prot);
153 static void kernel_on(void);
154 static void kernel_off(void);
155 static void set_process_prot(struct proc *p,
156 int text_prot, int data_stack_prot);
157 static void map_process(struct proc *p);
158 static void unmap_process(struct proc *p);
159
160
161 /*
162 * Function: protect_init
163 * Parameters: kernel_base - virtual address that the kernel begins at
164 * kernel_text - kernel text segment size (in clicks)
165 * kernel_data - kernel data segment size (in clicks)
166 * mem_clicks - total smx "physical" memory (in clocks)
167 * level - protection level (0, 1 or 2).
168 *
169 * Called during system bootstrapping. Data structures and memory protection
170 * are initialised according to the protection level specified.
171 */
172 void protect_init(phys_bytes kernel_base, phys_clicks kernel_text,
173 phys_clicks kernel_data, phys_clicks mem_clicks,
174 int level)
175 {
176 phys_bytes kernel_end; /* first click after kernel */
177 phys_bytes next_click;
178 int i, j;
179 register struct proc *rp;
180
181 pinfo.prot_lev = level;
182 debug_str("Protection level ");
183 debug_int(pinfo.prot_lev);
184 debug_char('\n');
185
186 if (pinfo.prot_lev == FULL_PROT) {
187 /*
188 * Full protection. Start by recording details of the kernel
189 * address space (needed when enabling access to the kernel.
190 */
191 pinfo.kernel_base = kernel_base;
192 pinfo.kernel_text = kernel_text << CLICK_SHIFT;
193 pinfo.kernel_data = kernel_data << CLICK_SHIFT;
194
195 pinfo.in_kernel = 1;
196 kernel_end = pinfo.kernel_base + pinfo.kernel_text + pinfo.kernel_data;
197
198 /*
199 * Update details in segs. Details of the kernel stack are
200 * added, and all entries are aligned to click boundaries
201 */
202 segs[0].end = upclick(getksp());
203 segs[0].begin = segs[0].end - K_STACK_BYTES;
204 for (i = 0 ; i < SEGS_SIZE ; i++) {
205 segs[i].begin = downclick(segs[i].begin);
206 segs[i].end = upclick(segs[i].end);
207 }
208
209 /*
210 * Sort segments by beginning address. Segments need to be sorted
211 * for the next step to work.
212 */
213 {
214 int done = 0;
215 struct seg_info temp;
216
217 while (!done) {
218 done = 1;
219 for (i = 0; i < SEGS_SIZE - 1; i++) {
220 if (segs[i].begin > segs[i+1].begin) {
221 temp = segs[i];
222 segs[i] = segs[i+1];
223 segs[i+1] = temp;
224 done = 0;
225 }
226 }
227 }
228 }
229
230 /*
231 * The entries in segs cover only parts of the kernel address
232 * space. The entries in pinfo.prot_info collectively cover
233 * the entire kernel address space, so that when access to the
234 * kernel is disabled we have information on what to do for all
235 * kernel areas. We now generate pinfo.prot_info from segs.
236 * This is basically a copying operating, with extra entries
237 * added to pinfo.prot_info to cover gaps in segs, and overlapping
238 * segs entries merged.
239 */
240 j = -1; /* j is the index into pinfo */
241 next_click = pinfo.kernel_base;
242 for (i = 0 ; i < SEGS_SIZE ; i++) {
243 /*
244 * Each time around the loop deal with an element of segs.
245 */
246 if (segs[i].begin > next_click) {
247 /*
248 * There's a gap between this special segment
249 * and the previous one - fill it with a no go area.
250 */
251 j++;
252 pinfo.prot_info[j].begin = next_click;
253 pinfo.prot_info[j].len = segs[i].begin - next_click;
254 pinfo.prot_info[j].prot = PROT_NONE;
255 }
256
257 if (segs[i].begin < next_click) {
258 /*
259 * An overlap - merge into a single segment,
260 * with protection as weak as required.
261 */
262 pinfo.prot_info[j].len = max(pinfo.prot_info[j].len,
263 segs[i].end - pinfo.prot_info[j].begin);
264 pinfo.prot_info[j].prot |= segs[i].prot;
265 } else {
266 /*
267 * No overlap - create a new segment
268 */
269 j++;
270 pinfo.prot_info[j].begin = segs[i].begin;
271 pinfo.prot_info[j].len = segs[i].end - segs[i].begin;
272 pinfo.prot_info[j].prot = segs[i].prot;
273 }
274 next_click = pinfo.prot_info[j].begin + pinfo.prot_info[j].len;
275 }
276
277 /*
278 * Is there a gap after the last segs entry?
279 */
280 if (next_click != kernel_end) {
281 j++;
282 pinfo.prot_info[j].begin = next_click;
283 pinfo.prot_info[j].len = kernel_end - next_click;
284 pinfo.prot_info[j].prot = PROT_NONE;
285 }
286
287 pinfo.numsegs = j + 1;
288 assert(pinfo.numsegs < SEGMENTS);
289
290 /*
291 * Dump the pinfo struct if debugging is on
292 */
293 debug_str("kbase: "); debug_int(pinfo.kernel_base);
294 debug_str("\nktext: "); debug_int(pinfo.kernel_text);
295 debug_str("\nkdata: "); debug_int(pinfo.kernel_data);
296 for (i=0 ; i < pinfo.numsegs ; i++) {
297 debug_str("\nbegin: "); debug_int(pinfo.prot_info[i].begin);
298 debug_str("\nlen: "); debug_int(pinfo.prot_info[i].len);
299 debug_str("\nprot: "); debug_int(pinfo.prot_info[i].prot);
300 }
301 debug_char('\n');
302
303 /*
304 * Now that the data structures are set up, setup the current
305 * protection so that there is normal access to the kernel, and
306 * no access to the rest of the SunOS Minix address space.
307 */
308 kernel_on();
309 mprotect(kernel_end, (mem_clicks << CLICK_SHIFT) -
310 (pinfo.kernel_text + pinfo.kernel_data), PROT_NONE);
311
312 } else if (pinfo.prot_lev == HALF_PROT) {
313 /*
314 * For half protection, we need to make the kernel, MM, FS, INET and
315 * init text segments read-exec only.
316 */
317
318 mprotect(kernel_base, kernel_text << CLICK_SHIFT, PROT_READ_EXEC);
319 for (rp = BEG_SERV_ADDR; rp <= BEG_USER_ADDR; rp++){
320 mprotect(rp->p_map[T].mem_phys << CLICK_SHIFT,
321 rp->p_map[T].mem_len << CLICK_SHIFT,
322 PROT_READ_EXEC);
323 }
324 } /* prot_lev == NO_PROT means no protection; do nothing. */
325 }
326
327
328 /*
329 * Function: mem_released
330 * Parameter: p - process whose memory is being released.
331 * Returns: nothing
332 *
333 * Called when the address space of a process is about to be released
334 * (process has called exec or exit). This gives the protection/mapping
335 * module a chance to remove any cached information.
336 */
337 void mem_released(struct proc *p)
338 {
339 unmap_process(p);
340 if (gwin_proc == p) {
341 gwin_proc = 0;
342 }
343 }
344
345
346 /*
347 * Function: set_protect
348 * Parameters: start - starting address of the area whose protection is to
349 * be changed.
350 * len - length of area.
351 * protection to assign to area.
352 *
353 * Set the protection for an area of memory. The area might not be
354 * click aligned, so the alignment must be performed before mprotect can be
355 * called.
356 */
357 void set_protect(phys_bytes start, int len, int prot)
358 {
359 phys_bytes prot_start;
360 int prot_len;
361
362 /*
363 * Only for full protection. In lower level protection the only areas
364 * of "physical" memory that are (at half protection) not writable are
365 * the text segments of kernel, mm, fs and init, which are loaded by
366 * the bootstrap and then never changed.
367 */
368 if (pinfo.prot_lev == FULL_PROT) {
369 /*
370 * If the area is part of the kernel then just quietly ignore this
371 * request as the required access should already be possible.
372 */
373 if (start > pinfo.kernel_base && start < pinfo.kernel_base +
374 pinfo.kernel_text + pinfo.kernel_data) {
375 return;
376 }
377
378 /*
379 * Click align the area whose protection is to be changed.
380 */
381 prot_start = downclick(start);
382 prot_len = upclick(start + len) - prot_start;
383 mprotect(prot_start, prot_len, prot);
384 }
385 }
386
387
388 /*
389 * DO NOT MOVE THIS FUNCTION---IT MARKS THE BEGINNING OF THE AREA OF CODE
390 * THAT MUST BE EXECUTABLE WHEN EXECUTION IS OUTSIDE THE KERNEL.
391 */
392 static void protect_start(void) {}
393
394
395 /*
396 * Function: mprotect
397 *
398 * This is a wrapper for the SunOS mprotect(2) system call. Errors
399 * are reported to the console.
400 */
401 static void mprotect(phys_bytes addr, int len, int prot)
402 {
403 typedef char *caddr_t;
404
405 if (SunOS(SYS_mprotect, (caddr_t) addr, len, prot) == -1) {
406 printk("mprotect failed: address 0x%x, len 0x%x, prot 0%o\n",
407 addr, len, prot);
408 }
409 }
410
411
412 /*
413 * Function: entering_kernel
414 *
415 * Called by SunOSsig to turn on full access to the kernel so that a
416 * SunOS signal can be handled.
417 */
418 void entering_kernel(void)
419 {
420 /*
421 * Note - only need to change protection if full protection is enabled
422 * and we were outside the kernel to start with.
423 */
424 if (pinfo.prot_lev == FULL_PROT && !pinfo.in_kernel) {
425 kernel_on();
426 pinfo.in_kernel = 1;
427
428 /*
429 * mm and fs are executed "in place" in "physical" memory (to avoid
430 * mapping and unmapping costs. We need to disable access to their
431 * portions of physical memory. For user processes, the physical
432 * memory containing the process has PROT_NONE access during,
433 * so no changes in "physical" memory protection are needed. Instead,
434 * the user process is unmapped from low memory where it has been
435 * executing. This is not strictly necessary, as these mappings will
436 * be over-ridden by the next user process is mapped in, but it gives
437 * some added protection against rogue memory accesses.
438 */
439 if (isuserp(proc_ptr)) {
440 unmap_process(proc_ptr);
441 } else {
442 set_process_prot(proc_ptr, PROT_NONE, PROT_NONE);
443 }
444 }
445 else unmap_process(proc_ptr);
446 }
447
448
449 /*
450 * Function: leaving_kernel
451 *
452 * Called just prior to leaving layer 1 and resuming an smx process
453 * in layers 2-4. If execution is switching to a user process, then
454 * that process is mapped into low memory. If full protection is enabled,
455 * leaving_kernel makes the kernel address space as inaccessible as
456 * possible.
457 */
458 void leaving_kernel(void)
459 {
460 if (isuserp(proc_ptr)) {
461 map_process(proc_ptr);
462 }
463
464 if (pinfo.prot_lev == FULL_PROT) {
465 /*
466 * Note - only change protection if execution is leaving the kernel,
467 * and full protection is enabled. If switching to a layer 2 task
468 * then execution is remaining withing the kernel.
469 */
470 if (!istaskp(proc_ptr) && !isidlehardware(proc_number(proc_ptr))) {
471 pinfo.in_kernel = 0;
472 if (!isuserp(proc_ptr)) {
473 /*
474 * mm, fs and inet are executed in place (i.e. are not mapped
475 * into low memory), so the address space must be made
476 * accessible for them to execute there.
477 */
478 set_process_prot(proc_ptr, PROT_READ_EXEC, PROT_ALL);
479 }
480 kernel_off();
481 }
482 }
483 }
484
485
486 /*
487 * Function: kernel_on
488 *
489 * Makes the kernel text segment r-x, the data segment rwx.
490 */
491 static void kernel_on(void)
492 {
493 mprotect(pinfo.kernel_base, pinfo.kernel_text, PROT_READ_EXEC);
494 mprotect(pinfo.kernel_base + pinfo.kernel_text, pinfo.kernel_data,
495 PROT_ALL);
496 }
497
498
499 /*
500 * Function: kernel_off
501 *
502 * Prepare for leaving the kernel---change protection as per pinfo.
503 * Areas to be left r-x and rwx are assumed to have the correct
504 * protection already.
505 */
506 static void kernel_off(void)
507 {
508 int i;
509
510 for (i = 0 ; i < pinfo.numsegs ; i++) {
511 if (pinfo.prot_info[i].prot != PROT_READ_EXEC &&
512 pinfo.prot_info[i].prot != PROT_ALL) {
513 mprotect(pinfo.prot_info[i].begin, pinfo.prot_info[i].len,
514 pinfo.prot_info[i].prot);
515 }
516 }
517 }
518
519
520 /*
521 * Counterpart to protect_start (see its comment for more info).
522 */
523 static void protect_end(void) {}
524
525
526 /*
527 * Function: set_process_prot
528 * Parameters: p - process whose "physical" memory protection is to change
529 * text_prot - access rights to give to the text segment of p
530 * data_prot - access rights to give to the data/stack/gap
531 * segment of p.
532 *
533 * Set the address space protection for the specified server.
534 */
535 static void set_process_prot(struct proc *p,
536 int text_prot, int data_stack_prot)
537 {
538 mprotect(p->p_map[T].mem_phys << CLICK_SHIFT,
539 p->p_map[T].mem_len << CLICK_SHIFT, text_prot);
540 /*
541 * Assume data, gap, and stack are contiguous
542 */
543 assert(!isuserp(p)); /* User processes have separate D and S segs */
544 mprotect(p->p_map[D].mem_phys << CLICK_SHIFT,
545 (p->p_map[S].mem_phys << CLICK_SHIFT) -
546 (p->p_map[D].mem_phys << CLICK_SHIFT) +
547 (p->p_map[S].mem_len << CLICK_SHIFT), data_stack_prot);
548 }
549
550
551 /*
552 * Function: map_process
553 * Parameter: p - process to map into low address space
554 *
555 * Setup mappings for the text, data and stack segments for process p.
556 * If the prot_lev is NO_PROT then the text segment is mapped rwx; otherwise
557 * it is mapped r-x. "Physical" memory is in fact a mapped file open
558 * on RAM_FD. The offset into the file of a "physical" address can be
559 * determined by subtracting the address of the start of the kernel
560 * from the "physical" address (the kernel starts at offset 0 in the mapped
561 * file).
562 */
563 static void map_process(struct proc *p)
564 {
565 if (mapped_proc == p) return; /* Already there! */
566 if (mapped_proc) {
567 unmap_process(mapped_proc);
568 }
569
570 /*
571 * Setup mappings. The flags 0x11 are MAP_SHARED | MAP_FIXED.
572 */
573 if (SunOS(SYS_mmap, p->p_map[T].mem_vir << CLICK_SHIFT,
574 p->p_map[T].mem_len << CLICK_SHIFT,
575 pinfo.prot_lev == NO_PROT ? PROT_ALL : PROT_READ_EXEC,
576 0x11, RAM_FD,
577 (p->p_map[T].mem_phys << CLICK_SHIFT) - code_base) == -1) {
578 panic("map_process: text segment mapping failed", errno);
579 }
580
581 if (SunOS(SYS_mmap, p->p_map[D].mem_vir << CLICK_SHIFT,
582 p->p_map[D].mem_len << CLICK_SHIFT,
583 PROT_ALL, 0x11, RAM_FD,
584 (p->p_map[D].mem_phys << CLICK_SHIFT) - code_base) == -1) {
585 panic("map_process: data segment mapping failed", errno);
586 }
587
588 if (SunOS(SYS_mmap, p->p_map[S].mem_vir << CLICK_SHIFT,
589 p->p_map[S].mem_len << CLICK_SHIFT,
590 PROT_ALL, 0x11, RAM_FD,
591 (p->p_map[S].mem_phys << CLICK_SHIFT) - code_base) == -1) {
592 panic("map_process: stack segment mapping failed", errno);
593 }
594
595 mapped_proc = p;
596 }
597
598
599 /*
600 * Function: unmap_process
601 * Parameter: p - process to unmap from low address space
602 *
603 * If p is the currently mapped process, then its mappings are removed.
604 * When unmap_process is called from entering_kernel, then p should
605 * always be the mapped process. When unmap_process is called from
606 * mem_released, then this won't always be the case.
607 */
608 static void unmap_process(struct proc *p)
609 {
610 if (p != mapped_proc) return;
611
612 /*
613 * Remove mapping for a user process
614 */
615 if (SunOS(SYS_munmap, p->p_map[T].mem_vir << CLICK_SHIFT,
616 p->p_map[T].mem_len << CLICK_SHIFT) == -1) {
617 panic("unmap_process: text segment unmapping failed", errno);
618 }
619
620 if (SunOS(SYS_munmap, p->p_map[D].mem_vir << CLICK_SHIFT,
621 p->p_map[D].mem_len << CLICK_SHIFT) == -1) {
622 panic("unmap_process: data segment unmapping failed", errno);
623 }
624
625 if (SunOS(SYS_munmap, p->p_map[S].mem_vir << CLICK_SHIFT,
626 p->p_map[S].mem_len << CLICK_SHIFT) == -1) {
627 panic("unmap_process: stack segment unmapping failed", errno);
628 }
629
630 mapped_proc = 0;
631 }
632
633
634 /*
635 * The following are wrappers for the various copying and zeroing functions.
636 * The wrappers ensure that the areas to be read and written are actually
637 * accessible.
638 */
639
640 void zeroclicks(phys_clicks addr, phys_clicks numclicks)
641 {
642 void real_zeroclicks(phys_clicks dest, phys_clicks nclicks);
643
644 set_protect(addr << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_WRITE);
645 real_zeroclicks(addr, numclicks);
646 set_protect(addr << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_NONE);
647 }
648
649
650 void copyclicks(phys_clicks src, phys_clicks dest, phys_clicks numclicks)
651 {
652 void real_copyclicks(phys_clicks src, phys_clicks dest,
653 phys_clicks nclicks);
654
655 set_protect(src << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_READ);
656 set_protect(dest << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_WRITE);
657 real_copyclicks(src, dest, numclicks);
658 set_protect(src << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_NONE);
659 set_protect(dest << CLICK_SHIFT, numclicks << CLICK_SHIFT, PROT_NONE);
660 }
661
662
663 void phys_copy(phys_bytes src, phys_bytes dest, phys_bytes len)
664 {
665 void real_phys_copy(phys_bytes src, phys_bytes dest, phys_bytes n);
666
667 set_protect(src, len, PROT_READ);
668 set_protect(dest, len, PROT_WRITE);
669 real_phys_copy(src, dest, len);
670 set_protect(src, len, PROT_NONE);
671 set_protect(dest, len, PROT_NONE);
672 }
673
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.