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 }, 415 { 416 func = "readv", 417 variant = "iov", 418 arguments = { 419 "STDIN_FILENO", 420 "iov", 421 "nitems(iov)", 422 }, 423 exclude = excludes_stack_overflow, 424 stackvars = readv_stackvars, 425 init = readv_init, 426 uses_len = true, 427 }, 428 { 429 func = "preadv", 430 buftype = "struct iovec[]", 431 bufsize = 2, 432 arguments = { 433 "STDIN_FILENO", 434 "__buf", 435 "__len", 436 "0", 437 }, 438 }, 439 { 440 func = "preadv", 441 variant = "iov", 442 arguments = { 443 "STDIN_FILENO", 444 "iov", 445 "nitems(iov)", 446 "0", 447 }, 448 exclude = excludes_stack_overflow, 449 stackvars = readv_stackvars, 450 init = readv_init, 451 uses_len = true, 452 }, 453 }, 454 poll = { 455 -- <poll.h> 456 { 457 func = "poll", 458 bufsize = "4", 459 buftype = "struct pollfd[]", 460 arguments = { 461 "__buf", 462 "__len", 463 "0", 464 }, 465 init = poll_init, 466 }, 467 { 468 func = "ppoll", 469 bufsize = "4", 470 buftype = "struct pollfd[]", 471 arguments = { 472 "__buf", 473 "__len", 474 "&tv", 475 "NULL", 476 }, 477 stackvars = "\tstruct timespec tv = { 0 };\n", 478 init = poll_init, 479 }, 480 }, 481 stdio = { 482 -- <stdio.h> 483 { 484 func = "ctermid", 485 bufsize = "L_ctermid", 486 arguments = { 487 "__buf", 488 }, 489 exclude = excludes_stack_overflow, 490 }, 491 { 492 func = "ctermid_r", 493 bufsize = "L_ctermid", 494 arguments = { 495 "__buf", 496 }, 497 exclude = excludes_stack_overflow, 498 }, 499 { 500 func = "fread", 501 arguments = { 502 "__buf", 503 "__len", 504 "1", 505 "stdin", 506 }, 507 exclude = excludes_stack_overflow, 508 init = stdio_init, 509 }, 510 { 511 func = "fread_unlocked", 512 arguments = { 513 "__buf", 514 "__len", 515 "1", 516 "stdin", 517 }, 518 exclude = excludes_stack_overflow, 519 init = stdio_init, 520 }, 521 { 522 func = "gets_s", 523 arguments = { 524 "__buf", 525 "__len", 526 }, 527 exclude = excludes_stack_overflow, 528 init = stdio_init, 529 }, 530 { 531 func = "sprintf", 532 arguments = { 533 "__buf", 534 "\"%.*s\"", 535 "(int)__len - 1", -- - 1 for NUL terminator 536 "srcvar", 537 }, 538 exclude = excludes_stack_overflow, 539 stackvars = printf_stackvars, 540 init = printf_init, 541 }, 542 { 543 func = "snprintf", 544 arguments = { 545 "__buf", 546 "__len", 547 "\"%.*s\"", 548 "(int)__len - 1", -- - 1 for NUL terminator 549 "srcvar", 550 }, 551 exclude = excludes_stack_overflow, 552 stackvars = printf_stackvars, 553 init = printf_init, 554 }, 555 { 556 func = "tmpnam", 557 bufsize = "L_tmpnam", 558 arguments = { 559 "__buf", 560 }, 561 exclude = excludes_stack_overflow, 562 }, 563 { 564 func = "fgets", 565 arguments = { 566 "__buf", 567 "__len", 568 "fp", 569 }, 570 exclude = excludes_stack_overflow, 571 stackvars = "\tFILE *fp;\n", 572 init = [[ 573 fp = new_fp(__len); 574]], 575 }, 576 }, 577 stdlib = { 578 -- <stdlib.h> 579 { 580 func = "arc4random_buf", 581 arguments = { 582 "__buf", 583 "__len", 584 }, 585 exclude = excludes_stack_overflow, 586 }, 587 { 588 func = "getenv_r", 589 arguments = { 590 "\"PATH\"", 591 "__buf", 592 "__len", 593 }, 594 exclude = excludes_stack_overflow, 595 }, 596 { 597 func = "realpath", 598 bufsize = "PATH_MAX", 599 arguments = { 600 "\".\"", 601 "__buf", 602 }, 603 exclude = excludes_stack_overflow, 604 }, 605 }, 606 string = { 607 -- <string.h> 608 { 609 func = "memcpy", 610 arguments = { 611 "__buf", 612 "src", 613 "__len", 614 }, 615 exclude = excludes_stack_overflow, 616 stackvars = "\tchar src[__len + 10];\n", 617 }, 618 { 619 func = "mempcpy", 620 arguments = { 621 "__buf", 622 "src", 623 "__len", 624 }, 625 exclude = excludes_stack_overflow, 626 stackvars = "\tchar src[__len + 10];\n", 627 }, 628 { 629 func = "memmove", 630 arguments = { 631 "__buf", 632 "src", 633 "__len", 634 }, 635 exclude = excludes_stack_overflow, 636 stackvars = "\tchar src[__len + 10];\n", 637 }, 638 { 639 func = "memset", 640 arguments = { 641 "__buf", 642 "0", 643 "__len", 644 }, 645 exclude = excludes_stack_overflow, 646 }, 647 { 648 func = "memset_explicit", 649 arguments = { 650 "__buf", 651 "0", 652 "__len", 653 }, 654 exclude = excludes_stack_overflow, 655 }, 656 { 657 func = "stpcpy", 658 arguments = { 659 "__buf", 660 "src", 661 }, 662 exclude = excludes_stack_overflow, 663 stackvars = string_stackvars, 664 init = string_init, 665 uses_len = true, 666 }, 667 { 668 func = "stpncpy", 669 arguments = { 670 "__buf", 671 "src", 672 "__len", 673 }, 674 exclude = excludes_stack_overflow, 675 stackvars = string_stackvars, 676 init = string_init, 677 }, 678 { 679 func = "strcat", 680 arguments = { 681 "__buf", 682 "src", 683 }, 684 exclude = excludes_stack_overflow, 685 stackvars = string_stackvars, 686 init = string_init, 687 uses_len = true, 688 }, 689 { 690 func = "strlcat", 691 arguments = { 692 "__buf", 693 "src", 694 "__len", 695 }, 696 exclude = excludes_stack_overflow, 697 stackvars = string_stackvars, 698 init = string_init, 699 }, 700 { 701 func = "strncat", 702 arguments = { 703 "__buf", 704 "src", 705 "__len", 706 }, 707 exclude = excludes_stack_overflow, 708 stackvars = string_stackvars, 709 init = string_init, 710 }, 711 { 712 func = "strcpy", 713 arguments = { 714 "__buf", 715 "src", 716 }, 717 exclude = excludes_stack_overflow, 718 stackvars = string_stackvars, 719 init = string_init, 720 uses_len = true, 721 }, 722 { 723 func = "strlcpy", 724 arguments = { 725 "__buf", 726 "src", 727 "__len", 728 }, 729 exclude = excludes_stack_overflow, 730 stackvars = string_stackvars, 731 init = string_init, 732 }, 733 { 734 func = "strncpy", 735 arguments = { 736 "__buf", 737 "src", 738 "__len", 739 }, 740 exclude = excludes_stack_overflow, 741 stackvars = string_stackvars, 742 init = string_init, 743 }, 744 }, 745 strings = { 746 -- <strings.h> 747 { 748 func = "bcopy", 749 arguments = { 750 "src", 751 "__buf", 752 "__len", 753 }, 754 exclude = excludes_stack_overflow, 755 stackvars = "\tchar src[__len + 10];\n", 756 }, 757 { 758 func = "bzero", 759 arguments = { 760 "__buf", 761 "__len", 762 }, 763 exclude = excludes_stack_overflow, 764 }, 765 { 766 func = "explicit_bzero", 767 arguments = { 768 "__buf", 769 "__len", 770 }, 771 exclude = excludes_stack_overflow, 772 }, 773 }, 774 unistd = { 775 -- <unistd.h> 776 { 777 func = "getcwd", 778 bufsize = "8", 779 arguments = { 780 "__buf", 781 "__len", 782 }, 783 exclude = excludes_stack_overflow, 784 }, 785 { 786 func = "getgrouplist", 787 bufsize = "4", 788 buftype = "gid_t[]", 789 arguments = { 790 "\"root\"", 791 "0", 792 "__buf", 793 "&intlen", 794 }, 795 exclude = excludes_stack_overflow, 796 stackvars = "\tint intlen = (int)__len;\n", 797 uses_len = true, 798 }, 799 { 800 func = "getgroups", 801 bufsize = "4", 802 buftype = "gid_t[]", 803 arguments = { 804 "__len", 805 "__buf", 806 }, 807 exclude = excludes_stack_overflow, 808 }, 809 { 810 func = "getloginclass", 811 arguments = { 812 "__buf", 813 "__len", 814 }, 815 exclude = excludes_stack_overflow, 816 }, 817 { 818 func = "pread", 819 bufsize = "41", 820 arguments = { 821 "fd", 822 "__buf", 823 "__len", 824 "0", 825 }, 826 exclude = excludes_stack_overflow, 827 stackvars = "\tint fd;\n", 828 init = [[ 829 fd = new_tmpfile(); /* Cannot fail */ 830]], 831 }, 832 { 833 func = "read", 834 bufsize = "41", 835 arguments = { 836 "fd", 837 "__buf", 838 "__len", 839 }, 840 exclude = excludes_stack_overflow, 841 stackvars = "\tint fd;\n", 842 init = [[ 843 fd = new_tmpfile(); /* Cannot fail */ 844]], 845 }, 846 { 847 func = "readlink", 848 arguments = { 849 "path", 850 "__buf", 851 "__len", 852 }, 853 exclude = excludes_stack_overflow, 854 stackvars = "\tconst char *path;\n", 855 init = [[ 856 path = new_symlink(__len); /* Cannot fail */ 857]], 858 }, 859 { 860 func = "readlinkat", 861 arguments = { 862 "AT_FDCWD", 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 = "getdomainname", 875 bufsize = #domainname + 1, 876 arguments = { 877 "__buf", 878 "__len", 879 }, 880 need_root = true, 881 exclude = excludes_stack_overflow, 882 early_init = " dhost_jail();", 883 }, 884 { 885 func = "getentropy", 886 arguments = { 887 "__buf", 888 "__len", 889 }, 890 exclude = excludes_stack_overflow, 891 }, 892 { 893 func = "gethostname", 894 bufsize = #hostname + 1, 895 arguments = { 896 "__buf", 897 "__len", 898 }, 899 need_root = true, 900 exclude = excludes_stack_overflow, 901 early_init = " dhost_jail();", 902 }, 903 { 904 func = "getlogin_r", 905 bufsize = "MAXLOGNAME + 1", 906 arguments = { 907 "__buf", 908 "__len", 909 }, 910 exclude = excludes_stack_overflow, 911 }, 912 { 913 func = "ttyname_r", 914 arguments = { 915 "fd", 916 "__buf", 917 "__len", 918 }, 919 exclude = excludes_stack_overflow, 920 stackvars = "\tint fd;\n", 921 early_init = [[ 922 fd = STDIN_FILENO; 923 if (!isatty(fd)) 924 atf_tc_skip("stdin is not an fd"); 925]] 926 }, 927 }, 928 wchar = { 929 -- <wchar.h> 930 { 931 func = "wmemcpy", 932 buftype = "wchar_t[]", 933 arguments = { 934 "__buf", 935 "src", 936 "__len", 937 }, 938 exclude = excludes_stack_overflow, 939 stackvars = "\twchar_t src[__len + 10];\n", 940 }, 941 { 942 func = "wmempcpy", 943 buftype = "wchar_t[]", 944 arguments = { 945 "__buf", 946 "src", 947 "__len", 948 }, 949 exclude = excludes_stack_overflow, 950 stackvars = "\twchar_t src[__len + 10];\n", 951 }, 952 { 953 func = "wmemmove", 954 buftype = "wchar_t[]", 955 arguments = { 956 "__buf", 957 "src", 958 "__len", 959 }, 960 exclude = excludes_stack_overflow, 961 stackvars = "\twchar_t src[__len + 10];\n", 962 }, 963 { 964 func = "wmemset", 965 buftype = "wchar_t[]", 966 arguments = { 967 "__buf", 968 "L'0'", 969 "__len", 970 }, 971 exclude = excludes_stack_overflow, 972 }, 973 { 974 func = "wcpcpy", 975 buftype = "wchar_t[]", 976 arguments = { 977 "__buf", 978 "src", 979 }, 980 exclude = excludes_stack_overflow, 981 stackvars = wstring_stackvars, 982 init = wstring_init, 983 uses_len = true, 984 }, 985 { 986 func = "wcpncpy", 987 buftype = "wchar_t[]", 988 arguments = { 989 "__buf", 990 "src", 991 "__len", 992 }, 993 exclude = excludes_stack_overflow, 994 stackvars = wstring_stackvars, 995 init = wstring_init, 996 }, 997 { 998 func = "wcscat", 999 buftype = "wchar_t[]", 1000 arguments = { 1001 "__buf", 1002 "src", 1003 }, 1004 exclude = excludes_stack_overflow, 1005 stackvars = wstring_stackvars, 1006 init = wstring_init, 1007 uses_len = true, 1008 }, 1009 { 1010 func = "wcslcat", 1011 buftype = "wchar_t[]", 1012 arguments = { 1013 "__buf", 1014 "src", 1015 "__len", 1016 }, 1017 exclude = excludes_stack_overflow, 1018 stackvars = wstring_stackvars, 1019 init = wstring_init, 1020 }, 1021 { 1022 func = "wcsncat", 1023 buftype = "wchar_t[]", 1024 arguments = { 1025 "__buf", 1026 "src", 1027 "__len", 1028 }, 1029 exclude = excludes_stack_overflow, 1030 stackvars = wstring_stackvars, 1031 init = wstring_init, 1032 }, 1033 { 1034 func = "wcscpy", 1035 buftype = "wchar_t[]", 1036 arguments = { 1037 "__buf", 1038 "src", 1039 }, 1040 exclude = excludes_stack_overflow, 1041 stackvars = wstring_stackvars, 1042 init = wstring_init, 1043 uses_len = true, 1044 }, 1045 { 1046 func = "wcslcpy", 1047 buftype = "wchar_t[]", 1048 arguments = { 1049 "__buf", 1050 "src", 1051 "__len", 1052 }, 1053 exclude = excludes_stack_overflow, 1054 stackvars = wstring_stackvars, 1055 init = wstring_init, 1056 }, 1057 { 1058 func = "wcsncpy", 1059 buftype = "wchar_t[]", 1060 arguments = { 1061 "__buf", 1062 "src", 1063 "__len", 1064 }, 1065 exclude = excludes_stack_overflow, 1066 stackvars = wstring_stackvars, 1067 init = wstring_init, 1068 }, 1069 }, 1070} 1071 1072local function write_test_boilerplate(fh, name, body, def) 1073 fh:write("ATF_TC(" .. name .. ");\n") 1074 fh:write("ATF_TC_HEAD(" .. name .. ", tc)\n") 1075 fh:write("{\n") 1076 if def.need_root then 1077 fh:write(" atf_tc_set_md_var(tc, \"require.user\", \"root\");\n") 1078 end 1079 fh:write("}\n") 1080 1081 fh:write("ATF_TC_BODY(" .. name .. ", tc)\n") 1082 fh:write("{\n" .. body .. "\n}\n\n") 1083 return name 1084end 1085 1086local function generate_test_name(func, variant, disposition, heap) 1087 local basename = func 1088 if variant then 1089 basename = basename .. "_" .. variant 1090 end 1091 if heap then 1092 basename = basename .. "_heap" 1093 end 1094 if disposition < 0 then 1095 return basename .. "_before_end" 1096 elseif disposition == 0 then 1097 return basename .. "_end" 1098 else 1099 return basename .. "_after_end" 1100 end 1101end 1102 1103local function array_type(buftype) 1104 if not buftype:match("%[%]") then 1105 return nil 1106 end 1107 1108 return buftype:gsub("%[%]", "") 1109end 1110 1111local function configurable(def, idx) 1112 local cfgitem = def[idx] 1113 1114 if not cfgitem then 1115 return nil 1116 end 1117 1118 if type(cfgitem) == "function" then 1119 return cfgitem() 1120 end 1121 1122 return cfgitem 1123end 1124 1125local function generate_stackframe(buftype, bufsize, disposition, heap, def) 1126 local function len_offset(inverted) 1127 -- Tests that don't use __len in their arguments may use an 1128 -- inverted sense because we can't just specify a length that 1129 -- would induce an access just after the end. Instead, we have 1130 -- to manipulate the buffer size to be too short so that the 1131 -- function under test would write one too many. 1132 if disposition < 0 then 1133 return ((inverted and " + ") or " - ") .. "1" 1134 elseif disposition == 0 then 1135 return "" 1136 else 1137 return ((inverted and " - ") or " + ") .. "1" 1138 end 1139 end 1140 1141 local function test_uses_len() 1142 if def.uses_len then 1143 return true 1144 end 1145 1146 for _, arg in ipairs(def.arguments) do 1147 if arg:match("__len") or arg:match("__idx") then 1148 return true 1149 end 1150 end 1151 1152 return false 1153 end 1154 1155 1156 -- This is perhaps a little convoluted, but we toss the buffer into a 1157 -- struct on the stack to guarantee that we have at least one valid 1158 -- byte on either side of the buffer -- a measure to make sure that 1159 -- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case, 1160 -- rather than some other stack or memory protection. 1161 local vars = "\tstruct {\n" 1162 vars = vars .. "\t\tuint8_t padding_l;\n" 1163 1164 local uses_len = test_uses_len() 1165 local bufsize_offset = len_offset(not uses_len) 1166 local buftype_elem = array_type(buftype) 1167 local size_expr = bufsize 1168 1169 if not uses_len then 1170 -- If the length isn't in use, we have to vary the buffer size 1171 -- since the fortified function likely has some internal size 1172 -- constraint that it's supposed to be checking. 1173 size_expr = size_expr .. bufsize_offset 1174 end 1175 1176 if not heap and buftype_elem then 1177 -- Array type: size goes after identifier 1178 vars = vars .. "\t\t" .. buftype_elem .. 1179 " __buf[" .. size_expr .. "];\n" 1180 else 1181 local basic_type = buftype_elem or buftype 1182 1183 -- Heap tests obviously just put a pointer on the stack that 1184 -- points to our new allocation, but we leave it in the padded 1185 -- struct just to simplify our generator. 1186 if heap then 1187 basic_type = basic_type .. " *" 1188 end 1189 vars = vars .. "\t\t" .. basic_type .. " __buf;\n" 1190 end 1191 1192 -- padding_r is our just-past-the-end padding that we use to make sure 1193 -- that there's a valid portion after the buffer that isn't being 1194 -- included in our function calls. If we didn't have it, then we'd have 1195 -- a hard time feeling confident that an abort on the just-after tests 1196 -- isn't maybe from some other memory or stack protection. 1197 vars = vars .. "\t\tuint8_t padding_r;\n" 1198 vars = vars .. "\t} __stack;\n" 1199 1200 -- Not all tests will use __bufsz, but some do for, e.g., clearing 1201 -- memory.. 1202 vars = vars .. "\tconst size_t __bufsz __unused = " 1203 if heap then 1204 local scalar = 1 1205 if buftype_elem then 1206 scalar = size_expr 1207 end 1208 1209 vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n" 1210 else 1211 vars = vars .. "sizeof(__stack.__buf);\n" 1212 end 1213 1214 vars = vars .. "\tconst size_t __len = " .. bufsize .. 1215 bufsize_offset .. ";\n" 1216 vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n" 1217 1218 -- For overflow testing, we need to fork() because we're expecting the 1219 -- test to ultimately abort()/_exit(). Then we can collect the exit 1220 -- status and report appropriately. 1221 if disposition > 0 then 1222 vars = vars .. "\tpid_t __child;\n" 1223 vars = vars .. "\tint __status;\n" 1224 end 1225 1226 -- Any other stackvars defined by the test get placed after everything 1227 -- else. 1228 vars = vars .. (configurable(def, "stackvars") or "") 1229 1230 return vars 1231end 1232 1233local function write_test(fh, func, disposition, heap, def) 1234 local testname = generate_test_name(func, def.variant, disposition, heap) 1235 local buftype = def.buftype or "unsigned char[]" 1236 local bufsize = def.bufsize or 42 1237 local body = "" 1238 1239 if def.exclude and def.exclude(disposition, heap) then 1240 return 1241 end 1242 1243 local function need_addr() 1244 return not (buftype:match("%[%]") or buftype:match("%*")) 1245 end 1246 1247 if heap then 1248 body = body .. "#define BUF __stack.__buf\n" 1249 else 1250 body = body .. "#define BUF &__stack.__buf\n" 1251 end 1252 1253 -- Setup the buffer 1254 body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) .. 1255 "\n" 1256 1257 -- Any early initialization goes before we would fork for the just-after 1258 -- tests, because they may want to skip the test based on some criteria 1259 -- and we can't propagate that up very easily once we're forked. 1260 local early_init = configurable(def, "early_init") 1261 body = body .. (early_init or "") 1262 if early_init then 1263 body = body .. "\n" 1264 end 1265 1266 -- Fork off, iff we're testing some access past the end of the buffer. 1267 if disposition > 0 then 1268 body = body .. [[ 1269 __child = fork(); 1270 ATF_REQUIRE(__child >= 0); 1271 if (__child > 0) 1272 goto monitor; 1273 1274 /* Child */ 1275 disable_coredumps(); 1276]] 1277 end 1278 1279 local bufvar = "__stack.__buf" 1280 if heap then 1281 -- Buffer needs to be initialized because it's a heap allocation. 1282 body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n" 1283 end 1284 1285 -- Non-early init happens just after the fork in the child, not the 1286 -- monitor. This is used to setup any other buffers we may need, for 1287 -- instance. 1288 local extra_init = configurable(def, "init") 1289 body = body .. (extra_init or "") 1290 1291 if heap or extra_init then 1292 body = body .. "\n" 1293 end 1294 1295 -- Setup the function call with arguments as described in the test 1296 -- definition. 1297 body = body .. "\t" .. func .. "(" 1298 1299 for idx, arg in ipairs(def.arguments) do 1300 if idx > 1 then 1301 body = body .. ", " 1302 end 1303 1304 if arg == "__buf" then 1305 if not heap and need_addr() then 1306 body = body .. "&" 1307 end 1308 1309 body = body .. bufvar 1310 else 1311 local argname = arg 1312 1313 if def.value_of then 1314 argname = argname or def.value_of(arg) 1315 end 1316 1317 body = body .. argname 1318 end 1319 end 1320 1321 body = body .. ");\n" 1322 1323 -- Monitor stuff follows, for OOB access. 1324 if disposition <= 0 then 1325 goto skip 1326 end 1327 1328 body = body .. [[ 1329 _exit(EX_SOFTWARE); /* Should have aborted. */ 1330 1331monitor: 1332 while (waitpid(__child, &__status, 0) != __child) { 1333 ATF_REQUIRE_EQ(EINTR, errno); 1334 } 1335 1336 if (!WIFSIGNALED(__status)) { 1337 switch (WEXITSTATUS(__status)) { 1338 case EX_SOFTWARE: 1339 atf_tc_fail("FORTIFY_SOURCE failed to abort"); 1340 break; 1341 case EX_OSERR: 1342 atf_tc_fail("setrlimit(2) failed"); 1343 break; 1344 default: 1345 atf_tc_fail("child exited with status %d", 1346 WEXITSTATUS(__status)); 1347 } 1348 } else { 1349 ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); 1350 } 1351]] 1352 1353::skip:: 1354 body = body .. "#undef BUF\n" 1355 return write_test_boilerplate(fh, testname, body, def) 1356end 1357 1358-- main() 1359local tests 1360local tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>") 1361for k, defs in pairs(all_tests) do 1362 if k == tcat then 1363 tests = defs 1364 break 1365 end 1366end 1367 1368assert(tests, "category " .. tcat .. " not found") 1369 1370local fh = io.stdout 1371fh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" .. 1372 tcat .. "\"` */\n\n") 1373fh:write("#define _FORTIFY_SOURCE 2\n") 1374fh:write("#define TMPFILE_SIZE (1024 * 32)\n") 1375 1376fh:write("\n") 1377for _, inc in ipairs(includes) do 1378 fh:write("#include <" .. inc .. ">\n") 1379end 1380 1381fh:write([[ 1382 1383static FILE * __unused 1384new_fp(size_t __len) 1385{ 1386 static char fpbuf[LINE_MAX]; 1387 FILE *fp; 1388 1389 ATF_REQUIRE(__len <= sizeof(fpbuf)); 1390 1391 memset(fpbuf, 'A', sizeof(fpbuf) - 1); 1392 fpbuf[sizeof(fpbuf) - 1] = '\0'; 1393 1394 fp = fmemopen(fpbuf, sizeof(fpbuf), "rb"); 1395 ATF_REQUIRE(fp != NULL); 1396 1397 return (fp); 1398} 1399 1400/* 1401 * Create a new symlink to use for readlink(2) style tests, we'll just use a 1402 * random target name to have something interesting to look at. 1403 */ 1404static const char * __unused 1405new_symlink(size_t __len) 1406{ 1407 static const char linkname[] = "link"; 1408 char target[MAXNAMLEN]; 1409 int error; 1410 1411 ATF_REQUIRE(__len <= sizeof(target)); 1412 1413 arc4random_buf(target, sizeof(target)); 1414 1415 error = unlink(linkname); 1416 ATF_REQUIRE(error == 0 || errno == ENOENT); 1417 1418 error = symlink(target, linkname); 1419 ATF_REQUIRE(error == 0); 1420 1421 return (linkname); 1422} 1423 1424/* 1425 * For our purposes, first descriptor will be the reader; we'll send both 1426 * raw data and a control message over it so that the result can be used for 1427 * any of our recv*() tests. 1428 */ 1429static void __unused 1430new_socket(int sock[2]) 1431{ 1432 unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; 1433 static char sockbuf[256]; 1434 ssize_t rv; 1435 size_t total = 0; 1436 struct msghdr hdr = { 0 }; 1437 struct cmsghdr *cmsg; 1438 int error, fd; 1439 1440 error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); 1441 ATF_REQUIRE(error == 0); 1442 1443 while (total != sizeof(sockbuf)) { 1444 rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); 1445 1446 ATF_REQUIRE_MSG(rv > 0, 1447 "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", 1448 rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); 1449 ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), 1450 "%zd exceeds total %zu", rv, sizeof(sockbuf)); 1451 total += rv; 1452 } 1453 1454 hdr.msg_control = ctrl; 1455 hdr.msg_controllen = sizeof(ctrl); 1456 1457 cmsg = CMSG_FIRSTHDR(&hdr); 1458 cmsg->cmsg_level = SOL_SOCKET; 1459 cmsg->cmsg_type = SCM_RIGHTS; 1460 cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 1461 fd = STDIN_FILENO; 1462 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); 1463 1464 error = sendmsg(sock[1], &hdr, 0); 1465 ATF_REQUIRE(error != -1); 1466} 1467 1468/* 1469 * Constructs a tmpfile that we can use for testing read(2) and friends. 1470 */ 1471static int __unused 1472new_tmpfile(void) 1473{ 1474 char buf[1024]; 1475 ssize_t rv; 1476 size_t written; 1477 int fd; 1478 1479 fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644); 1480 ATF_REQUIRE(fd >= 0); 1481 1482 written = 0; 1483 while (written < TMPFILE_SIZE) { 1484 rv = write(fd, buf, sizeof(buf)); 1485 ATF_REQUIRE(rv > 0); 1486 1487 written += rv; 1488 } 1489 1490 ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET)); 1491 return (fd); 1492} 1493 1494static void 1495disable_coredumps(void) 1496{ 1497 struct rlimit rl = { 0 }; 1498 1499 if (setrlimit(RLIMIT_CORE, &rl) == -1) 1500 _exit(EX_OSERR); 1501} 1502 1503/* 1504 * Replaces stdin with a file that we can actually read from, for tests where 1505 * we want a FILE * or fd that we can get data from. 1506 */ 1507static void __unused 1508replace_stdin(void) 1509{ 1510 int fd; 1511 1512 fd = new_tmpfile(); 1513 1514 (void)dup2(fd, STDIN_FILENO); 1515 if (fd != STDIN_FILENO) 1516 close(fd); 1517} 1518 1519]]) 1520 1521if tcat == "unistd" then 1522 fh:write("#define JAIL_HOSTNAME \"" .. hostname .. "\"\n") 1523 fh:write("#define JAIL_DOMAINNAME \"" .. domainname .. "\"\n") 1524 fh:write([[ 1525static void 1526dhost_jail(void) 1527{ 1528 struct iovec iov[4]; 1529 int jid; 1530 1531 iov[0].iov_base = __DECONST(char *, "host.hostname"); 1532 iov[0].iov_len = sizeof("host.hostname"); 1533 iov[1].iov_base = __DECONST(char *, JAIL_HOSTNAME); 1534 iov[1].iov_len = sizeof(JAIL_HOSTNAME); 1535 iov[2].iov_base = __DECONST(char *, "host.domainname"); 1536 iov[2].iov_len = sizeof("host.domainname"); 1537 iov[3].iov_base = __DECONST(char *, JAIL_DOMAINNAME); 1538 iov[3].iov_len = sizeof(JAIL_DOMAINNAME); 1539 1540 jid = jail_set(iov, nitems(iov), JAIL_CREATE | JAIL_ATTACH); 1541 ATF_REQUIRE_MSG(jid > 0, "Jail creation failed: %s", strerror(errno)); 1542} 1543 1544]]) 1545end 1546 1547for _, def in pairs(tests) do 1548 local func = def.func 1549 local function write_tests(heap) 1550 -- Dispositions here are relative to the buffer size prescribed 1551 -- by the test definition. 1552 local dispositions = def.dispositions or { -1, 0, 1 } 1553 1554 for _, disposition in ipairs(dispositions) do 1555 tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def) 1556 end 1557 end 1558 1559 write_tests(false) 1560 write_tests(true) 1561end 1562 1563fh:write("ATF_TP_ADD_TCS(tp)\n") 1564fh:write("{\n") 1565for idx = 1, #tests_added do 1566 fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n") 1567end 1568fh:write("\treturn (atf_no_error());\n") 1569fh:write("}\n") 1570