1 /*- 2 * Copyright (c) 2006 nCircle Network Security, Inc. 3 * Copyright (c) 2007 Robert N. M. Watson 4 * All rights reserved. 5 * 6 * This software was developed by Robert N. M. Watson for the TrustedBSD 7 * Project under contract to nCircle Network Security, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY, 22 * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * Privilege test framework. Each test is encapsulated on a .c file 33 * exporting a function that implements the test. Each test is run from its 34 * own child process, and they are run in sequence one at a time. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/jail.h> 39 #include <sys/stat.h> 40 #include <sys/wait.h> 41 42 #include <netinet/in.h> 43 44 #include <err.h> 45 #include <errno.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include "main.h" 52 53 /* 54 * If true, some test or preparatory step failed along the execution of this 55 * program. 56 * 57 * Intuitively, we would define a counter instead of a boolean. However, 58 * we fork to run the subtests and keeping proper track of the number of 59 * failed tests would be tricky and not provide any real value. 60 */ 61 static int something_failed = 0; 62 63 /* 64 * Registration table of privilege tests. Each test registers a name, a test 65 * function, and a cleanup function to run after the test has completed, 66 * regardless of success/failure. 67 */ 68 static struct test tests[] = { 69 { "priv_acct_enable", priv_acct_setup, priv_acct_enable, 70 priv_acct_cleanup }, 71 72 { "priv_acct_disable", priv_acct_setup, priv_acct_disable, 73 priv_acct_cleanup }, 74 75 { "priv_acct_rotate", priv_acct_setup, priv_acct_rotate, 76 priv_acct_cleanup }, 77 78 { "priv_acct_noopdisable", priv_acct_setup, priv_acct_noopdisable, 79 priv_acct_cleanup }, 80 81 { "priv_adjtime_set", priv_adjtime_setup, priv_adjtime_set, 82 priv_adjtime_cleanup }, 83 84 { "priv_audit_submit", priv_audit_submit_setup, priv_audit_submit, 85 priv_audit_submit_cleanup }, 86 87 { "priv_audit_control", priv_audit_control_setup, priv_audit_control, 88 priv_audit_control_cleanup }, 89 90 { "priv_audit_getaudit", priv_audit_getaudit_setup, 91 priv_audit_getaudit, priv_audit_getaudit_cleanup }, 92 93 { "priv_audit_getaudit_addr", priv_audit_getaudit_setup, 94 priv_audit_getaudit_addr, priv_audit_getaudit_cleanup }, 95 96 { "priv_audit_setaudit", priv_audit_setaudit_setup, 97 priv_audit_setaudit, priv_audit_setaudit_cleanup }, 98 99 { "priv_audit_setaudit_addr", priv_audit_setaudit_setup, 100 priv_audit_setaudit_addr, priv_audit_setaudit_cleanup }, 101 102 { "priv_clock_settime", priv_clock_settime_setup, priv_clock_settime, 103 priv_clock_settime_cleanup }, 104 105 { "priv_cred_setuid", priv_cred_setup, priv_cred_setuid, 106 priv_cred_cleanup }, 107 108 { "priv_cred_seteuid", priv_cred_setup, priv_cred_seteuid, 109 priv_cred_cleanup }, 110 111 { "priv_cred_setgid", priv_cred_setup, priv_cred_setgid, 112 priv_cred_cleanup }, 113 114 { "priv_cred_setegid", priv_cred_setup, priv_cred_setegid, 115 priv_cred_cleanup }, 116 117 { "priv_cred_setgroups", priv_cred_setup, priv_cred_setgroups, 118 priv_cred_cleanup }, 119 120 { "priv_cred_setreuid", priv_cred_setup, priv_cred_setreuid, 121 priv_cred_cleanup }, 122 123 { "priv_cred_setregid", priv_cred_setup, priv_cred_setregid, 124 priv_cred_cleanup }, 125 126 { "priv_cred_setresuid", priv_cred_setup, priv_cred_setresuid, 127 priv_cred_cleanup }, 128 129 { "priv_cred_setresgid", priv_cred_setup, priv_cred_setresgid, 130 priv_cred_cleanup }, 131 132 { "priv_io", priv_io_setup, priv_io, priv_io_cleanup }, 133 134 { "priv_kenv_set", priv_kenv_set_setup, priv_kenv_set, 135 priv_kenv_set_cleanup }, 136 137 { "priv_kenv_unset", priv_kenv_unset_setup, priv_kenv_unset, 138 priv_kenv_unset_cleanup }, 139 140 { "priv_msgbuf_privonly", priv_msgbuf_privonly_setup, 141 priv_msgbuf_privonly, priv_msgbuf_cleanup }, 142 143 { "priv_msgbuf_unprivok", priv_msgbuf_unprivok_setup, 144 priv_msgbuf_unprivok, priv_msgbuf_cleanup }, 145 146 { "priv_netinet_ipsec_pfkey", NULL, priv_netinet_ipsec_pfkey, NULL }, 147 148 { "priv_netinet_ipsec_policy4_bypass", 149 priv_netinet_ipsec_policy4_bypass_setup, 150 priv_netinet_ipsec_policy4_bypass, 151 priv_netinet_ipsec_policy_bypass_cleanup }, 152 153 #ifdef INET6 154 { "priv_netinet_ipsec_policy6_bypass", 155 priv_netinet_ipsec_policy6_bypass_setup, 156 priv_netinet_ipsec_policy6_bypass, 157 priv_netinet_ipsec_policy_bypass_cleanup }, 158 #endif 159 160 { "priv_netinet_ipsec_policy4_entrust", 161 priv_netinet_ipsec_policy4_entrust_setup, 162 priv_netinet_ipsec_policy4_entrust, 163 priv_netinet_ipsec_policy_entrust_cleanup }, 164 165 #ifdef INET6 166 { "priv_netinet_ipsec_policy6_entrust", 167 priv_netinet_ipsec_policy6_entrust_setup, 168 priv_netinet_ipsec_policy6_entrust, 169 priv_netinet_ipsec_policy_entrust_cleanup }, 170 #endif 171 172 { "priv_netinet_raw", priv_netinet_raw_setup, priv_netinet_raw, 173 priv_netinet_raw_cleanup }, 174 175 { "priv_proc_setlogin", priv_proc_setlogin_setup, priv_proc_setlogin, 176 priv_proc_setlogin_cleanup }, 177 178 { "priv_proc_setrlimit_raisemax", priv_proc_setrlimit_setup, 179 priv_proc_setrlimit_raisemax, priv_proc_setrlimit_cleanup }, 180 181 { "priv_proc_setrlimit_raisecur", priv_proc_setrlimit_setup, 182 priv_proc_setrlimit_raisecur, priv_proc_setrlimit_cleanup }, 183 184 { "priv_proc_setrlimit_raisecur_nopriv", priv_proc_setrlimit_setup, 185 priv_proc_setrlimit_raisecur_nopriv, 186 priv_proc_setrlimit_cleanup }, 187 188 { "priv_sched_rtprio_curproc_normal", priv_sched_rtprio_setup, 189 priv_sched_rtprio_curproc_normal, priv_sched_rtprio_cleanup }, 190 191 { "priv_sched_rtprio_curproc_idle", priv_sched_rtprio_setup, 192 priv_sched_rtprio_curproc_idle, priv_sched_rtprio_cleanup }, 193 194 { "priv_sched_rtprio_curproc_realtime", priv_sched_rtprio_setup, 195 priv_sched_rtprio_curproc_realtime, priv_sched_rtprio_cleanup }, 196 197 { "priv_sched_rtprio_myproc_normal", priv_sched_rtprio_setup, 198 priv_sched_rtprio_myproc_normal, priv_sched_rtprio_cleanup }, 199 200 { "priv_sched_rtprio_myproc_idle", priv_sched_rtprio_setup, 201 priv_sched_rtprio_myproc_idle, priv_sched_rtprio_cleanup }, 202 203 { "priv_sched_rtprio_myproc_realtime", priv_sched_rtprio_setup, 204 priv_sched_rtprio_myproc_realtime, priv_sched_rtprio_cleanup }, 205 206 { "priv_sched_rtprio_aproc_normal", priv_sched_rtprio_setup, 207 priv_sched_rtprio_aproc_normal, priv_sched_rtprio_cleanup }, 208 209 { "priv_sched_rtprio_aproc_idle", priv_sched_rtprio_setup, 210 priv_sched_rtprio_aproc_idle, priv_sched_rtprio_cleanup }, 211 212 { "priv_sched_rtprio_aproc_realtime", priv_sched_rtprio_setup, 213 priv_sched_rtprio_aproc_realtime, priv_sched_rtprio_cleanup }, 214 215 { "priv_sched_setpriority_curproc", priv_sched_setpriority_setup, 216 priv_sched_setpriority_curproc, priv_sched_setpriority_cleanup }, 217 218 { "priv_sched_setpriority_myproc", priv_sched_setpriority_setup, 219 priv_sched_setpriority_myproc, priv_sched_setpriority_cleanup }, 220 221 { "priv_sched_setpriority_aproc", priv_sched_setpriority_setup, 222 priv_sched_setpriority_aproc, priv_sched_setpriority_cleanup }, 223 224 { "priv_settimeofday", priv_settimeofday_setup, priv_settimeofday, 225 priv_settimeofday_cleanup }, 226 227 { "priv_sysctl_write", priv_sysctl_write_setup, priv_sysctl_write, 228 priv_sysctl_write_cleanup }, 229 230 { "priv_sysctl_writejail", priv_sysctl_write_setup, 231 priv_sysctl_writejail, priv_sysctl_write_cleanup }, 232 233 { "priv_vfs_chflags_froot_uflags", priv_vfs_chflags_froot_setup, 234 priv_vfs_chflags_froot_uflags, priv_vfs_chflags_cleanup }, 235 236 { "priv_vfs_chflags_froot_sflags", priv_vfs_chflags_froot_setup, 237 priv_vfs_chflags_froot_sflags, priv_vfs_chflags_cleanup }, 238 239 { "priv_vfs_chflags_fowner_uflags", priv_vfs_chflags_fowner_setup, 240 priv_vfs_chflags_fowner_uflags, priv_vfs_chflags_cleanup }, 241 242 { "priv_vfs_chflags_fowner_sflags", priv_vfs_chflags_fowner_setup, 243 priv_vfs_chflags_fowner_sflags, priv_vfs_chflags_cleanup }, 244 245 { "priv_vfs_chflags_fother_uflags", priv_vfs_chflags_fother_setup, 246 priv_vfs_chflags_fother_uflags, priv_vfs_chflags_cleanup }, 247 248 { "priv_vfs_chflags_fother_sflags", priv_vfs_chflags_fother_setup, 249 priv_vfs_chflags_fother_sflags, priv_vfs_chflags_cleanup }, 250 251 { "priv_vfs_chmod_froot", priv_vfs_chmod_froot_setup, 252 priv_vfs_chmod_froot, priv_vfs_chmod_cleanup }, 253 254 { "priv_vfs_chmod_fowner", priv_vfs_chmod_fowner_setup, 255 priv_vfs_chmod_fowner, priv_vfs_chmod_cleanup }, 256 257 { "priv_vfs_chmod_fother", priv_vfs_chmod_fother_setup, 258 priv_vfs_chmod_fother, priv_vfs_chmod_cleanup }, 259 260 { "priv_vfs_chown_uid", priv_vfs_chown_uid_setup, priv_vfs_chown_uid, 261 priv_vfs_chown_cleanup }, 262 263 { "priv_vfs_chown_mygid", priv_vfs_chown_mygid_setup, 264 priv_vfs_chown_mygid, priv_vfs_chown_cleanup }, 265 266 { "priv_vfs_chown_othergid", priv_vfs_chown_othergid_setup, 267 priv_vfs_chown_othergid, priv_vfs_chown_cleanup }, 268 269 { "priv_vfs_chroot", priv_vfs_chroot_setup, priv_vfs_chroot, 270 priv_vfs_chroot_cleanup }, 271 272 { "priv_vfs_clearsugid_chgrp", priv_vfs_clearsugid_setup, 273 priv_vfs_clearsugid_chgrp, priv_vfs_clearsugid_cleanup }, 274 275 { "priv_vfs_clearsugid_extattr", priv_vfs_clearsugid_setup, 276 priv_vfs_clearsugid_extattr, priv_vfs_clearsugid_cleanup }, 277 278 { "priv_vfs_clearsugid_write", priv_vfs_clearsugid_setup, 279 priv_vfs_clearsugid_write, priv_vfs_clearsugid_cleanup }, 280 281 { "priv_vfs_extattr_system", priv_vfs_extattr_system_setup, 282 priv_vfs_extattr_system, priv_vfs_extattr_system_cleanup }, 283 284 { "priv_vfs_fhopen", priv_vfs_fhopen_setup, priv_vfs_fhopen, 285 priv_vfs_fhopen_cleanup }, 286 287 { "priv_vfs_fhstat", priv_vfs_fhstat_setup, priv_vfs_fhstat, 288 priv_vfs_fhstat_cleanup }, 289 290 { "priv_vfs_fhstatfs", priv_vfs_fhstatfs_setup, priv_vfs_fhstatfs, 291 priv_vfs_fhstatfs_cleanup }, 292 293 { "priv_vfs_generation", priv_vfs_generation_setup, 294 priv_vfs_generation, priv_vfs_generation_cleanup }, 295 296 { "priv_vfs_getfh", priv_vfs_getfh_setup, priv_vfs_getfh, 297 priv_vfs_getfh_cleanup }, 298 299 { "priv_vfs_readwrite_fowner", priv_vfs_readwrite_fowner_setup, 300 priv_vfs_readwrite_fowner, priv_vfs_readwrite_cleanup }, 301 302 { "priv_vfs_readwrite_fgroup", priv_vfs_readwrite_fgroup_setup, 303 priv_vfs_readwrite_fgroup, priv_vfs_readwrite_cleanup }, 304 305 { "priv_vfs_readwrite_fother", priv_vfs_readwrite_fother_setup, 306 priv_vfs_readwrite_fother, priv_vfs_readwrite_cleanup }, 307 308 { "priv_vfs_setgid_fowner", priv_vfs_setgid_fowner_setup, 309 priv_vfs_setgid_fowner, priv_vfs_setgid_cleanup }, 310 311 { "priv_vfs_setgid_fother", priv_vfs_setgid_fother_setup, 312 priv_vfs_setgid_fother, priv_vfs_setgid_cleanup }, 313 314 { "priv_vfs_stickyfile_dir_fowner", 315 priv_vfs_stickyfile_dir_fowner_setup, 316 priv_vfs_stickyfile_dir_fowner, 317 priv_vfs_stickyfile_dir_cleanup }, 318 319 { "priv_vfs_stickyfile_dir_fother", 320 priv_vfs_stickyfile_dir_fother_setup, 321 priv_vfs_stickyfile_dir_fother, 322 priv_vfs_stickyfile_dir_cleanup }, 323 324 { "priv_vfs_stickyfile_file_fowner", 325 priv_vfs_stickyfile_file_fowner_setup, 326 priv_vfs_stickyfile_file_fowner, 327 priv_vfs_stickyfile_file_cleanup }, 328 329 { "priv_vfs_stickyfile_file_fother", 330 priv_vfs_stickyfile_file_fother_setup, 331 priv_vfs_stickyfile_file_fother, 332 priv_vfs_stickyfile_file_cleanup }, 333 334 { "priv_vfs_utimes_froot", priv_vfs_utimes_froot_setup, 335 priv_vfs_utimes_froot, priv_vfs_utimes_cleanup }, 336 337 { "priv_vfs_utimes_froot_null", priv_vfs_utimes_froot_setup, 338 priv_vfs_utimes_froot_null, priv_vfs_utimes_cleanup }, 339 340 { "priv_vfs_utimes_fowner", priv_vfs_utimes_fowner_setup, 341 priv_vfs_utimes_fowner, priv_vfs_utimes_cleanup }, 342 343 { "priv_vfs_utimes_fowner_null", priv_vfs_utimes_fowner_setup, 344 priv_vfs_utimes_fowner_null, priv_vfs_utimes_cleanup }, 345 346 { "priv_vfs_utimes_fother", priv_vfs_utimes_fother_setup, 347 priv_vfs_utimes_fother, priv_vfs_utimes_cleanup }, 348 349 { "priv_vfs_utimes_fother_null", priv_vfs_utimes_fother_setup, 350 priv_vfs_utimes_fother_null, priv_vfs_utimes_cleanup }, 351 352 { "priv_vm_madv_protect", priv_vm_madv_protect_setup, 353 priv_vm_madv_protect, priv_vm_madv_protect_cleanup }, 354 355 { "priv_vm_mlock", priv_vm_mlock_setup, priv_vm_mlock, 356 priv_vm_mlock_cleanup }, 357 358 { "priv_vm_munlock", priv_vm_munlock_setup, priv_vm_munlock, 359 priv_vm_munlock_cleanup }, 360 361 }; 362 static int tests_count = sizeof(tests) / sizeof(struct test); 363 364 void 365 expect(const char *test, int error, int expected_error, int expected_errno) 366 { 367 368 if (error == 0) { 369 if (expected_error != 0) { 370 something_failed = 1; 371 warnx("%s: returned 0", test); 372 } 373 } else { 374 if (expected_error == 0) { 375 something_failed = 1; 376 warn("%s: returned (%d, %d)", test, error, errno); 377 } else if (expected_errno != errno) { 378 something_failed = 1; 379 warn("%s: returned (%d, %d)", test, error, errno); 380 } 381 } 382 } 383 384 void 385 setup_dir(const char *test, char *dpathp, uid_t uid, gid_t gid, mode_t mode) 386 { 387 388 strcpy(dpathp, "/tmp/priv.XXXXXXXXXXX"); 389 if (mkdtemp(dpathp) == NULL) 390 err(-1, "test %s: mkdtemp", test); 391 392 if (chown(dpathp, uid, gid) < 0) 393 err(-1, "test %s: chown(%s, %d, %d)", test, dpathp, uid, 394 gid); 395 396 if (chmod(dpathp, mode) < 0) 397 err(-1, "test %s: chmod(%s, 0%o)", test, dpathp, mode); 398 } 399 400 void 401 setup_file(const char *test, char *fpathp, uid_t uid, gid_t gid, mode_t mode) 402 { 403 int fd; 404 405 strcpy(fpathp, "/tmp/priv.XXXXXXXXXXX"); 406 fd = mkstemp(fpathp); 407 if (fd < 0) 408 err(-1, "test %s: mkstemp", test); 409 410 if (fchown(fd, uid, gid) < 0) 411 err(-1, "test %s: fchown(%s, %d, %d)", test, fpathp, uid, 412 gid); 413 414 if (fchmod(fd, mode) < 0) 415 err(-1, "test %s: chmod(%s, 0%o)", test, fpathp, mode); 416 417 close(fd); 418 } 419 420 /* 421 * Irrevocably set credentials to specific uid and gid. 422 */ 423 static void 424 set_creds(const char *test, uid_t uid, gid_t gid) 425 { 426 gid_t gids[1] = { gid }; 427 428 if (setgid(gid) < 0) 429 err(-1, "test %s: setegid(%d)", test, gid); 430 if (setgroups(sizeof(gids)/sizeof(gid_t), gids) < 0) 431 err(-1, "test %s: setgroups(%d)", test, gid); 432 if (setuid(uid) < 0) 433 err(-1, "test %s: seteuid(%d)", test, uid); 434 } 435 436 static void 437 enter_jail(const char *test) 438 { 439 struct jail j; 440 struct in_addr ia4; 441 #ifdef INET6 442 struct in6_addr ia6 = IN6ADDR_LOOPBACK_INIT; 443 #endif 444 445 bzero(&j, sizeof(j)); 446 j.version = JAIL_API_VERSION; 447 j.path = "/"; 448 j.hostname = "test"; 449 j.jailname = "regressions/priv"; 450 ia4.s_addr = htonl(INADDR_LOOPBACK); 451 j.ip4s = 1; 452 j.ip4 = &ia4; 453 #ifdef INET6 454 j.ip6s = 1; 455 j.ip6 = &ia6; 456 #endif 457 if (jail(&j) < 0) 458 err(-1, "test %s: jail", test); 459 } 460 461 static void 462 run_child(struct test *test, int asroot, int injail) 463 { 464 465 setprogname(test->t_name); 466 if (injail) 467 enter_jail(test->t_name); 468 if (!asroot) 469 set_creds(test->t_name, UID_OWNER, GID_OWNER); 470 test->t_test_func(asroot, injail, test); 471 } 472 473 /* 474 * Run a test in a particular credential context -- always call the setup and 475 * cleanup routines; if setup succeeds, also run the test. Test cleanup must 476 * handle cases where the setup has failed, so may need to maintain their own 477 * state in order to know what needs cleaning up (such as whether temporary 478 * files were created). 479 */ 480 static void 481 run(struct test *test, int asroot, int injail) 482 { 483 pid_t childpid, pid; 484 485 if (test->t_setup_func != NULL) { 486 if ((test->t_setup_func)(asroot, injail, test) != 0) { 487 warnx("run(%s, %d, %d) setup failed", test->t_name, 488 asroot, injail); 489 goto cleanup; 490 } 491 } 492 fflush(stdout); 493 fflush(stderr); 494 childpid = fork(); 495 if (childpid == -1) { 496 warn("run(%s, %d, %d) fork failed", test->t_name, asroot, 497 injail); 498 goto cleanup; 499 } 500 if (childpid == 0) { 501 run_child(test, asroot, injail); 502 fflush(stdout); 503 fflush(stderr); 504 exit(something_failed ? EXIT_FAILURE : EXIT_SUCCESS); 505 } else { 506 while (1) { 507 int status; 508 pid = waitpid(childpid, &status, 0); 509 if (pid == -1) { 510 something_failed = 1; 511 warn("test: waitpid %s", test->t_name); 512 } 513 if (pid == childpid) { 514 if (WIFEXITED(status) && 515 WEXITSTATUS(status) == EXIT_SUCCESS) { 516 /* All good in the subprocess! */ 517 } else { 518 something_failed = 1; 519 } 520 break; 521 } 522 } 523 } 524 fflush(stdout); 525 fflush(stderr); 526 cleanup: 527 if (test->t_cleanup_func != NULL) 528 test->t_cleanup_func(asroot, injail, test); 529 } 530 531 int 532 main(int argc, char *argv[]) 533 { 534 int i; 535 536 /* 537 * This test suite will need to become quite a bit more enlightened 538 * if the notion of privilege is truly separated from root, as tests 539 * make assumptions about when privilege will be present. In 540 * particular, VFS-related tests need to manage uids in order to 541 * force the use of privilege, and will likely need checking. 542 */ 543 if (getuid() != 0 && geteuid() != 0) 544 errx(-1, "must be run as root"); 545 546 /* 547 * Run each test four times, varying whether the process is running 548 * as root and in jail in order to test all possible combinations. 549 */ 550 for (i = 0; i < tests_count; i++) { 551 run(&tests[i], 0, 0); 552 run(&tests[i], 0, 1); 553 run(&tests[i], 1, 0); 554 run(&tests[i], 1, 1); 555 } 556 return (something_failed ? EXIT_FAILURE : EXIT_SUCCESS); 557 } 558