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