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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * Abstract Machine Test; executes memory access tests to show
31 * compliance with Common Criteria object reuse and process address
32 * space separation requirements.
33 */
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <iso/stdlib_iso.h>
37 #include <libelf.h>
38 #include <libintl.h>
39 #include <locale.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/param.h>
44 #include <sys/resource.h>
45 #include <sys/sysmacros.h>
46 #include <sys/types.h>
47 #include <unistd.h>
48 #include <wait.h>
49
50 #define NOT_SILENT 0
51 #define SILENT_MODE 1
52
53 #define CHILD_SLEEP_PERIOD 2
54 #define PARENT_SLEEP_PERIOD 1
55 #define SIG_EVENT SIGUSR1
56
57 #define PASS 0 /* test passed, no SEGV */
58 #define FAIL_ZERO 1 /* expected to read zero, didn't */
59 #define FAIL_SEGV 2 /* expected good read or write, didn't */
60 #define PASS_SEGV 3 /* expected SEGV, got it */
61 #define FAIL_ABORT 4 /* test logic error */
62
63 #define PH_VALID 0 /* arg for probe_hole -- do valid memory */
64 /* access */
65 #define PH_INVALID 1 /* do illegal memory access */
66
67 #define WASTE_PAGES 8 /* a guess at where virgin stack space */
68 /* is likely to exist */
69 #define STACK_SLOP 256 /* a guess at how far below current end */
70 /* of stack I'll find unused space */
71
72 #if !defined(TEXT_DOMAIN)
73 #define TEXT_DOMAIN "SYS_TEST"
74 #endif
75
76 extern int _end; /* first address after the end of initialized data */
77
78 static int data_boundary_test();
79 static void handler(int);
80 static int memory_not_shared_after_use();
81 static int memory_allocation_not_shared();
82 static int memory_type(const char *);
83 static void print_message(char *);
84 static void probe_data_area(void);
85 static void probe_hole(int);
86 static void probe_stack(void);
87 static void probe_text_area(void);
88 static void segv_action(int, siginfo_t *, void *);
89 static void set_handler(int);
90 static int test_stack_end_of_hole();
91 static int text_area_not_writeable();
92
93 static int done_memory_grab = 0;
94 static int silent;
95 static int handler_exit_code;
96
97 /*
98 * Main Routine
99 */
100 int
main(int argc,char * argv[])101 main(int argc, char *argv[])
102 {
103 int fail_count = 0;
104 int status = 0;
105 int bitsize;
106
107 /* Internationalization */
108 (void) setlocale(LC_ALL, "");
109 (void) textdomain(TEXT_DOMAIN);
110
111 silent = NOT_SILENT;
112
113 if (argc == 2) {
114 /* Pull out argument provided */
115 /* -s silent mode, no status or error messages. */
116 if (strncmp(argv[1], "-s", 4) == 0)
117 silent = SILENT_MODE;
118 else {
119 /* Wrong argument */
120 (void) fprintf(stderr, gettext(
121 "Wrong argument, USAGE: amt [-s]\n"));
122 exit(EXIT_FAILURE);
123 }
124 } else if (argc != 1) {
125 /* Illegal number of arguments. */
126 (void) fprintf(stderr, gettext(
127 "Wrong usage, USAGE: amt [-s]\n"));
128 exit(EXIT_FAILURE);
129 }
130 bitsize = memory_type(argv[0]);
131
132 if (silent == NOT_SILENT)
133 (void) printf(gettext(
134 "\n\nAMT Test Program -- %d bit application\n"
135 "================\n"), bitsize);
136 /*
137 * test_stack_end_of_hole must be the first test, or the stack
138 * is of an unknown size.
139 */
140 if ((status = test_stack_end_of_hole()) == EXIT_FAILURE) {
141 /* Normal fail */
142 fail_count++;
143 print_message(gettext("TEST 1 FAILED\n"));
144 } else if (status == FAIL_ABORT) {
145 /* Test logic failure */
146 fail_count++;
147 print_message(gettext("FAIL: Logic error in test\n"));
148 } else if (status == EXIT_SUCCESS)
149 print_message(gettext("TEST 1 PASSED\n"));
150
151 /* Carry out test 2 */
152 if (data_boundary_test() != EXIT_SUCCESS) {
153 fail_count++;
154 print_message(gettext("TEST 2 FAILED\n"));
155 } else
156 print_message(gettext("TEST 2 PASSED\n"));
157
158 /* Carry out test 3 */
159 if (text_area_not_writeable() != EXIT_SUCCESS) {
160 fail_count++;
161 print_message(gettext("TEST 3 FAILED\n"));
162 } else
163 print_message(gettext("TEST 3 PASSED\n"));
164
165 /* Carry out test 4 */
166 if (memory_not_shared_after_use() != EXIT_SUCCESS) {
167 fail_count++;
168 print_message(gettext("TEST 4 FAILED\n"));
169 } else
170 print_message(gettext("TEST 4 PASSED\n"));
171
172 /* Carry out test 5 */
173 if (memory_allocation_not_shared() != EXIT_SUCCESS) {
174 fail_count++;
175 print_message(gettext("TEST 5 FAILED\n"));
176 } else
177 print_message(gettext("TEST 5 PASSED\n"));
178
179 if (silent == NOT_SILENT) {
180 if (fail_count > 0)
181 (void) printf(gettext("\n %d TESTS FAILED\n\n"),
182 fail_count);
183 else
184 (void) printf(gettext("\nTESTS SUCCEEDED\n\n"));
185 }
186 return (fail_count);
187 }
188
189 /*
190 * Test the data boundaries. First test inside the data area at the boundary
191 * of the "hole" area. Second test inside the data area at the text area
192 * boundary. Both should pass.
193 */
194 static int
data_boundary_test()195 data_boundary_test()
196 {
197 int exit_status = EXIT_SUCCESS;
198 pid_t pid;
199 int status;
200
201 print_message(gettext("\n\nTest 2- Data Side Boundary Test.\n"));
202
203 if ((pid = fork()) == -1) {
204 print_message(gettext("Fork failed\n"));
205 return (EXIT_FAILURE);
206 } else if (pid == 0) { /* Am I my child? */
207 set_handler(SIGSEGV);
208
209 /* probe_data_area() does exit() */
210 probe_data_area();
211 }
212 /* still parent */
213 (void) wait(&status);
214 status = WEXITSTATUS(status);
215
216 if (status == PASS)
217 print_message(gettext(
218 "PASS: Successful read/write in data area.\n"));
219 else if (status == FAIL_SEGV) {
220 print_message(gettext(
221 "FAIL: Caught a segmentation fault while "
222 "attempting to write to the data area.\n"));
223 exit_status = EXIT_FAILURE;
224 } else {
225 (void) printf(gettext("Test program failure: %d\n"),
226 status);
227 exit_status = EXIT_FAILURE;
228 }
229 return (exit_status);
230 }
231
232 static void
probe_data_area()233 probe_data_area()
234 {
235 int *p;
236 /* LINTED */
237 volatile int p1;
238 void *address;
239
240 /* set handler status */
241 handler_exit_code = FAIL_SEGV;
242
243 /*
244 * Get an address in the data area, near to the "hole".
245 * sbrk returns prior address value; rather than calculating
246 * the sbrk result, sbrk is called twice, so address points
247 * to the new end of data
248 */
249 (void) sbrk(PAGESIZE);
250 address = sbrk(0);
251 /*
252 * Get to the inside edge of a page boundary
253 * two integer words short of a new page
254 */
255 p = ((int *)P2ROUNDUP((uintptr_t)address, PAGESIZE)) - 2;
256
257 /* Try writing to it, shouldn't cause a segmentation fault. */
258 *p = 9999;
259
260 /* Should be able to read back with no problems. */
261 p1 = *p;
262
263 /*
264 * Get an address near the text area boundary, but in the data
265 * area. _etext rounded up a page isn't correct since the
266 * initialized data area isn't writeable.
267 *
268 * Future versions should consider handling initialized data
269 * separately -- writing to initialized data should generate
270 * a fault.
271 */
272 p = &_end;
273
274 /* Try writing to it, should succeed. */
275 *p = 9898;
276
277 /* Should be able to read back with no problems. */
278 p1 = *p;
279
280 exit(EXIT_SUCCESS);
281 }
282
283 /*
284 * Test that we cannot write to the text area. An attempt to write to
285 * the text area will result in a segmentation fault. So if we catch it,
286 * test has succeed, else it has failed.
287 */
288 static int
text_area_not_writeable()289 text_area_not_writeable()
290 {
291 int exit_status = EXIT_SUCCESS;
292 pid_t pid;
293 int status;
294
295 print_message(gettext(
296 "\n\nTest 3- Text Area Not Writeable\n"
297 "Verify that a write to the text space does not cause "
298 "a write to the executable\n"
299 "file from which it came, or to another process which "
300 "shares that text.\n"));
301
302 if ((pid = fork()) == -1) {
303 print_message(gettext("Fork failed\n"));
304 return (EXIT_FAILURE);
305 } else if (pid == 0) { /* Am I my child? */
306 set_handler(SIGSEGV);
307
308 /* probe_text_area() does exit() */
309 probe_text_area();
310 }
311 /* still parent */
312 (void) wait(&status);
313 status = WEXITSTATUS(status);
314
315 if (status == PASS) {
316 print_message(gettext(
317 "FAIL: We did not cause a segmentation fault.\n"));
318 exit_status = EXIT_FAILURE;
319 } else if (status == FAIL_SEGV) {
320 print_message(gettext(
321 "PASS: Caught the segmentation fault, "
322 "meaning we can't write to text area.\n"));
323 } else {
324 (void) printf(gettext(
325 "Test program failure: %d\n"), status);
326 exit_status = EXIT_FAILURE;
327 }
328 return (exit_status);
329 }
330
331 /*
332 * write to text area, trigger a SEGV
333 */
334 static void
probe_text_area()335 probe_text_area()
336 {
337 handler_exit_code = FAIL_SEGV;
338 *(caddr_t)probe_text_area = 0xff;
339 exit(EXIT_FAILURE);
340 }
341
342 /*
343 * Test that when we set some values and fork a process, when the child
344 * writes to these inherited values, the parents copies are not changed.
345 */
346 static int
memory_not_shared_after_use()347 memory_not_shared_after_use()
348 {
349 pid_t pid;
350 int x = 1000;
351 int exit_status = EXIT_SUCCESS;
352
353 print_message(gettext("\n\nTest 4- Memory Not Shared After Write\n"
354 "Verify that anonymous memory initially shared by two "
355 "processes (e.g. after a\n"
356 "fork) is not shared after either process writes "
357 "to it.\n"));
358
359 if ((pid = fork()) == -1) {
360 print_message(gettext("Fork failed\n"));
361 return (EXIT_FAILURE);
362 } else if (pid == 0) { /* I am the child. */
363 /*
364 * Change child value; this should not change
365 * parent value.
366 */
367 x = 2000;
368
369 /* Wait for parent to test value */
370 (void) sleep(CHILD_SLEEP_PERIOD);
371
372 exit(EXIT_SUCCESS);
373 }
374 /* Wait for child to do its stuff. */
375 (void) sleep(PARENT_SLEEP_PERIOD);
376
377 if (x == 1000)
378 exit_status = EXIT_SUCCESS;
379 else
380 exit_status = EXIT_FAILURE;
381
382 return (exit_status);
383 }
384
385 /*
386 * If we fork a process and then allocate some memory in that process,
387 * we should not see any memory changes in the parent.
388 */
389 static int
memory_allocation_not_shared()390 memory_allocation_not_shared()
391 {
392 pid_t pid;
393 pid_t parent_pid;
394 int exit_status = 0;
395 caddr_t address;
396 caddr_t hole_start;
397 caddr_t hole_after;
398 void (*old_handler) ();
399
400 print_message(gettext(
401 "\n\nTest 5- Memory Allocation is Not Shared\n"
402 "Verify that newly allocated memory in one of two "
403 "processes created by forking\n"
404 "does not result in newly allocated memory in the other.\n"));
405
406 /* Save Size of data area and 1st block address of "hole" */
407 hole_start = (caddr_t)sbrk(0);
408
409 if (silent == NOT_SILENT)
410 (void) printf(gettext(
411 "Parent address of hole before child change: %08X\n"),
412 hole_start);
413
414 /* Set handler for signal SIG_EVENT (define at start) */
415 old_handler = signal(SIG_EVENT, &handler);
416 if (old_handler == SIG_ERR) {
417 print_message(gettext(
418 "Can't establish signal handler, test failed\n"));
419 return (EXIT_FAILURE);
420 }
421
422 if ((pid = fork()) == -1) {
423 print_message(gettext("Fork failed\n"));
424 return (EXIT_FAILURE);
425 } else if (pid == 0) { /* We are the child. */
426 address = sbrk(0);
427 if (silent == NOT_SILENT)
428 (void) printf(gettext(
429 "Child end of hole before change: %08X\n"),
430 address);
431
432 if (brk((address+PAGESIZE)) != 0) {
433 print_message(gettext(
434 "Can't change start of hole address.\n"));
435 exit(EXIT_FAILURE);
436 }
437
438 address = sbrk(0);
439 if (silent == NOT_SILENT)
440 (void) printf(gettext(
441 "Child end of hole after change: %08X\n"),
442 address);
443
444 /* Tell the parent we're done. */
445 parent_pid = getppid();
446 if (sigsend(P_PID, parent_pid, SIG_EVENT) != 0) {
447 print_message(gettext("Can't send signal to parent, "
448 "test failed\n"));
449 exit(EXIT_FAILURE);
450 }
451
452 /* Sleep before exiting to allow parent to finish processing. */
453 (void) sleep(CHILD_SLEEP_PERIOD);
454 exit(EXIT_SUCCESS);
455 }
456 /* Wait for child to do its work. */
457 (void) sleep(PARENT_SLEEP_PERIOD);
458
459 if (done_memory_grab != 1) {
460 print_message(gettext(
461 "Child failed to do memory alterations, "
462 "exiting\n"));
463 return (EXIT_FAILURE);
464 }
465
466 hole_after = sbrk(0);
467 if (silent == NOT_SILENT)
468 (void) printf(gettext(
469 "Parent address of hole after child change: "
470 "%08X\n"), hole_after);
471
472 /* Test size of hole and data region. */
473 if (hole_start == hole_after)
474 print_message(gettext(
475 "PASS: Hole is same size in parent.\n"));
476 else {
477 print_message(gettext(
478 "FAIL: Hole is a different size.\n"));
479 exit_status = EXIT_FAILURE;
480 }
481
482 /* Wait for child to finish. */
483 (void) wait(0);
484
485 if (signal(SIG_EVENT, old_handler) == SIG_ERR) {
486 print_message(gettext("Couldn't put back old signal handler, "
487 "test failed.\n"));
488 return (EXIT_FAILURE);
489 }
490 return (exit_status);
491 }
492
493 static void
print_message(char * message)494 print_message(char *message)
495 {
496 if (silent == NOT_SILENT)
497 (void) printf("%s", message);
498 }
499
500 static int
test_stack_end_of_hole()501 test_stack_end_of_hole()
502 {
503 pid_t pid;
504 int status;
505 int exit_status = EXIT_SUCCESS;
506
507 print_message(gettext("\n\nTest 1- stack Side Boundary Test\n"));
508
509 /* sub test 1: the space the stack grows into is zero */
510
511 if ((pid = fork()) == -1) {
512 print_message(gettext("Fork failed\n"));
513 return (EXIT_FAILURE);
514 } else if (pid == 0) { /* Am I my child? */
515 set_handler(SIGSEGV);
516
517 /* probe_stack() does exit */
518 probe_stack();
519 }
520 /* still parent */
521 (void) wait(&status);
522 status = WEXITSTATUS(status);
523
524 if (status == FAIL_ZERO) {
525 print_message(gettext("Fail with non-zero read.\n"));
526 exit_status = EXIT_FAILURE;
527 } else if (status != PASS) {
528 print_message(gettext("Test program failure\n"));
529 exit_status = EXIT_FAILURE;
530 }
531 /* sub test 2: the space in hole is not readable */
532
533 if ((pid = fork()) == -1) {
534 print_message(gettext("Fork failed\n"));
535 return (EXIT_FAILURE);
536 } else if (pid == 0) { /* Am I my child? */
537 set_handler(SIGSEGV);
538
539 /* probe_hole does exit */
540 probe_hole(PH_INVALID);
541 }
542 /* still parent */
543 (void) wait(&status);
544 status = WEXITSTATUS(status);
545
546 if (status == FAIL_SEGV) {
547 print_message(
548 gettext("Fail (SEGV expected, not received).\n"));
549 exit_status = EXIT_FAILURE;
550 } else if (status != PASS_SEGV) {
551 print_message(gettext("Test program failure.\n"));
552 exit_status = EXIT_FAILURE;
553 }
554
555 /* sub test 3: the space in new page below hole is zero */
556
557 if ((pid = fork()) == -1) {
558 print_message(gettext("Fork failed\n"));
559 return (EXIT_FAILURE);
560 } else if (pid == 0) { /* Am I my child? */
561 set_handler(SIGSEGV);
562
563 /* probe_hole does exit */
564 probe_hole(PH_VALID);
565 }
566 /* still parent */
567 (void) wait(&status);
568 status = WEXITSTATUS(status);
569
570 if (status == FAIL_SEGV) {
571 print_message(gettext("Fail (got SEGV).\n"));
572 exit_status = EXIT_FAILURE;
573 } else if (status != PASS) {
574 print_message(gettext("Test program failure.\n"));
575 exit_status = EXIT_FAILURE;
576 }
577 return (exit_status);
578 }
579
580
581 /*
582 * set_handler
583 */
584 static void
set_handler(int sig)585 set_handler(int sig)
586 {
587 struct sigaction act;
588
589 act.sa_handler = NULL;
590 act.sa_flags = SA_SIGINFO;
591 act.sa_sigaction = segv_action;
592 (void) sigemptyset(&(act.sa_mask));
593
594 if (sigaction(sig, &act, NULL) < 0) {
595 if (silent == NOT_SILENT) {
596 (void) fprintf(stderr, gettext(
597 "sigaction() returned error: %s\n"),
598 strerror(errno));
599 }
600 exit(EXIT_FAILURE);
601 }
602 }
603
604
605 /*ARGSUSED*/
606 static void
segv_action(int which_sig,siginfo_t * t1,void * t2)607 segv_action(int which_sig, siginfo_t *t1, void *t2)
608 {
609 exit(handler_exit_code);
610 }
611
612 /*
613 * probe_stack
614 *
615 * Warning -- if you do a printf or fprintf prior to the actual
616 * reading from the stack, you've changed the stack to an unknown
617 * state. (stack memory isn't free'd automatically and this function
618 * needs to touch virgin stack space.)
619 */
620 static void
probe_stack(void)621 probe_stack(void)
622 {
623 unsigned char *end; /* end of stack */
624 unsigned char probe;
625 long i;
626 int j;
627 unsigned char last_fail, *last_fail_address;
628 unsigned char mark = 0xAA; /* roughly the end of stack */
629 handler_exit_code = FAIL_SEGV;
630
631 end = &mark;
632 /* stack growth is negative */
633 end -= (WASTE_PAGES * PAGESIZE) + STACK_SLOP;
634
635 for (i = 0, j = 0; i < PAGESIZE; i++) {
636 if ((probe = *end) != 0) {
637 j++;
638 last_fail = probe;
639 last_fail_address = end;
640 }
641 end--;
642 }
643
644 if (j != 0) {
645 if (silent == NOT_SILENT)
646 (void) fprintf(stderr, gettext(
647 "probe_stack failed. address=0x%08X; "
648 "probe=0x%02X; content = %d\n"),
649 (caddr_t)last_fail_address, last_fail, j);
650
651 exit(FAIL_ZERO); /* test failed at least once */
652 }
653 exit(EXIT_SUCCESS);
654 }
655
656 static void
probe_hole(int test_type)657 probe_hole(int test_type)
658 {
659 long i;
660 /* LINTED */
661 volatile unsigned char probe;
662 unsigned char *probe_adr;
663 void *address;
664
665 address = sbrk(0); /* current end data + 1 */
666
667 if (address == (void *)-1) {
668 print_message(gettext("Test program logic error\n"));
669 exit(FAIL_ABORT);
670 }
671 if (test_type == PH_VALID) {
672 /* show that access works inside the hole */
673 handler_exit_code = FAIL_SEGV;
674
675 probe_adr = (unsigned char *)address - sizeof (char);
676
677 for (i = 0; i < PAGESIZE; i++)
678 probe = *probe_adr--;
679
680 exit(EXIT_SUCCESS);
681 } else {
682 /* show that a trap occurs in the hole */
683 handler_exit_code = PASS_SEGV;
684
685 address = (void *)P2ROUNDUP((uintptr_t)address, PAGESIZE);
686 probe_adr = (unsigned char *)address;
687
688 probe = *probe_adr;
689 exit(FAIL_SEGV); /* expected SEGV, didn't get it */
690 }
691 }
692
693 /*
694 * Catch signal, child to parent
695 */
696 /*ARGSUSED*/
697 void
handler(int signal)698 handler(int signal)
699 {
700 done_memory_grab = 1;
701 }
702 /*
703 * memory_type: Determine whether a given executable file is compiled
704 * as 32 or 64 bit.
705 *
706 * The following code was stolen from isainfo (1)
707 */
708
709 static int
memory_type(const char * path)710 memory_type(const char *path) {
711 char *idarray;
712 Elf *elf;
713 int d;
714 int bits = 0;
715
716 if ((d = open(path, O_RDONLY)) < 0) {
717 (void) fprintf(stderr,
718 "cannot open: %s -- %s\n",
719 path, strerror(errno));
720 return (bits);
721 }
722
723 if (elf_version(EV_CURRENT) == EV_NONE) {
724 (void) fprintf(stderr,
725 "internal error: ELF library out of date?\n");
726 (void) close(d);
727 return (bits);
728 }
729
730 elf = elf_begin(d, ELF_C_READ, (Elf *)0);
731 if (elf_kind(elf) != ELF_K_ELF) {
732 (void) elf_end(elf);
733 (void) close(d);
734 return (bits);
735 }
736
737 idarray = elf_getident(elf, 0);
738
739 if (idarray[EI_CLASS] == ELFCLASS32) {
740 bits = 32;
741 } else if (idarray[EI_CLASS] == ELFCLASS64) {
742 bits = 64;
743 }
744
745 (void) elf_end(elf);
746 (void) close(d);
747 return (bits);
748 }
749