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