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 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 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 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 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 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 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 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 492 print_message(char *message) 493 { 494 if (silent == NOT_SILENT) 495 (void) printf("%s", message); 496 } 497 498 static int 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 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 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 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 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 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 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