1/* 2 * xnu_quick_test - this tool will do a quick test of every (well, to be 3 * honest most) system calls we support in xnu. 4 * 5 * WARNING - this is not meant to be a full regression test of all the 6 * system calls. The intent is to have a quick test of each system call that 7 * can be run very easily and quickly when a new kerenl is built. 8 * 9 * This tool is meant to grow as we find xnu problems that could have be 10 * caught before we submit to a build train. So please add more tests and 11 * make the existing ones better. Some of the original tests are nothing 12 * more than place holders and quite lame. Just keep in mind that the tool 13 * should run as fast as possible. If it gets too slow then most people 14 * will stop running it. 15 * 16 * LP64 testing tip - when adding or modifying tests, keep in mind the 17 * variants in the LP64 world. If xnu gets passed a structure the varies in 18 * size between 32 and 64-bit processes, try to test that a field in the 19 * sructure contains valid data. For example if we know foo structure 20 * looks like: 21 * struct foo { 22 * int an_int; 23 * long a_long; 24 * int another_int; 25 * } 26 * And if we know what another_int should contain then test for the known 27 * value since it's offset will vary depending on whether the calling process 28 * is 32 or 64 bits. 29 * 30 * NOTE - we have several workarounds and test exceptions for some 31 * outstanding bugs in xnu. All the workarounds are marked with "todo" and 32 * some comments noting the radar number of the offending bug. Do a seach 33 * for "todo" in the source files for this project to locate which tests have 34 * known failures. And please tag any new exceptions you find with "todo" 35 * in the comment and the radar number of the bug. 36 */ 37 38#include <errno.h> 39#include <fcntl.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <time.h> 44#include <unistd.h> 45#include <sys/mount.h> 46#include <sys/param.h> 47#include <sys/select.h> 48#include <sys/stat.h> 49#include <sys/syslimits.h> 50#include <sys/types.h> 51#include <sys/ucred.h> 52#include <sys/uio.h> 53#include "tests.h" 54 55#if !TARGET_OS_EMBEDDED 56#include <XILog/XILog.h> 57#endif 58 59 60 61/* our table of tests to run */ 62struct test_entry g_tests[] = 63{ 64 {1, &syscall_test, NULL, "syscall"}, 65 {1, &fork_wait4_exit_test, NULL, "fork, wait4, exit"}, 66 {1, &read_write_test, NULL, "fsync, ftruncate, lseek, pread, pwrite, read, readv, truncate, write, writev"}, 67 {1, &open_close_test, NULL, "close, fpathconf, fstat, open, pathconf"}, 68 {1, &link_stat_unlink_test, NULL, "link, stat, unlink"}, 69 {1, &chdir_fchdir_test, NULL, "chdir, fchdir"}, 70 {1, &access_chmod_fchmod_test, NULL, "access, chmod, fchmod"}, 71 {1, &chown_fchown_lchown_lstat_symlink_test, NULL, "chown, fchown, lchown, lstat, readlink, symlink"}, 72 {1, &fs_stat_tests, NULL, "fstatfs, getattrlist, getfsstat, statfs, getfsstat64, statfs64, fstatfs64"}, 73 {1, &getpid_getppid_pipe_test, NULL, "getpid, getppid, pipe"}, 74 {1, &uid_tests, NULL, "getauid, gettid, getuid, geteuid, issetugid, setauid, seteuid, settid, settid_with_pid, setuid"}, 75 {1, &mkdir_rmdir_umask_test, NULL, "mkdir, rmdir, umask"}, 76 {1, &mknod_sync_test, NULL, "mknod, sync"}, 77 {1, &socket2_tests, NULL, "fsync, getsockopt, poll, select, setsockopt, socketpair"}, 78 {1, &socket_tests, NULL, "accept, bind, connect, getpeername, getsockname, listen, socket, recvmsg, sendmsg, sendto"}, 79 {1, &chflags_fchflags_test, NULL, "chflags, fchflags"}, 80 {1, &execve_kill_vfork_test, NULL, "kill, vfork, execve, posix_spawn"}, 81 {1, &groups_test, NULL, "getegid, getgid, getgroups, setegid, setgid, setgroups"}, 82 {1, &dup_test, NULL, "dup, dup2, getdtablesize"}, 83 {1, &getrusage_profil_test, NULL, "getrusage, profil"}, 84 {1, &signals_test, NULL, "getitimer, setitimer, sigaction, sigpending, sigprocmask, sigsuspend, sigwait"}, 85 {1, &acct_test, NULL, "acct"}, 86 {1, &ioctl_test, NULL, "ioctl"}, 87 {1, &chroot_test, NULL, "chroot"}, 88 {1, &memory_tests, NULL, "madvise, mincore, minherit, mlock, mlock, mmap, mprotect, msync, munmap"}, 89 {1, &process_group_test, NULL, "getpgrp, getpgid, getsid, setpgid, setpgrp, setsid"}, 90 {1, &fcntl_test, NULL, "fcntl"}, 91 {1, &getlogin_setlogin_test, NULL, "getlogin, setlogin"}, 92 {1, &getpriority_setpriority_test, NULL, "getpriority, setpriority"}, 93 {1, &time_tests, NULL, "futimes, gettimeofday, settimeofday, utimes"}, 94 {1, &rename_test, NULL, "rename, stat"}, 95 {1, &locking_test, NULL, "flock"}, 96 {1, &mkfifo_test, NULL, "mkfifo, read, write"}, 97 {1, "actl_test, NULL, "quotactl"}, 98 {1, &limit_tests, NULL, "getrlimit, setrlimit"}, 99 {1, &directory_tests, NULL, "getattrlist, getdirentries, getdirentriesattr, setattrlist"}, 100 {1, &exchangedata_test, NULL, "exchangedata"}, 101 {1, &searchfs_test, NULL, "searchfs"}, 102 {1, &sema2_tests, NULL, "sem_close, sem_open, sem_post, sem_trywait, sem_unlink, sem_wait"}, 103 {1, &sema_tests, NULL, "semctl, semget, semop"}, 104 {1, &bsd_shm_tests, NULL, "shm_open, shm_unlink"}, 105 {1, &shm_tests, NULL, "shmat, shmctl, shmdt, shmget"}, 106 {1, &xattr_tests, NULL, "fgetxattr, flistxattr, fremovexattr, fsetxattr, getxattr, listxattr, removexattr, setxattr"}, 107 {1, &aio_tests, NULL, "aio_cancel, aio_error, aio_read, aio_return, aio_suspend, aio_write, fcntl, lio_listio"}, 108 {1, &kqueue_tests, NULL, "kevent, kqueue"}, 109 {1, &message_queue_tests, NULL, "msgctl, msgget, msgrcv, msgsnd"}, 110 {0, NULL, NULL, "last one"} 111}; 112 113static void create_target_directory( const char * the_targetp ); 114static void list_all_tests( void ); 115static void mark_tests_to_run( long my_start, long my_end ); 116static int parse_tests_to_run( int argc, const char * argv[], int * indexp ); 117static void usage( void ); 118 119/* globals */ 120long g_max_failures = 0; 121int g_skip_setuid_tests = 0; 122int g_xilog_active = 0; 123const char * g_cmd_namep; 124char g_target_path[ PATH_MAX ]; 125int g_is_under_rosetta = 0; 126 127int main( int argc, const char * argv[] ) 128{ 129 int my_tests_count, i; 130 int err; 131 int my_failures = 0; 132 int list_the_tests = 0; 133 const char * my_targetp; 134 time_t my_start_time, my_end_time; 135 struct stat my_stat_buf; 136 char my_buffer[64]; 137 /* vars for XILog */ 138#if !TARGET_OS_EMBEDDED 139 XILogRef logRef; 140 char *logPath = ""; 141 char *config = NULL; 142 int echo = 0; 143 int xml = 0; 144#endif 145 sranddev( ); /* set up seed for our random name generator */ 146 g_cmd_namep = argv[0]; 147 148 /* NOTE - code in create_target_directory will append '/' if it is necessary */ 149 my_targetp = getenv("TMPDIR"); 150 if ( my_targetp == NULL ) 151 my_targetp = "/tmp"; 152 153 /* make sure our executable is owned by root and has set uid bit */ 154 err = stat( g_cmd_namep, &my_stat_buf ); 155 if ( err != 0 ) { 156 err = errno; 157 printf( "stat call on our executable failed - \"%s\" \n", g_cmd_namep ); 158 printf( " failed with error %d - \"%s\" \n", err, strerror( err) ); 159 exit( -1 ); 160 } 161 if ( my_stat_buf.st_uid != 0 || (my_stat_buf.st_mode & S_ISUID) == 0 ) { 162 printf( "executable file - \"%s\" \n", g_cmd_namep ); 163 printf( "does not have correct owner (must be root) or setuid bit is not set \n" ); 164 exit( -1 ); 165 } 166 167 /* parse input parameters */ 168 for ( i = 1; i < argc; i++ ) { 169 if ( strcmp( argv[i], "-u" ) == 0 ) { 170 usage( ); 171 } 172 if ( strcmp( argv[i], "-t" ) == 0 || 173 strcmp( argv[i], "-target" ) == 0 ) { 174 if ( ++i >= argc ) { 175 printf( "invalid target parameter \n" ); 176 usage( ); 177 } 178 /* verify our target directory exists */ 179 my_targetp = argv[i]; 180 err = stat( my_targetp, &my_stat_buf ); 181 if ( err != 0 || S_ISDIR(my_stat_buf.st_mode) == 0 ) { 182 printf( "invalid target path \n" ); 183 if ( err != 0 ) { 184 printf( "stat call failed with error %d - \"%s\" \n", errno, strerror( errno) ); 185 } 186 usage( ); 187 } 188 continue; 189 } 190 if ( strcmp( argv[i], "-f" ) == 0 || 191 strcmp( argv[i], "-failures" ) == 0 ) { 192 if ( ++i >= argc ) { 193 printf( "invalid failures parameter \n" ); 194 usage( ); 195 } 196 197 /* get our max number of failures */ 198 g_max_failures = strtol( argv[i], NULL, 10 ); 199 continue; 200 } 201 if ( strcmp( argv[i], "-l" ) == 0 || 202 strcmp( argv[i], "-list" ) == 0 ) { 203 /* list all the tests this tool will do. 204 */ 205 list_the_tests = 1; 206 continue; 207 } 208 if ( strcmp( argv[i], "-r" ) == 0 || 209 strcmp( argv[i], "-run" ) == 0 ) { 210 if ( ++i >= argc ) { 211 printf( "invalid run tests parameter \n" ); 212 usage( ); 213 } 214 215 /* get which tests to run */ 216 if ( parse_tests_to_run( argc, argv, &i ) != 0 ) { 217 printf( "invalid run tests parameter \n" ); 218 usage( ); 219 } 220 continue; 221 } 222 if ( strcmp( argv[i], "-s" ) == 0 || 223 strcmp( argv[i], "-skip" ) == 0 ) { 224 /* set that want to skip the setuid related tests - this is useful for debgugging since since I can't 225 * get setuid tests to work while in gdb. 226 */ 227 g_skip_setuid_tests = 1; 228 continue; 229 } 230#if !TARGET_OS_EMBEDDED 231 if ( strcmp( argv[i], "-x" ) == 0 || 232 strcmp( argv[i], "-xilog" ) == 0 ) { 233 g_xilog_active = 1; 234 continue; 235 } 236#endif 237 printf( "invalid argument \"%s\" \n", argv[i] ); 238 usage( ); 239 } 240 241 /* done parsing. 242 */ 243 244#ifdef __ppc__ 245 /* determine if we are running under Rosetta 246 */ 247 { 248 int val = 0; 249 size_t size = sizeof val; 250 if (sysctlbyname("sysctl.proc_native", &val, &size, NULL, 0) == -1) 251 g_is_under_rosetta = 0; 252 else 253 g_is_under_rosetta = val ? 0 : 1; 254 } 255#endif 256 257 if ( list_the_tests != 0 ) { 258 list_all_tests( ); 259 return 0; 260 } 261#if !TARGET_OS_EMBEDDED 262 if (g_xilog_active == 1) { 263 logRef = XILogOpenLogExtended( logPath, "xnu_quick_test", "com.apple.coreos", 264 config, xml, echo, NULL, "ResultOwner", 265 "com.apple.coreos", NULL ); 266 if( logRef == NULL ) { 267 fprintf(stderr,"Couldn't create log: %s",logPath); 268 exit(-1); 269 } 270 } 271#endif 272 273 /* build a test target directory that we use as our path to create any test 274 * files and directories. 275 */ 276 create_target_directory( my_targetp ); 277 printf( "Will allow %d failures before testing is aborted \n", g_max_failures ); 278 279 if (g_is_under_rosetta) { 280 printf("Running under Rosetta.\n"); 281 } 282 283 my_start_time = time( NULL ); 284 printf( "\nBegin testing - %s \n", ctime_r( &my_start_time, &my_buffer[0] ) ); 285 286 /* run each test that is marked to run in our table until we complete all of them or 287 * hit the maximum number of failures. 288 */ 289 my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] )); 290 for ( i = 0; i < (my_tests_count - 1); i++ ) { 291 int my_err; 292 test_entryp my_testp; 293 294 my_testp = &g_tests[i]; 295 if ( my_testp->test_run_it == 0 || my_testp->test_routine == NULL ) 296 continue; 297#if !TARGET_OS_EMBEDDED 298 if (g_xilog_active == 1) { 299 XILogBeginTestCase( logRef, my_testp->test_infop, my_testp->test_infop ); 300 XILogMsg( "test #%d - %s \n", (i + 1), my_testp->test_infop ); 301 } 302#endif 303 printf( "test #%d - %s \n", (i + 1), my_testp->test_infop ); 304 my_err = my_testp->test_routine( my_testp->test_input ); 305 if ( my_err != 0 ) { 306 printf("\t--> FAILED \n"); 307#if !TARGET_OS_EMBEDDED 308 if (g_xilog_active == 1) { 309 XILogMsg("SysCall %s failed", my_testp->test_infop); 310 XILogErr("Result %d", my_err); 311 } 312#endif 313 my_failures++; 314 if ( my_failures > g_max_failures ) { 315#if !TARGET_OS_EMBEDDED 316 if (g_xilog_active == 1) { 317 XILogEndTestCase( logRef, kXILogTestPassOnErrorLevel ); 318 } 319#endif 320 printf( "\n too many failures - test aborted \n" ); 321 goto exit_this_routine; 322 } 323 } 324#if !TARGET_OS_EMBEDDED 325 if (g_xilog_active == 1) { 326 XILogEndTestCase(logRef, kXILogTestPassOnErrorLevel); 327 } 328#endif 329 } 330 331exit_this_routine: 332 my_end_time = time( NULL ); 333 printf( "\nEnd testing - %s \n", ctime_r( &my_end_time, &my_buffer[0] ) ); 334 335 /* clean up our test directory */ 336 rmdir( &g_target_path[0] ); 337 338#if !TARGET_OS_EMBEDDED 339 if (g_xilog_active == 1) { 340 XILogCloseLog(logRef); 341 } 342#endif 343 344 return 0; 345} /* main */ 346 347 348/* 349 * parse_tests_to_run - parse the -run argument parameters. the run argument tells us which tests the user 350 * wants to run. we accept ranges (example 1 - 44) and runs of tests (example 2, 33, 34, 100) or a mix of 351 * both (example 1, 44 - 100, 200, 250) 352 */ 353static int parse_tests_to_run( int argc, const char * argv[], int * indexp ) 354{ 355 int my_tests_count, is_range = 0, i; 356 const char * my_ptr; 357 char * my_temp_ptr; 358 long my_first_test_number, my_last_test_number; 359 char my_buffer[ 128 ]; 360 361 /* set tests table to not run any tests then go back and set the specific tests the caller asked for */ 362 my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] )); 363 for ( i = 0; i < (my_tests_count - 1); i++ ) { 364 g_tests[ i ].test_run_it = 0; 365 } 366 367 for ( i = *indexp; i < argc; i++ ) { 368 my_ptr = argv[ i ]; 369 if ( strlen( my_ptr ) > 1 && *my_ptr == '-' && isalpha( *(my_ptr + 1) ) ) { 370 /* we have hit a new argument - need to make sure caller uses this argument on the next 371 * pass through its parse loop (which will bump the index value so we want to be one less 372 * than the actual index). 373 */ 374 *indexp = (i - 1); 375 return 0; 376 } 377 378 if ( strlen( my_ptr ) == 1 && *my_ptr == '-' ) { 379 /* we are dealing with a range of tests, for example: 33 - 44 */ 380 is_range = 1; 381 continue; 382 } 383 384 if ( strlen( my_ptr ) > (sizeof( my_buffer ) - 1) ) { 385 printf( "-run argument has too many test parameters (max of %d characters) \n", sizeof( my_buffer ) ); 386 return -1; 387 } 388 /* get a local copy of the parameter string to work with - break range into two strings */ 389 strcpy( &my_buffer[0], my_ptr ); 390 391 my_temp_ptr = strrchr( &my_buffer[0], '-' ); 392 if ( my_temp_ptr != NULL ) { 393 /* we are dealing with a range of tests with no white space, for example: 33-44 or 33- 44 */ 394 my_temp_ptr = strrchr( &my_buffer[0], '-' ); 395 *my_temp_ptr = 0x00; 396 my_first_test_number = strtol( &my_buffer[0], NULL, 10 ); 397 if ( *(my_temp_ptr + 1) == 0x00 ) { 398 /* we hit the case where the range indicator is at the end of one string, for example: 33- */ 399 is_range = 1; 400 continue; 401 } 402 my_last_test_number = strtol( (my_temp_ptr + 1), NULL, 10 ); 403 if ( my_first_test_number < 1 || my_first_test_number > my_last_test_number ) { 404 printf( "-run argument has invalid range parmeters \n" ); 405 return -1; 406 } 407 mark_tests_to_run( my_first_test_number, my_last_test_number ); 408 is_range = 0; 409 continue; 410 } 411 412 if ( is_range ) { 413 /* should be the second part of the test number range */ 414 my_last_test_number = strtol( &my_buffer[0], NULL, 10 ); 415 if ( my_first_test_number < 1 || my_first_test_number > my_last_test_number ) { 416 printf( "-run argument has invalid range parmeters \n" ); 417 return -1; 418 } 419 420 mark_tests_to_run( my_first_test_number, my_last_test_number ); 421 is_range = 0; 422 continue; 423 } 424 else { 425 my_first_test_number = strtol( &my_buffer[0], NULL, 10 ); 426 if ( my_first_test_number < 1 ) { 427 printf( "-run argument has invalid test number parameter \n" ); 428 return -1; 429 } 430 mark_tests_to_run( my_first_test_number, my_first_test_number ); 431 continue; 432 } 433 } 434 435 *indexp = i; 436 return 0; 437 438} /* parse_tests_to_run */ 439 440 441static void create_target_directory( const char * the_targetp ) 442{ 443 int err; 444 445 if ( strlen( the_targetp ) > (sizeof(g_target_path) - 1) ) { 446 printf( "target path too long - \"%s\" \n", the_targetp ); 447 exit( 1 ); 448 } 449 450 for ( ;; ) { 451 int my_rand; 452 char my_name[64]; 453 454 my_rand = rand( ); 455 sprintf( &my_name[0], "xnu_quick_test-%d", my_rand ); 456 if ( (strlen( &my_name[0] ) + strlen( the_targetp ) + 2) > PATH_MAX ) { 457 printf( "target path plus our test directory name is too long: \n" ); 458 printf( "target path - \"%s\" \n", the_targetp ); 459 printf( "test directory name - \"%s\" \n", &my_name[0] ); 460 exit( 1 ); 461 } 462 463 /* append generated directory name onto our path */ 464 g_target_path[0] = 0x00; 465 strcat( &g_target_path[0], the_targetp ); 466 if ( g_target_path[ (strlen(the_targetp) - 1) ] != '/' ) { 467 strcat( &g_target_path[0], "/" ); 468 } 469 strcat( &g_target_path[0], &my_name[0] ); 470 471 /* try to create the test directory */ 472 err = mkdir( &g_target_path[0], (S_IRWXU | S_IRWXG | S_IROTH) ); 473 if ( err == 0 ) { 474 break; 475 } 476 err = errno; 477 if ( EEXIST != err ) { 478 printf( "test directory creation failed - \"%s\" \n", &g_target_path[0] ); 479 printf( "mkdir call failed with error %d - \"%s\" \n", errno, strerror( err) ); 480 exit( 1 ); 481 } 482 } 483 printf( "created test directory at \"%s\" \n", &g_target_path[0] ); 484 485} /* create_target_directory */ 486 487 488static void list_all_tests( void ) 489{ 490 int i, my_tests_count; 491 492 my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] )); 493 printf( "\nList of all tests this tool performs... \n" ); 494 495 for ( i = 0; i < (my_tests_count - 1); i++ ) { 496 printf( " %d \t %s \n", (i + 1), g_tests[ i ].test_infop ); 497 } 498 499 return; 500} /* list_all_tests */ 501 502 503static void mark_tests_to_run( long my_start, long my_end ) 504{ 505 int my_tests_count, i; 506 507 my_tests_count = (sizeof( g_tests ) / sizeof( g_tests[0] )); 508 my_end = (my_end < (my_tests_count - 1)) ? my_end : (my_tests_count - 1); 509 for ( i = (my_start - 1); i < my_end; i++ ) { 510 g_tests[ i ].test_run_it = 1; /* run this test */ 511 } 512 return; 513} /* mark_tests_to_run */ 514 515 516static void usage( void ) 517{ 518 char * my_ptr; 519 520 /* skip past full path and just show the tool name */ 521 my_ptr = strrchr( g_cmd_namep, '/' ); 522 if ( my_ptr != NULL ) { 523 my_ptr++; 524 } 525 526 printf( "\nUSAGE: %s -target TARGET_PATH \n\n", (my_ptr != NULL) ? my_ptr : g_cmd_namep ); 527 printf( "\t -f[ailures] MAX_FAILS_ALLOWED # number of test cases that may fail before we give up. defaults to 0 \n" ); 528 printf( "\t -l[ist] # list all the tests this tool performs \n" ); 529 printf( "\t -r[un] 1, 3, 10 - 19 # run specific tests. enter individual test numbers and/or range of numbers. use -list to list tests. \n" ); 530 printf( "\t -s[kip] # skip setuid tests \n" ); 531 printf( "\t -t[arget] TARGET_PATH # path to directory where tool will create test files. defaults to \"/tmp/\" \n" ); 532#if !TARGET_OS_EMBEDDED 533 printf( "\t -x[ilog] # use XILog\n"); 534#endif 535 printf( "\nexamples: \n" ); 536 printf( "--- Place all test files and directories at the root of volume \"test_vol\" --- \n" ); 537 printf( "%s -t /Volumes/test_vol/ \n", (my_ptr != NULL) ? my_ptr : g_cmd_namep ); 538 printf( " \n" ); 539 printf( "--- Run the tool for tests 10 thru 15, test 18 and test 20 --- \n" ); 540 printf( "%s -r 10-15, 18, 20 \n", (my_ptr != NULL) ? my_ptr : g_cmd_namep ); 541 printf( " \n" ); 542 exit( 1 ); 543 544} /* usage */ 545 546