1#!/usr/libexec/flua 2-- 3-- SPDX-License-Identifier: BSD-2-Clause 4-- 5-- Copyright (c) 2024, Klara, Inc. 6-- 7-- Redistribution and use in source and binary forms, with or without 8-- modification, are permitted provided that the following conditions 9-- are met: 10-- 1. Redistributions of source code must retain the above copyright 11-- notice, this list of conditions and the following disclaimer. 12-- 2. Redistributions in binary form must reproduce the above copyright 13-- notice, this list of conditions and the following disclaimer in the 14-- documentation and/or other materials provided with the distribution. 15-- 16-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26-- SUCH DAMAGE. 27-- 28 29-- THEORY OF OPERATION 30-- 31-- generate-fortify-tests.lua is intended to test fortified functions as found 32-- mostly in the various headers in /usr/include/ssp. Each fortified function 33-- gets three basic tests: 34-- 35-- 1. Write just before the end of the buffer, 36-- 2. Write right at the end of the buffer, 37-- 3. Write just after the end of the buffer. 38-- 39-- Each test is actually generated twice: once with a buffer on the stack, and 40-- again with a buffer on the heap, to confirm that __builtin_object_size(3) can 41-- deduce the buffer size in both scenarios. The tests work by setting up the 42-- stack with our buffer (and some padding on either side to avoid tripping any 43-- other stack or memory protection), doing any initialization as described by 44-- the test definition, then calling the fortified function with the buffer as 45-- outlined by the test definition. 46-- 47-- For the 'before' and 'at' the end tests, we're ensuring that valid writes 48-- that are on the verge of being invalid aren't accidentally being detected as 49-- invalid. 50-- 51-- The 'after' test is the one that actually tests the functional benefit of 52-- _FORTIFY_SOURCE by violating a boundary that should trigger an abort. As 53-- such, this test differs more from the other two in that it has to fork() off 54-- the fortified function call so that we can monitor for a SIGABRT and 55-- pass/fail the test at function end appropriately. 56 57-- Some tests, like the FD_*() macros, may define these differently. For 58-- instance, for fd sets we're varying the index we pass and not using arbitrary 59-- buffers. Other tests that don't use the length in any way may physically 60-- vary the buffer size for each test case when we'd typically vary the length 61-- we're requesting a write for. 62 63local includes = { 64 "sys/param.h", 65 "sys/jail.h", 66 "sys/random.h", 67 "sys/resource.h", 68 "sys/select.h", 69 "sys/socket.h", 70 "sys/time.h", 71 "sys/uio.h", 72 "sys/wait.h", 73 "dirent.h", 74 "errno.h", 75 "fcntl.h", 76 "limits.h", 77 "poll.h", 78 "signal.h", 79 "stdio.h", 80 "stdlib.h", 81 "string.h", 82 "strings.h", 83 "sysexits.h", 84 "unistd.h", 85 "wchar.h", 86 "atf-c.h", 87} 88 89local tests_added = {} 90 91-- Configuration for tests that want the host/domainname 92local hostname = "host.example.com" 93local domainname = "example.com" 94 95-- Some of these will need to be excluded because clang sees the wrong size when 96-- an array is embedded inside a struct, we'll get something that looks more 97-- like __builtin_object_size(ptr, 0) than it does the correct 98-- __builtin_object_size(ptr, 1) (i.e., includes the padding after). This is 99-- almost certainly a bug in llvm. 100local function excludes_stack_overflow(disposition, is_heap) 101 return (not is_heap) and disposition > 0 102end 103 104local poll_init = [[ 105 for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { 106 __stack.__buf[i].fd = -1; 107 } 108]] 109 110local printf_stackvars = "\tchar srcvar[__len + 10];\n" 111local printf_init = [[ 112 memset(srcvar, 'A', sizeof(srcvar) - 1); 113 srcvar[sizeof(srcvar) - 1] = '\0'; 114]] 115 116local readv_stackvars = "\tstruct iovec iov[1];\n" 117local readv_init = [[ 118 iov[0].iov_base = __stack.__buf; 119 iov[0].iov_len = __len; 120 121 replace_stdin(); 122]] 123 124local socket_stackvars = "\tint sock[2] = { -1, -1 };\n" 125local recvfrom_sockaddr_stackvars = socket_stackvars .. [[ 126 char data[16]; 127 socklen_t socklen; 128]] 129local recvmsg_stackvars = socket_stackvars .. "\tstruct msghdr msg;\n" 130local socket_init = [[ 131 new_socket(sock); 132]] 133local socket_socklen_init = socket_init .. [[ 134 socklen = __len; 135]] 136 137local stdio_init = [[ 138 replace_stdin(); 139]] 140 141local string_stackvars = "\tchar src[__len];\n" 142local string_init = [[ 143 memset(__stack.__buf, 0, __len); 144 memset(src, 'A', __len - 1); 145 src[__len - 1] = '\0'; 146]] 147 148local wstring_stackvars = "\twchar_t src[__len];\n" 149local wstring_init = [[ 150 wmemset(__stack.__buf, 0, __len); 151 wmemset(src, 'A', __len - 1); 152 src[__len - 1] = '\0'; 153]] 154 155-- Each test entry describes how to test a given function. We need to know how 156-- to construct the buffer, we need to know the argument set we're dealing with, 157-- and we need to know what we're passing to each argument. We could be passing 158-- fixed values, or we could be passing the __buf under test. 159-- 160-- definition: 161-- func: name of the function under test to call 162-- bufsize: size of buffer to generate, defaults to 42 163-- buftype: type of buffer to generate, defaults to unsigned char[] 164-- arguments: __buf, __len, or the name of a variable placed on the stack 165-- exclude: a function(disposition, is_heap) that returns true if this combo 166-- should be excluded. 167-- stackvars: extra variables to be placed on the stack, should be a string 168-- optionally formatted with tabs and newlines 169-- init: extra code to inject just before the function call for initialization 170-- of the buffer or any of the above-added stackvars; also a string 171-- uses_len: bool-ish, necessary if arguments doesn't include either __idx or 172-- or __len so that the test generator doesn't try to vary the size of the 173-- buffer instead of just manipulating __idx/__len to try and induce an 174-- overflow. 175-- 176-- Most tests will just use the default bufsize/buftype, but under some 177-- circumstances it's useful to use a different type (e.g., for alignment 178-- requirements). 179local all_tests = { 180 random = { 181 -- <sys/random.h> 182 { 183 func = "getrandom", 184 arguments = { 185 "__buf", 186 "__len", 187 "0", 188 }, 189 exclude = excludes_stack_overflow, 190 }, 191 }, 192 select = { 193 -- <sys/select.h> 194 { 195 func = "FD_SET", 196 bufsize = "FD_SETSIZE", 197 buftype = "fd_set", 198 arguments = { 199 "__idx", 200 "__buf", 201 }, 202 }, 203 { 204 func = "FD_CLR", 205 bufsize = "FD_SETSIZE", 206 buftype = "fd_set", 207 arguments = { 208 "__idx", 209 "__buf", 210 }, 211 }, 212 { 213 func = "FD_ISSET", 214 bufsize = "FD_SETSIZE", 215 buftype = "fd_set", 216 arguments = { 217 "__idx", 218 "__buf", 219 }, 220 }, 221 }, 222 socket = { 223 -- <sys/socket.h> 224 { 225 func = "getpeername", 226 buftype = "struct sockaddr", 227 bufsize = "sizeof(struct sockaddr)", 228 arguments = { 229 "sock[0]", 230 "__buf", 231 "&socklen", 232 }, 233 exclude = excludes_stack_overflow, 234 stackvars = socket_stackvars .. "\tsocklen_t socklen;", 235 init = socket_socklen_init, 236 uses_len = true, 237 }, 238 { 239 func = "getsockname", 240 buftype = "struct sockaddr", 241 bufsize = "sizeof(struct sockaddr)", 242 arguments = { 243 "sock[0]", 244 "__buf", 245 "&socklen", 246 }, 247 exclude = excludes_stack_overflow, 248 stackvars = socket_stackvars .. "\tsocklen_t socklen;", 249 init = socket_socklen_init, 250 uses_len = true, 251 }, 252 { 253 func = "recv", 254 arguments = { 255 "sock[0]", 256 "__buf", 257 "__len", 258 "0", 259 }, 260 exclude = excludes_stack_overflow, 261 stackvars = socket_stackvars, 262 init = socket_init, 263 }, 264 { 265 func = "recvfrom", 266 arguments = { 267 "sock[0]", 268 "__buf", 269 "__len", 270 "0", 271 "NULL", 272 "NULL", 273 }, 274 exclude = excludes_stack_overflow, 275 stackvars = socket_stackvars, 276 init = socket_init, 277 }, 278 { 279 func = "recvfrom", 280 variant = "sockaddr", 281 buftype = "struct sockaddr", 282 bufsize = "sizeof(struct sockaddr)", 283 arguments = { 284 "sock[0]", 285 "data", 286 "sizeof(data)", 287 "0", 288 "__buf", 289 "&socklen", 290 }, 291 exclude = excludes_stack_overflow, 292 stackvars = recvfrom_sockaddr_stackvars, 293 init = socket_socklen_init, 294 uses_len = true, 295 }, 296 { 297 func = "recvmsg", 298 variant = "msg_name", 299 buftype = "struct sockaddr", 300 bufsize = "sizeof(struct sockaddr)", 301 arguments = { 302 "sock[0]", 303 "&msg", 304 "0", 305 }, 306 exclude = excludes_stack_overflow, 307 stackvars = recvmsg_stackvars, 308 init = [[ 309 memset(&msg, 0, sizeof(msg)); 310 msg.msg_name = BUF; 311 msg.msg_namelen = __len; 312]], 313 uses_len = true, 314 }, 315 { 316 func = "recvmsg", 317 variant = "msg_iov", 318 arguments = { 319 "sock[0]", 320 "&msg", 321 "0", 322 }, 323 exclude = excludes_stack_overflow, 324 stackvars = recvmsg_stackvars .. "\tstruct iovec iov[2];\n", 325 init = [[ 326 memset(&msg, 0, sizeof(msg)); 327 memset(&iov[0], 0, sizeof(iov)); 328 329 /* 330 * We position the buffer second just so that we can confirm that the 331 * fortification bits are traversing the iovec correctly. 332 */ 333 iov[1].iov_base = BUF; 334 iov[1].iov_len = __len; 335 336 msg.msg_iov = &iov[0]; 337 msg.msg_iovlen = nitems(iov); 338]], 339 uses_len = true, 340 }, 341 { 342 func = "recvmsg", 343 variant = "msg_control", 344 bufsize = "CMSG_SPACE(sizeof(int))", 345 arguments = { 346 "sock[0]", 347 "&msg", 348 "0", 349 }, 350 exclude = excludes_stack_overflow, 351 stackvars = recvmsg_stackvars, 352 init = [[ 353 memset(&msg, 0, sizeof(msg)); 354 355 msg.msg_control = BUF; 356 msg.msg_controllen = __len; 357]], 358 uses_len = true, 359 }, 360 { 361 func = "recvmmsg", 362 variant = "msgvec", 363 buftype = "struct mmsghdr[]", 364 bufsize = "2", 365 arguments = { 366 "sock[0]", 367 "__buf", 368 "__len", 369 "0", 370 "NULL", 371 }, 372 stackvars = socket_stackvars, 373 }, 374 { 375 -- We'll assume that recvmsg is covering msghdr 376 -- validation thoroughly enough, we'll just try tossing 377 -- an error in the second element of a msgvec to try and 378 -- make sure that each one is being validated. 379 func = "recvmmsg", 380 variant = "msghdr", 381 arguments = { 382 "sock[0]", 383 "&msgvec[0]", 384 "nitems(msgvec)", 385 "0", 386 "NULL", 387 }, 388 exclude = excludes_stack_overflow, 389 stackvars = socket_stackvars .. "\tstruct mmsghdr msgvec[2];\n", 390 init = [[ 391 memset(&msgvec[0], 0, sizeof(msgvec)); 392 393 /* 394 * Same as above, make sure fortification isn't ignoring n > 1 elements 395 * of the msgvec. 396 */ 397 msgvec[1].msg_hdr.msg_control = BUF; 398 msgvec[1].msg_hdr.msg_controllen = __len; 399]], 400 uses_len = true, 401 }, 402 }, 403 uio = { 404 -- <sys/uio.h> 405 { 406 func = "readv", 407 buftype = "struct iovec[]", 408 bufsize = 2, 409 arguments = { 410 "STDIN_FILENO", 411 "__buf", 412 "__len", 413 }, 414 init = stdio_init, 415 }, 416 { 417 func = "readv", 418 variant = "iov", 419 arguments = { 420 "STDIN_FILENO", 421 "iov", 422 "nitems(iov)", 423 }, 424 exclude = excludes_stack_overflow, 425 stackvars = readv_stackvars, 426 init = readv_init, 427 uses_len = true, 428 }, 429 { 430 func = "preadv", 431 buftype = "struct iovec[]", 432 bufsize = 2, 433 arguments = { 434 "STDIN_FILENO", 435 "__buf", 436 "__len", 437 "0", 438 }, 439 init = stdio_init, 440 }, 441 { 442 func = "preadv", 443 variant = "iov", 444 arguments = { 445 "STDIN_FILENO", 446 "iov", 447 "nitems(iov)", 448 "0", 449 }, 450 exclude = excludes_stack_overflow, 451 stackvars = readv_stackvars, 452 init = readv_init, 453 uses_len = true, 454 }, 455 }, 456 poll = { 457 -- <poll.h> 458 { 459 func = "poll", 460 bufsize = "4", 461 buftype = "struct pollfd[]", 462 arguments = { 463 "__buf", 464 "__len", 465 "0", 466 }, 467 init = poll_init, 468 }, 469 { 470 func = "ppoll", 471 bufsize = "4", 472 buftype = "struct pollfd[]", 473 arguments = { 474 "__buf", 475 "__len", 476 "&tv", 477 "NULL", 478 }, 479 stackvars = "\tstruct timespec tv = { 0 };\n", 480 init = poll_init, 481 }, 482 }, 483 signal = { 484 -- <signal.h> 485 { 486 func = "sig2str", 487 bufsize = "SIG2STR_MAX", 488 arguments = { 489 "1", 490 "__buf", 491 }, 492 exclude = excludes_stack_overflow, 493 }, 494 }, 495 stdio = { 496 -- <stdio.h> 497 { 498 func = "ctermid", 499 bufsize = "L_ctermid", 500 arguments = { 501 "__buf", 502 }, 503 exclude = excludes_stack_overflow, 504 }, 505 { 506 func = "ctermid_r", 507 bufsize = "L_ctermid", 508 arguments = { 509 "__buf", 510 }, 511 exclude = excludes_stack_overflow, 512 }, 513 { 514 func = "fread", 515 arguments = { 516 "__buf", 517 "__len", 518 "1", 519 "stdin", 520 }, 521 exclude = excludes_stack_overflow, 522 init = stdio_init, 523 }, 524 { 525 func = "fread_unlocked", 526 arguments = { 527 "__buf", 528 "__len", 529 "1", 530 "stdin", 531 }, 532 exclude = excludes_stack_overflow, 533 init = stdio_init, 534 }, 535 { 536 func = "gets_s", 537 arguments = { 538 "__buf", 539 "__len", 540 }, 541 exclude = excludes_stack_overflow, 542 init = stdio_init, 543 }, 544 { 545 func = "sprintf", 546 arguments = { 547 "__buf", 548 "\"%.*s\"", 549 "(int)__len - 1", -- - 1 for NUL terminator 550 "srcvar", 551 }, 552 exclude = excludes_stack_overflow, 553 stackvars = printf_stackvars, 554 init = printf_init, 555 }, 556 { 557 func = "snprintf", 558 arguments = { 559 "__buf", 560 "__len", 561 "\"%.*s\"", 562 "(int)__len - 1", -- - 1 for NUL terminator 563 "srcvar", 564 }, 565 exclude = excludes_stack_overflow, 566 stackvars = printf_stackvars, 567 init = printf_init, 568 }, 569 { 570 func = "tmpnam", 571 bufsize = "L_tmpnam", 572 arguments = { 573 "__buf", 574 }, 575 exclude = excludes_stack_overflow, 576 }, 577 { 578 func = "fgets", 579 arguments = { 580 "__buf", 581 "__len", 582 "fp", 583 }, 584 exclude = excludes_stack_overflow, 585 stackvars = "\tFILE *fp;\n", 586 init = [[ 587 fp = new_fp(__len); 588]], 589 }, 590 }, 591 stdlib = { 592 -- <stdlib.h> 593 { 594 func = "arc4random_buf", 595 arguments = { 596 "__buf", 597 "__len", 598 }, 599 exclude = excludes_stack_overflow, 600 }, 601 { 602 func = "getenv_r", 603 arguments = { 604 "\"PATH\"", 605 "__buf", 606 "__len", 607 }, 608 exclude = excludes_stack_overflow, 609 }, 610 { 611 func = "realpath", 612 bufsize = "PATH_MAX", 613 arguments = { 614 "\".\"", 615 "__buf", 616 }, 617 exclude = excludes_stack_overflow, 618 }, 619 }, 620 string = { 621 -- <string.h> 622 { 623 func = "memcpy", 624 arguments = { 625 "__buf", 626 "src", 627 "__len", 628 }, 629 exclude = excludes_stack_overflow, 630 stackvars = "\tchar src[__len + 10];\n", 631 }, 632 { 633 func = "mempcpy", 634 arguments = { 635 "__buf", 636 "src", 637 "__len", 638 }, 639 exclude = excludes_stack_overflow, 640 stackvars = "\tchar src[__len + 10];\n", 641 }, 642 { 643 func = "memmove", 644 arguments = { 645 "__buf", 646 "src", 647 "__len", 648 }, 649 exclude = excludes_stack_overflow, 650 stackvars = "\tchar src[__len + 10];\n", 651 }, 652 { 653 func = "memset", 654 arguments = { 655 "__buf", 656 "0", 657 "__len", 658 }, 659 exclude = excludes_stack_overflow, 660 }, 661 { 662 func = "memset_explicit", 663 arguments = { 664 "__buf", 665 "0", 666 "__len", 667 }, 668 exclude = excludes_stack_overflow, 669 }, 670 { 671 func = "stpcpy", 672 arguments = { 673 "__buf", 674 "src", 675 }, 676 exclude = excludes_stack_overflow, 677 stackvars = string_stackvars, 678 init = string_init, 679 uses_len = true, 680 }, 681 { 682 func = "stpncpy", 683 arguments = { 684 "__buf", 685 "src", 686 "__len", 687 }, 688 exclude = excludes_stack_overflow, 689 stackvars = string_stackvars, 690 init = string_init, 691 }, 692 { 693 func = "strcat", 694 arguments = { 695 "__buf", 696 "src", 697 }, 698 exclude = excludes_stack_overflow, 699 stackvars = string_stackvars, 700 init = string_init, 701 uses_len = true, 702 }, 703 { 704 func = "strlcat", 705 arguments = { 706 "__buf", 707 "src", 708 "__len", 709 }, 710 exclude = excludes_stack_overflow, 711 stackvars = string_stackvars, 712 init = string_init, 713 }, 714 { 715 func = "strncat", 716 arguments = { 717 "__buf", 718 "src", 719 "__len", 720 }, 721 exclude = excludes_stack_overflow, 722 stackvars = string_stackvars, 723 init = string_init, 724 }, 725 { 726 func = "strcpy", 727 arguments = { 728 "__buf", 729 "src", 730 }, 731 exclude = excludes_stack_overflow, 732 stackvars = string_stackvars, 733 init = string_init, 734 uses_len = true, 735 }, 736 { 737 func = "strlcpy", 738 arguments = { 739 "__buf", 740 "src", 741 "__len", 742 }, 743 exclude = excludes_stack_overflow, 744 stackvars = string_stackvars, 745 init = string_init, 746 }, 747 { 748 func = "strncpy", 749 arguments = { 750 "__buf", 751 "src", 752 "__len", 753 }, 754 exclude = excludes_stack_overflow, 755 stackvars = string_stackvars, 756 init = string_init, 757 }, 758 }, 759 strings = { 760 -- <strings.h> 761 { 762 func = "bcopy", 763 arguments = { 764 "src", 765 "__buf", 766 "__len", 767 }, 768 exclude = excludes_stack_overflow, 769 stackvars = "\tchar src[__len + 10];\n", 770 }, 771 { 772 func = "bzero", 773 arguments = { 774 "__buf", 775 "__len", 776 }, 777 exclude = excludes_stack_overflow, 778 }, 779 { 780 func = "explicit_bzero", 781 arguments = { 782 "__buf", 783 "__len", 784 }, 785 exclude = excludes_stack_overflow, 786 }, 787 }, 788 unistd = { 789 -- <unistd.h> 790 { 791 func = "getcwd", 792 bufsize = "8", 793 arguments = { 794 "__buf", 795 "__len", 796 }, 797 exclude = excludes_stack_overflow, 798 }, 799 { 800 func = "getgrouplist", 801 bufsize = "4", 802 buftype = "gid_t[]", 803 arguments = { 804 "\"root\"", 805 "0", 806 "__buf", 807 "&intlen", 808 }, 809 exclude = excludes_stack_overflow, 810 stackvars = "\tint intlen = (int)__len;\n", 811 uses_len = true, 812 }, 813 { 814 func = "getgroups", 815 bufsize = "4", 816 buftype = "gid_t[]", 817 arguments = { 818 "__len", 819 "__buf", 820 }, 821 exclude = excludes_stack_overflow, 822 }, 823 { 824 func = "getloginclass", 825 arguments = { 826 "__buf", 827 "__len", 828 }, 829 exclude = excludes_stack_overflow, 830 }, 831 { 832 func = "pread", 833 bufsize = "41", 834 arguments = { 835 "fd", 836 "__buf", 837 "__len", 838 "0", 839 }, 840 exclude = excludes_stack_overflow, 841 stackvars = "\tint fd;\n", 842 init = [[ 843 fd = new_tmpfile(); /* Cannot fail */ 844]], 845 }, 846 { 847 func = "read", 848 bufsize = "41", 849 arguments = { 850 "fd", 851 "__buf", 852 "__len", 853 }, 854 exclude = excludes_stack_overflow, 855 stackvars = "\tint fd;\n", 856 init = [[ 857 fd = new_tmpfile(); /* Cannot fail */ 858]], 859 }, 860 { 861 func = "readlink", 862 arguments = { 863 "path", 864 "__buf", 865 "__len", 866 }, 867 exclude = excludes_stack_overflow, 868 stackvars = "\tconst char *path;\n", 869 init = [[ 870 path = new_symlink(__len); /* Cannot fail */ 871]], 872 }, 873 { 874 func = "readlinkat", 875 arguments = { 876 "AT_FDCWD", 877 "path", 878 "__buf", 879 "__len", 880 }, 881 exclude = excludes_stack_overflow, 882 stackvars = "\tconst char *path;\n", 883 init = [[ 884 path = new_symlink(__len); /* Cannot fail */ 885]], 886 }, 887 { 888 func = "getdomainname", 889 bufsize = #domainname + 1, 890 arguments = { 891 "__buf", 892 "__len", 893 }, 894 need_root = true, 895 exclude = excludes_stack_overflow, 896 early_init = " dhost_jail();", 897 }, 898 { 899 func = "getentropy", 900 arguments = { 901 "__buf", 902 "__len", 903 }, 904 exclude = excludes_stack_overflow, 905 }, 906 { 907 func = "gethostname", 908 bufsize = #hostname + 1, 909 arguments = { 910 "__buf", 911 "__len", 912 }, 913 need_root = true, 914 exclude = excludes_stack_overflow, 915 early_init = " dhost_jail();", 916 }, 917 { 918 func = "getlogin_r", 919 bufsize = "MAXLOGNAME + 1", 920 arguments = { 921 "__buf", 922 "__len", 923 }, 924 exclude = excludes_stack_overflow, 925 }, 926 { 927 func = "ttyname_r", 928 arguments = { 929 "fd", 930 "__buf", 931 "__len", 932 }, 933 exclude = excludes_stack_overflow, 934 stackvars = "\tint fd;\n", 935 early_init = [[ 936 fd = STDIN_FILENO; 937 if (!isatty(fd)) 938 atf_tc_skip("stdin is not an fd"); 939]] 940 }, 941 }, 942 wchar = { 943 -- <wchar.h> 944 { 945 func = "wmemcpy", 946 buftype = "wchar_t[]", 947 arguments = { 948 "__buf", 949 "src", 950 "__len", 951 }, 952 exclude = excludes_stack_overflow, 953 stackvars = "\twchar_t src[__len + 10];\n", 954 }, 955 { 956 func = "wmempcpy", 957 buftype = "wchar_t[]", 958 arguments = { 959 "__buf", 960 "src", 961 "__len", 962 }, 963 exclude = excludes_stack_overflow, 964 stackvars = "\twchar_t src[__len + 10];\n", 965 }, 966 { 967 func = "wmemmove", 968 buftype = "wchar_t[]", 969 arguments = { 970 "__buf", 971 "src", 972 "__len", 973 }, 974 exclude = excludes_stack_overflow, 975 stackvars = "\twchar_t src[__len + 10];\n", 976 }, 977 { 978 func = "wmemset", 979 buftype = "wchar_t[]", 980 arguments = { 981 "__buf", 982 "L'0'", 983 "__len", 984 }, 985 exclude = excludes_stack_overflow, 986 }, 987 { 988 func = "wcpcpy", 989 buftype = "wchar_t[]", 990 arguments = { 991 "__buf", 992 "src", 993 }, 994 exclude = excludes_stack_overflow, 995 stackvars = wstring_stackvars, 996 init = wstring_init, 997 uses_len = true, 998 }, 999 { 1000 func = "wcpncpy", 1001 buftype = "wchar_t[]", 1002 arguments = { 1003 "__buf", 1004 "src", 1005 "__len", 1006 }, 1007 exclude = excludes_stack_overflow, 1008 stackvars = wstring_stackvars, 1009 init = wstring_init, 1010 }, 1011 { 1012 func = "wcscat", 1013 buftype = "wchar_t[]", 1014 arguments = { 1015 "__buf", 1016 "src", 1017 }, 1018 exclude = excludes_stack_overflow, 1019 stackvars = wstring_stackvars, 1020 init = wstring_init, 1021 uses_len = true, 1022 }, 1023 { 1024 func = "wcslcat", 1025 buftype = "wchar_t[]", 1026 arguments = { 1027 "__buf", 1028 "src", 1029 "__len", 1030 }, 1031 exclude = excludes_stack_overflow, 1032 stackvars = wstring_stackvars, 1033 init = wstring_init, 1034 }, 1035 { 1036 func = "wcsncat", 1037 buftype = "wchar_t[]", 1038 arguments = { 1039 "__buf", 1040 "src", 1041 "__len", 1042 }, 1043 exclude = excludes_stack_overflow, 1044 stackvars = wstring_stackvars, 1045 init = wstring_init, 1046 }, 1047 { 1048 func = "wcscpy", 1049 buftype = "wchar_t[]", 1050 arguments = { 1051 "__buf", 1052 "src", 1053 }, 1054 exclude = excludes_stack_overflow, 1055 stackvars = wstring_stackvars, 1056 init = wstring_init, 1057 uses_len = true, 1058 }, 1059 { 1060 func = "wcslcpy", 1061 buftype = "wchar_t[]", 1062 arguments = { 1063 "__buf", 1064 "src", 1065 "__len", 1066 }, 1067 exclude = excludes_stack_overflow, 1068 stackvars = wstring_stackvars, 1069 init = wstring_init, 1070 }, 1071 { 1072 func = "wcsncpy", 1073 buftype = "wchar_t[]", 1074 arguments = { 1075 "__buf", 1076 "src", 1077 "__len", 1078 }, 1079 exclude = excludes_stack_overflow, 1080 stackvars = wstring_stackvars, 1081 init = wstring_init, 1082 }, 1083 }, 1084} 1085 1086local function write_test_boilerplate(fh, name, body, def) 1087 fh:write("ATF_TC(" .. name .. ");\n") 1088 fh:write("ATF_TC_HEAD(" .. name .. ", tc)\n") 1089 fh:write("{\n") 1090 if def.need_root then 1091 fh:write(" atf_tc_set_md_var(tc, \"require.user\", \"root\");\n") 1092 end 1093 fh:write("}\n") 1094 1095 fh:write("ATF_TC_BODY(" .. name .. ", tc)\n") 1096 fh:write("{\n" .. body .. "\n}\n\n") 1097 return name 1098end 1099 1100local function generate_test_name(func, variant, disposition, heap) 1101 local basename = func 1102 if variant then 1103 basename = basename .. "_" .. variant 1104 end 1105 if heap then 1106 basename = basename .. "_heap" 1107 end 1108 if disposition < 0 then 1109 return basename .. "_before_end" 1110 elseif disposition == 0 then 1111 return basename .. "_end" 1112 else 1113 return basename .. "_after_end" 1114 end 1115end 1116 1117local function array_type(buftype) 1118 if not buftype:match("%[%]") then 1119 return nil 1120 end 1121 1122 return buftype:gsub("%[%]", "") 1123end 1124 1125local function configurable(def, idx) 1126 local cfgitem = def[idx] 1127 1128 if not cfgitem then 1129 return nil 1130 end 1131 1132 if type(cfgitem) == "function" then 1133 return cfgitem() 1134 end 1135 1136 return cfgitem 1137end 1138 1139local function generate_stackframe(buftype, bufsize, disposition, heap, def) 1140 local function len_offset(inverted) 1141 -- Tests that don't use __len in their arguments may use an 1142 -- inverted sense because we can't just specify a length that 1143 -- would induce an access just after the end. Instead, we have 1144 -- to manipulate the buffer size to be too short so that the 1145 -- function under test would write one too many. 1146 if disposition < 0 then 1147 return ((inverted and " + ") or " - ") .. "1" 1148 elseif disposition == 0 then 1149 return "" 1150 else 1151 return ((inverted and " - ") or " + ") .. "1" 1152 end 1153 end 1154 1155 local function test_uses_len() 1156 if def.uses_len then 1157 return true 1158 end 1159 1160 for _, arg in ipairs(def.arguments) do 1161 if arg:match("__len") or arg:match("__idx") then 1162 return true 1163 end 1164 end 1165 1166 return false 1167 end 1168 1169 1170 -- This is perhaps a little convoluted, but we toss the buffer into a 1171 -- struct on the stack to guarantee that we have at least one valid 1172 -- byte on either side of the buffer -- a measure to make sure that 1173 -- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case, 1174 -- rather than some other stack or memory protection. 1175 local vars = "\tstruct {\n" 1176 vars = vars .. "\t\tuint8_t padding_l;\n" 1177 1178 local uses_len = test_uses_len() 1179 local bufsize_offset = len_offset(not uses_len) 1180 local buftype_elem = array_type(buftype) 1181 local size_expr = bufsize 1182 1183 if not uses_len then 1184 -- If the length isn't in use, we have to vary the buffer size 1185 -- since the fortified function likely has some internal size 1186 -- constraint that it's supposed to be checking. 1187 size_expr = size_expr .. bufsize_offset 1188 end 1189 1190 if not heap and buftype_elem then 1191 -- Array type: size goes after identifier 1192 vars = vars .. "\t\t" .. buftype_elem .. 1193 " __buf[" .. size_expr .. "];\n" 1194 else 1195 local basic_type = buftype_elem or buftype 1196 1197 -- Heap tests obviously just put a pointer on the stack that 1198 -- points to our new allocation, but we leave it in the padded 1199 -- struct just to simplify our generator. 1200 if heap then 1201 basic_type = basic_type .. " *" 1202 end 1203 vars = vars .. "\t\t" .. basic_type .. " __buf;\n" 1204 end 1205 1206 -- padding_r is our just-past-the-end padding that we use to make sure 1207 -- that there's a valid portion after the buffer that isn't being 1208 -- included in our function calls. If we didn't have it, then we'd have 1209 -- a hard time feeling confident that an abort on the just-after tests 1210 -- isn't maybe from some other memory or stack protection. 1211 vars = vars .. "\t\tuint8_t padding_r;\n" 1212 vars = vars .. "\t} __stack;\n" 1213 1214 -- Not all tests will use __bufsz, but some do for, e.g., clearing 1215 -- memory.. 1216 vars = vars .. "\tconst size_t __bufsz __unused = " 1217 if heap then 1218 local scalar = 1 1219 if buftype_elem then 1220 scalar = size_expr 1221 end 1222 1223 vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n" 1224 else 1225 vars = vars .. "sizeof(__stack.__buf);\n" 1226 end 1227 1228 vars = vars .. "\tconst size_t __len = " .. bufsize .. 1229 bufsize_offset .. ";\n" 1230 vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n" 1231 1232 -- For overflow testing, we need to fork() because we're expecting the 1233 -- test to ultimately abort()/_exit(). Then we can collect the exit 1234 -- status and report appropriately. 1235 if disposition > 0 then 1236 vars = vars .. "\tpid_t __child;\n" 1237 vars = vars .. "\tint __status;\n" 1238 end 1239 1240 -- Any other stackvars defined by the test get placed after everything 1241 -- else. 1242 vars = vars .. (configurable(def, "stackvars") or "") 1243 1244 return vars 1245end 1246 1247local function write_test(fh, func, disposition, heap, def) 1248 local testname = generate_test_name(func, def.variant, disposition, heap) 1249 local buftype = def.buftype or "unsigned char[]" 1250 local bufsize = def.bufsize or 42 1251 local body = "" 1252 1253 if def.exclude and def.exclude(disposition, heap) then 1254 return 1255 end 1256 1257 local function need_addr() 1258 return not (buftype:match("%[%]") or buftype:match("%*")) 1259 end 1260 1261 if heap then 1262 body = body .. "#define BUF __stack.__buf\n" 1263 else 1264 body = body .. "#define BUF &__stack.__buf\n" 1265 end 1266 1267 -- Setup the buffer 1268 body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) .. 1269 "\n" 1270 1271 -- Any early initialization goes before we would fork for the just-after 1272 -- tests, because they may want to skip the test based on some criteria 1273 -- and we can't propagate that up very easily once we're forked. 1274 local early_init = configurable(def, "early_init") 1275 body = body .. (early_init or "") 1276 if early_init then 1277 body = body .. "\n" 1278 end 1279 1280 -- Fork off, iff we're testing some access past the end of the buffer. 1281 if disposition > 0 then 1282 body = body .. [[ 1283 __child = fork(); 1284 ATF_REQUIRE(__child >= 0); 1285 if (__child > 0) 1286 goto monitor; 1287 1288 /* Child */ 1289 disable_coredumps(); 1290]] 1291 end 1292 1293 local bufvar = "__stack.__buf" 1294 if heap then 1295 -- Buffer needs to be initialized because it's a heap allocation. 1296 body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n" 1297 end 1298 1299 -- Non-early init happens just after the fork in the child, not the 1300 -- monitor. This is used to setup any other buffers we may need, for 1301 -- instance. 1302 local extra_init = configurable(def, "init") 1303 body = body .. (extra_init or "") 1304 1305 if heap or extra_init then 1306 body = body .. "\n" 1307 end 1308 1309 -- Setup the function call with arguments as described in the test 1310 -- definition. 1311 body = body .. "\t" .. func .. "(" 1312 1313 for idx, arg in ipairs(def.arguments) do 1314 if idx > 1 then 1315 body = body .. ", " 1316 end 1317 1318 if arg == "__buf" then 1319 if not heap and need_addr() then 1320 body = body .. "&" 1321 end 1322 1323 body = body .. bufvar 1324 else 1325 local argname = arg 1326 1327 if def.value_of then 1328 argname = argname or def.value_of(arg) 1329 end 1330 1331 body = body .. argname 1332 end 1333 end 1334 1335 body = body .. ");\n" 1336 1337 -- Monitor stuff follows, for OOB access. 1338 if disposition <= 0 then 1339 goto skip 1340 end 1341 1342 body = body .. [[ 1343 _exit(EX_SOFTWARE); /* Should have aborted. */ 1344 1345monitor: 1346 while (waitpid(__child, &__status, 0) != __child) { 1347 ATF_REQUIRE_EQ(EINTR, errno); 1348 } 1349 1350 if (!WIFSIGNALED(__status)) { 1351 switch (WEXITSTATUS(__status)) { 1352 case EX_SOFTWARE: 1353 atf_tc_fail("FORTIFY_SOURCE failed to abort"); 1354 break; 1355 case EX_OSERR: 1356 atf_tc_fail("setrlimit(2) failed"); 1357 break; 1358 default: 1359 atf_tc_fail("child exited with status %d", 1360 WEXITSTATUS(__status)); 1361 } 1362 } else { 1363 ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); 1364 } 1365]] 1366 1367::skip:: 1368 body = body .. "#undef BUF\n" 1369 return write_test_boilerplate(fh, testname, body, def) 1370end 1371 1372-- main() 1373local tests 1374local tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>") 1375for k, defs in pairs(all_tests) do 1376 if k == tcat then 1377 tests = defs 1378 break 1379 end 1380end 1381 1382assert(tests, "category " .. tcat .. " not found") 1383 1384local fh = io.stdout 1385fh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" .. 1386 tcat .. "\"` */\n\n") 1387fh:write("#define _FORTIFY_SOURCE 2\n") 1388fh:write("#define TMPFILE_SIZE (1024 * 32)\n") 1389 1390fh:write("\n") 1391for _, inc in ipairs(includes) do 1392 fh:write("#include <" .. inc .. ">\n") 1393end 1394 1395fh:write([[ 1396 1397static FILE * __unused 1398new_fp(size_t __len) 1399{ 1400 static char fpbuf[LINE_MAX]; 1401 FILE *fp; 1402 1403 ATF_REQUIRE(__len <= sizeof(fpbuf)); 1404 1405 memset(fpbuf, 'A', sizeof(fpbuf) - 1); 1406 fpbuf[sizeof(fpbuf) - 1] = '\0'; 1407 1408 fp = fmemopen(fpbuf, sizeof(fpbuf), "rb"); 1409 ATF_REQUIRE(fp != NULL); 1410 1411 return (fp); 1412} 1413 1414/* 1415 * Create a new symlink to use for readlink(2) style tests, we'll just use a 1416 * random target name to have something interesting to look at. 1417 */ 1418static const char * __unused 1419new_symlink(size_t __len) 1420{ 1421 static const char linkname[] = "link"; 1422 char target[MAXNAMLEN]; 1423 int error; 1424 1425 ATF_REQUIRE(__len <= sizeof(target)); 1426 1427 arc4random_buf(target, sizeof(target)); 1428 1429 error = unlink(linkname); 1430 ATF_REQUIRE(error == 0 || errno == ENOENT); 1431 1432 error = symlink(target, linkname); 1433 ATF_REQUIRE(error == 0); 1434 1435 return (linkname); 1436} 1437 1438/* 1439 * For our purposes, first descriptor will be the reader; we'll send both 1440 * raw data and a control message over it so that the result can be used for 1441 * any of our recv*() tests. 1442 */ 1443static void __unused 1444new_socket(int sock[2]) 1445{ 1446 unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; 1447 static char sockbuf[256]; 1448 ssize_t rv; 1449 size_t total = 0; 1450 struct msghdr hdr = { 0 }; 1451 struct cmsghdr *cmsg; 1452 int error, fd; 1453 1454 error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); 1455 ATF_REQUIRE(error == 0); 1456 1457 while (total != sizeof(sockbuf)) { 1458 rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); 1459 1460 ATF_REQUIRE_MSG(rv > 0, 1461 "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", 1462 rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); 1463 ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), 1464 "%zd exceeds total %zu", rv, sizeof(sockbuf)); 1465 total += rv; 1466 } 1467 1468 hdr.msg_control = ctrl; 1469 hdr.msg_controllen = sizeof(ctrl); 1470 1471 cmsg = CMSG_FIRSTHDR(&hdr); 1472 cmsg->cmsg_level = SOL_SOCKET; 1473 cmsg->cmsg_type = SCM_RIGHTS; 1474 cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 1475 fd = STDIN_FILENO; 1476 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); 1477 1478 error = sendmsg(sock[1], &hdr, 0); 1479 ATF_REQUIRE(error != -1); 1480} 1481 1482/* 1483 * Constructs a tmpfile that we can use for testing read(2) and friends. 1484 */ 1485static int __unused 1486new_tmpfile(void) 1487{ 1488 char buf[1024]; 1489 ssize_t rv; 1490 size_t written; 1491 int fd; 1492 1493 fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644); 1494 ATF_REQUIRE(fd >= 0); 1495 1496 written = 0; 1497 while (written < TMPFILE_SIZE) { 1498 rv = write(fd, buf, sizeof(buf)); 1499 ATF_REQUIRE(rv > 0); 1500 1501 written += rv; 1502 } 1503 1504 ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET)); 1505 return (fd); 1506} 1507 1508static void 1509disable_coredumps(void) 1510{ 1511 struct rlimit rl = { 0 }; 1512 1513 if (setrlimit(RLIMIT_CORE, &rl) == -1) 1514 _exit(EX_OSERR); 1515} 1516 1517/* 1518 * Replaces stdin with a file that we can actually read from, for tests where 1519 * we want a FILE * or fd that we can get data from. 1520 */ 1521static void __unused 1522replace_stdin(void) 1523{ 1524 int fd; 1525 1526 fd = new_tmpfile(); 1527 1528 (void)dup2(fd, STDIN_FILENO); 1529 if (fd != STDIN_FILENO) 1530 close(fd); 1531} 1532 1533]]) 1534 1535if tcat == "unistd" then 1536 fh:write("#define JAIL_HOSTNAME \"" .. hostname .. "\"\n") 1537 fh:write("#define JAIL_DOMAINNAME \"" .. domainname .. "\"\n") 1538 fh:write([[ 1539static void 1540dhost_jail(void) 1541{ 1542 struct iovec iov[4]; 1543 int jid; 1544 1545 iov[0].iov_base = __DECONST(char *, "host.hostname"); 1546 iov[0].iov_len = sizeof("host.hostname"); 1547 iov[1].iov_base = __DECONST(char *, JAIL_HOSTNAME); 1548 iov[1].iov_len = sizeof(JAIL_HOSTNAME); 1549 iov[2].iov_base = __DECONST(char *, "host.domainname"); 1550 iov[2].iov_len = sizeof("host.domainname"); 1551 iov[3].iov_base = __DECONST(char *, JAIL_DOMAINNAME); 1552 iov[3].iov_len = sizeof(JAIL_DOMAINNAME); 1553 1554 jid = jail_set(iov, nitems(iov), JAIL_CREATE | JAIL_ATTACH); 1555 ATF_REQUIRE_MSG(jid > 0, "Jail creation failed: %s", strerror(errno)); 1556} 1557 1558]]) 1559end 1560 1561for _, def in pairs(tests) do 1562 local func = def.func 1563 local function write_tests(heap) 1564 -- Dispositions here are relative to the buffer size prescribed 1565 -- by the test definition. 1566 local dispositions = def.dispositions or { -1, 0, 1 } 1567 1568 for _, disposition in ipairs(dispositions) do 1569 tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def) 1570 end 1571 end 1572 1573 write_tests(false) 1574 write_tests(true) 1575end 1576 1577fh:write("ATF_TP_ADD_TCS(tp)\n") 1578fh:write("{\n") 1579for idx = 1, #tests_added do 1580 fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n") 1581end 1582fh:write("\treturn (atf_no_error());\n") 1583fh:write("}\n") 1584