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