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