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