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