Path: blob/main/lib/libc/tests/secure/generate-fortify-tests.lua
39553 views
#!/usr/libexec/flua1--2-- SPDX-License-Identifier: BSD-2-Clause3--4-- Copyright (c) 2024, Klara, Inc.5--6-- Redistribution and use in source and binary forms, with or without7-- modification, are permitted provided that the following conditions8-- are met:9-- 1. Redistributions of source code must retain the above copyright10-- notice, this list of conditions and the following disclaimer.11-- 2. Redistributions in binary form must reproduce the above copyright12-- notice, this list of conditions and the following disclaimer in the13-- documentation and/or other materials provided with the distribution.14--15-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25-- SUCH DAMAGE.26--2728-- THEORY OF OPERATION29--30-- generate-fortify-tests.lua is intended to test fortified functions as found31-- mostly in the various headers in /usr/include/ssp. Each fortified function32-- gets three basic tests:33--34-- 1. Write just before the end of the buffer,35-- 2. Write right at the end of the buffer,36-- 3. Write just after the end of the buffer.37--38-- Each test is actually generated twice: once with a buffer on the stack, and39-- again with a buffer on the heap, to confirm that __builtin_object_size(3) can40-- deduce the buffer size in both scenarios. The tests work by setting up the41-- stack with our buffer (and some padding on either side to avoid tripping any42-- other stack or memory protection), doing any initialization as described by43-- the test definition, then calling the fortified function with the buffer as44-- outlined by the test definition.45--46-- For the 'before' and 'at' the end tests, we're ensuring that valid writes47-- that are on the verge of being invalid aren't accidentally being detected as48-- invalid.49--50-- The 'after' test is the one that actually tests the functional benefit of51-- _FORTIFY_SOURCE by violating a boundary that should trigger an abort. As52-- such, this test differs more from the other two in that it has to fork() off53-- the fortified function call so that we can monitor for a SIGABRT and54-- pass/fail the test at function end appropriately.5556-- Some tests, like the FD_*() macros, may define these differently. For57-- instance, for fd sets we're varying the index we pass and not using arbitrary58-- buffers. Other tests that don't use the length in any way may physically59-- vary the buffer size for each test case when we'd typically vary the length60-- we're requesting a write for.6162local includes = {63"sys/param.h",64"sys/jail.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}8788local tests_added = {}8990-- Configuration for tests that want the host/domainname91local hostname = "host.example.com"92local domainname = "example.com"9394-- Some of these will need to be excluded because clang sees the wrong size when95-- an array is embedded inside a struct, we'll get something that looks more96-- like __builtin_object_size(ptr, 0) than it does the correct97-- __builtin_object_size(ptr, 1) (i.e., includes the padding after). This is98-- almost certainly a bug in llvm.99local function excludes_stack_overflow(disposition, is_heap)100return (not is_heap) and disposition > 0101end102103local poll_init = [[104for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {105__stack.__buf[i].fd = -1;106}107]]108109local printf_stackvars = "\tchar srcvar[__len + 10];\n"110local printf_init = [[111memset(srcvar, 'A', sizeof(srcvar) - 1);112srcvar[sizeof(srcvar) - 1] = '\0';113]]114115local readv_stackvars = "\tstruct iovec iov[1];\n"116local readv_init = [[117iov[0].iov_base = __stack.__buf;118iov[0].iov_len = __len;119120replace_stdin();121]]122123local socket_stackvars = "\tint sock[2] = { -1, -1 };\n"124local recvfrom_sockaddr_stackvars = socket_stackvars .. [[125char data[16];126socklen_t socklen;127]]128local recvmsg_stackvars = socket_stackvars .. "\tstruct msghdr msg;\n"129local socket_init = [[130new_socket(sock);131]]132local socket_socklen_init = socket_init .. [[133socklen = __len;134]]135136local stdio_init = [[137replace_stdin();138]]139140local string_stackvars = "\tchar src[__len];\n"141local string_init = [[142memset(__stack.__buf, 0, __len);143memset(src, 'A', __len - 1);144src[__len - 1] = '\0';145]]146147local wstring_stackvars = "\twchar_t src[__len];\n"148local wstring_init = [[149wmemset(__stack.__buf, 0, __len);150wmemset(src, 'A', __len - 1);151src[__len - 1] = '\0';152]]153154-- Each test entry describes how to test a given function. We need to know how155-- to construct the buffer, we need to know the argument set we're dealing with,156-- and we need to know what we're passing to each argument. We could be passing157-- fixed values, or we could be passing the __buf under test.158--159-- definition:160-- func: name of the function under test to call161-- bufsize: size of buffer to generate, defaults to 42162-- buftype: type of buffer to generate, defaults to unsigned char[]163-- arguments: __buf, __len, or the name of a variable placed on the stack164-- exclude: a function(disposition, is_heap) that returns true if this combo165-- should be excluded.166-- stackvars: extra variables to be placed on the stack, should be a string167-- optionally formatted with tabs and newlines168-- init: extra code to inject just before the function call for initialization169-- of the buffer or any of the above-added stackvars; also a string170-- uses_len: bool-ish, necessary if arguments doesn't include either __idx or171-- or __len so that the test generator doesn't try to vary the size of the172-- buffer instead of just manipulating __idx/__len to try and induce an173-- overflow.174--175-- Most tests will just use the default bufsize/buftype, but under some176-- circumstances it's useful to use a different type (e.g., for alignment177-- requirements).178local all_tests = {179random = {180-- <sys/random.h>181{182func = "getrandom",183arguments = {184"__buf",185"__len",186"0",187},188exclude = excludes_stack_overflow,189},190},191select = {192-- <sys/select.h>193{194func = "FD_SET",195bufsize = "FD_SETSIZE",196buftype = "fd_set",197arguments = {198"__idx",199"__buf",200},201},202{203func = "FD_CLR",204bufsize = "FD_SETSIZE",205buftype = "fd_set",206arguments = {207"__idx",208"__buf",209},210},211{212func = "FD_ISSET",213bufsize = "FD_SETSIZE",214buftype = "fd_set",215arguments = {216"__idx",217"__buf",218},219},220},221socket = {222-- <sys/socket.h>223{224func = "getpeername",225buftype = "struct sockaddr",226bufsize = "sizeof(struct sockaddr)",227arguments = {228"sock[0]",229"__buf",230"&socklen",231},232exclude = excludes_stack_overflow,233stackvars = socket_stackvars .. "\tsocklen_t socklen;",234init = socket_socklen_init,235uses_len = true,236},237{238func = "getsockname",239buftype = "struct sockaddr",240bufsize = "sizeof(struct sockaddr)",241arguments = {242"sock[0]",243"__buf",244"&socklen",245},246exclude = excludes_stack_overflow,247stackvars = socket_stackvars .. "\tsocklen_t socklen;",248init = socket_socklen_init,249uses_len = true,250},251{252func = "recv",253arguments = {254"sock[0]",255"__buf",256"__len",257"0",258},259exclude = excludes_stack_overflow,260stackvars = socket_stackvars,261init = socket_init,262},263{264func = "recvfrom",265arguments = {266"sock[0]",267"__buf",268"__len",269"0",270"NULL",271"NULL",272},273exclude = excludes_stack_overflow,274stackvars = socket_stackvars,275init = socket_init,276},277{278func = "recvfrom",279variant = "sockaddr",280buftype = "struct sockaddr",281bufsize = "sizeof(struct sockaddr)",282arguments = {283"sock[0]",284"data",285"sizeof(data)",286"0",287"__buf",288"&socklen",289},290exclude = excludes_stack_overflow,291stackvars = recvfrom_sockaddr_stackvars,292init = socket_socklen_init,293uses_len = true,294},295{296func = "recvmsg",297variant = "msg_name",298buftype = "struct sockaddr",299bufsize = "sizeof(struct sockaddr)",300arguments = {301"sock[0]",302"&msg",303"0",304},305exclude = excludes_stack_overflow,306stackvars = recvmsg_stackvars,307init = [[308memset(&msg, 0, sizeof(msg));309msg.msg_name = BUF;310msg.msg_namelen = __len;311]],312uses_len = true,313},314{315func = "recvmsg",316variant = "msg_iov",317arguments = {318"sock[0]",319"&msg",320"0",321},322exclude = excludes_stack_overflow,323stackvars = recvmsg_stackvars .. "\tstruct iovec iov[2];\n",324init = [[325memset(&msg, 0, sizeof(msg));326memset(&iov[0], 0, sizeof(iov));327328/*329* We position the buffer second just so that we can confirm that the330* fortification bits are traversing the iovec correctly.331*/332iov[1].iov_base = BUF;333iov[1].iov_len = __len;334335msg.msg_iov = &iov[0];336msg.msg_iovlen = nitems(iov);337]],338uses_len = true,339},340{341func = "recvmsg",342variant = "msg_control",343bufsize = "CMSG_SPACE(sizeof(int))",344arguments = {345"sock[0]",346"&msg",347"0",348},349exclude = excludes_stack_overflow,350stackvars = recvmsg_stackvars,351init = [[352memset(&msg, 0, sizeof(msg));353354msg.msg_control = BUF;355msg.msg_controllen = __len;356]],357uses_len = true,358},359{360func = "recvmmsg",361variant = "msgvec",362buftype = "struct mmsghdr[]",363bufsize = "2",364arguments = {365"sock[0]",366"__buf",367"__len",368"0",369"NULL",370},371stackvars = socket_stackvars,372},373{374-- We'll assume that recvmsg is covering msghdr375-- validation thoroughly enough, we'll just try tossing376-- an error in the second element of a msgvec to try and377-- make sure that each one is being validated.378func = "recvmmsg",379variant = "msghdr",380arguments = {381"sock[0]",382"&msgvec[0]",383"nitems(msgvec)",384"0",385"NULL",386},387exclude = excludes_stack_overflow,388stackvars = socket_stackvars .. "\tstruct mmsghdr msgvec[2];\n",389init = [[390memset(&msgvec[0], 0, sizeof(msgvec));391392/*393* Same as above, make sure fortification isn't ignoring n > 1 elements394* of the msgvec.395*/396msgvec[1].msg_hdr.msg_control = BUF;397msgvec[1].msg_hdr.msg_controllen = __len;398]],399uses_len = true,400},401},402uio = {403-- <sys/uio.h>404{405func = "readv",406buftype = "struct iovec[]",407bufsize = 2,408arguments = {409"STDIN_FILENO",410"__buf",411"__len",412},413},414{415func = "readv",416variant = "iov",417arguments = {418"STDIN_FILENO",419"iov",420"nitems(iov)",421},422exclude = excludes_stack_overflow,423stackvars = readv_stackvars,424init = readv_init,425uses_len = true,426},427{428func = "preadv",429buftype = "struct iovec[]",430bufsize = 2,431arguments = {432"STDIN_FILENO",433"__buf",434"__len",435"0",436},437},438{439func = "preadv",440variant = "iov",441arguments = {442"STDIN_FILENO",443"iov",444"nitems(iov)",445"0",446},447exclude = excludes_stack_overflow,448stackvars = readv_stackvars,449init = readv_init,450uses_len = true,451},452},453poll = {454-- <poll.h>455{456func = "poll",457bufsize = "4",458buftype = "struct pollfd[]",459arguments = {460"__buf",461"__len",462"0",463},464init = poll_init,465},466{467func = "ppoll",468bufsize = "4",469buftype = "struct pollfd[]",470arguments = {471"__buf",472"__len",473"&tv",474"NULL",475},476stackvars = "\tstruct timespec tv = { 0 };\n",477init = poll_init,478},479},480signal = {481-- <signal.h>482{483func = "sig2str",484bufsize = "SIG2STR_MAX",485arguments = {486"1",487"__buf",488},489exclude = excludes_stack_overflow,490},491},492stdio = {493-- <stdio.h>494{495func = "ctermid",496bufsize = "L_ctermid",497arguments = {498"__buf",499},500exclude = excludes_stack_overflow,501},502{503func = "ctermid_r",504bufsize = "L_ctermid",505arguments = {506"__buf",507},508exclude = excludes_stack_overflow,509},510{511func = "fread",512arguments = {513"__buf",514"__len",515"1",516"stdin",517},518exclude = excludes_stack_overflow,519init = stdio_init,520},521{522func = "fread_unlocked",523arguments = {524"__buf",525"__len",526"1",527"stdin",528},529exclude = excludes_stack_overflow,530init = stdio_init,531},532{533func = "gets_s",534arguments = {535"__buf",536"__len",537},538exclude = excludes_stack_overflow,539init = stdio_init,540},541{542func = "sprintf",543arguments = {544"__buf",545"\"%.*s\"",546"(int)__len - 1", -- - 1 for NUL terminator547"srcvar",548},549exclude = excludes_stack_overflow,550stackvars = printf_stackvars,551init = printf_init,552},553{554func = "snprintf",555arguments = {556"__buf",557"__len",558"\"%.*s\"",559"(int)__len - 1", -- - 1 for NUL terminator560"srcvar",561},562exclude = excludes_stack_overflow,563stackvars = printf_stackvars,564init = printf_init,565},566{567func = "tmpnam",568bufsize = "L_tmpnam",569arguments = {570"__buf",571},572exclude = excludes_stack_overflow,573},574{575func = "fgets",576arguments = {577"__buf",578"__len",579"fp",580},581exclude = excludes_stack_overflow,582stackvars = "\tFILE *fp;\n",583init = [[584fp = new_fp(__len);585]],586},587},588stdlib = {589-- <stdlib.h>590{591func = "arc4random_buf",592arguments = {593"__buf",594"__len",595},596exclude = excludes_stack_overflow,597},598{599func = "getenv_r",600arguments = {601"\"PATH\"",602"__buf",603"__len",604},605exclude = excludes_stack_overflow,606},607{608func = "realpath",609bufsize = "PATH_MAX",610arguments = {611"\".\"",612"__buf",613},614exclude = excludes_stack_overflow,615},616},617string = {618-- <string.h>619{620func = "memcpy",621arguments = {622"__buf",623"src",624"__len",625},626exclude = excludes_stack_overflow,627stackvars = "\tchar src[__len + 10];\n",628},629{630func = "mempcpy",631arguments = {632"__buf",633"src",634"__len",635},636exclude = excludes_stack_overflow,637stackvars = "\tchar src[__len + 10];\n",638},639{640func = "memmove",641arguments = {642"__buf",643"src",644"__len",645},646exclude = excludes_stack_overflow,647stackvars = "\tchar src[__len + 10];\n",648},649{650func = "memset",651arguments = {652"__buf",653"0",654"__len",655},656exclude = excludes_stack_overflow,657},658{659func = "memset_explicit",660arguments = {661"__buf",662"0",663"__len",664},665exclude = excludes_stack_overflow,666},667{668func = "stpcpy",669arguments = {670"__buf",671"src",672},673exclude = excludes_stack_overflow,674stackvars = string_stackvars,675init = string_init,676uses_len = true,677},678{679func = "stpncpy",680arguments = {681"__buf",682"src",683"__len",684},685exclude = excludes_stack_overflow,686stackvars = string_stackvars,687init = string_init,688},689{690func = "strcat",691arguments = {692"__buf",693"src",694},695exclude = excludes_stack_overflow,696stackvars = string_stackvars,697init = string_init,698uses_len = true,699},700{701func = "strlcat",702arguments = {703"__buf",704"src",705"__len",706},707exclude = excludes_stack_overflow,708stackvars = string_stackvars,709init = string_init,710},711{712func = "strncat",713arguments = {714"__buf",715"src",716"__len",717},718exclude = excludes_stack_overflow,719stackvars = string_stackvars,720init = string_init,721},722{723func = "strcpy",724arguments = {725"__buf",726"src",727},728exclude = excludes_stack_overflow,729stackvars = string_stackvars,730init = string_init,731uses_len = true,732},733{734func = "strlcpy",735arguments = {736"__buf",737"src",738"__len",739},740exclude = excludes_stack_overflow,741stackvars = string_stackvars,742init = string_init,743},744{745func = "strncpy",746arguments = {747"__buf",748"src",749"__len",750},751exclude = excludes_stack_overflow,752stackvars = string_stackvars,753init = string_init,754},755},756strings = {757-- <strings.h>758{759func = "bcopy",760arguments = {761"src",762"__buf",763"__len",764},765exclude = excludes_stack_overflow,766stackvars = "\tchar src[__len + 10];\n",767},768{769func = "bzero",770arguments = {771"__buf",772"__len",773},774exclude = excludes_stack_overflow,775},776{777func = "explicit_bzero",778arguments = {779"__buf",780"__len",781},782exclude = excludes_stack_overflow,783},784},785unistd = {786-- <unistd.h>787{788func = "getcwd",789bufsize = "8",790arguments = {791"__buf",792"__len",793},794exclude = excludes_stack_overflow,795},796{797func = "getgrouplist",798bufsize = "4",799buftype = "gid_t[]",800arguments = {801"\"root\"",802"0",803"__buf",804"&intlen",805},806exclude = excludes_stack_overflow,807stackvars = "\tint intlen = (int)__len;\n",808uses_len = true,809},810{811func = "getgroups",812bufsize = "4",813buftype = "gid_t[]",814arguments = {815"__len",816"__buf",817},818exclude = excludes_stack_overflow,819},820{821func = "getloginclass",822arguments = {823"__buf",824"__len",825},826exclude = excludes_stack_overflow,827},828{829func = "pread",830bufsize = "41",831arguments = {832"fd",833"__buf",834"__len",835"0",836},837exclude = excludes_stack_overflow,838stackvars = "\tint fd;\n",839init = [[840fd = new_tmpfile(); /* Cannot fail */841]],842},843{844func = "read",845bufsize = "41",846arguments = {847"fd",848"__buf",849"__len",850},851exclude = excludes_stack_overflow,852stackvars = "\tint fd;\n",853init = [[854fd = new_tmpfile(); /* Cannot fail */855]],856},857{858func = "readlink",859arguments = {860"path",861"__buf",862"__len",863},864exclude = excludes_stack_overflow,865stackvars = "\tconst char *path;\n",866init = [[867path = new_symlink(__len); /* Cannot fail */868]],869},870{871func = "readlinkat",872arguments = {873"AT_FDCWD",874"path",875"__buf",876"__len",877},878exclude = excludes_stack_overflow,879stackvars = "\tconst char *path;\n",880init = [[881path = new_symlink(__len); /* Cannot fail */882]],883},884{885func = "getdomainname",886bufsize = #domainname + 1,887arguments = {888"__buf",889"__len",890},891need_root = true,892exclude = excludes_stack_overflow,893early_init = " dhost_jail();",894},895{896func = "getentropy",897arguments = {898"__buf",899"__len",900},901exclude = excludes_stack_overflow,902},903{904func = "gethostname",905bufsize = #hostname + 1,906arguments = {907"__buf",908"__len",909},910need_root = true,911exclude = excludes_stack_overflow,912early_init = " dhost_jail();",913},914{915func = "getlogin_r",916bufsize = "MAXLOGNAME + 1",917arguments = {918"__buf",919"__len",920},921exclude = excludes_stack_overflow,922},923{924func = "ttyname_r",925arguments = {926"fd",927"__buf",928"__len",929},930exclude = excludes_stack_overflow,931stackvars = "\tint fd;\n",932early_init = [[933fd = STDIN_FILENO;934if (!isatty(fd))935atf_tc_skip("stdin is not an fd");936]]937},938},939wchar = {940-- <wchar.h>941{942func = "wmemcpy",943buftype = "wchar_t[]",944arguments = {945"__buf",946"src",947"__len",948},949exclude = excludes_stack_overflow,950stackvars = "\twchar_t src[__len + 10];\n",951},952{953func = "wmempcpy",954buftype = "wchar_t[]",955arguments = {956"__buf",957"src",958"__len",959},960exclude = excludes_stack_overflow,961stackvars = "\twchar_t src[__len + 10];\n",962},963{964func = "wmemmove",965buftype = "wchar_t[]",966arguments = {967"__buf",968"src",969"__len",970},971exclude = excludes_stack_overflow,972stackvars = "\twchar_t src[__len + 10];\n",973},974{975func = "wmemset",976buftype = "wchar_t[]",977arguments = {978"__buf",979"L'0'",980"__len",981},982exclude = excludes_stack_overflow,983},984{985func = "wcpcpy",986buftype = "wchar_t[]",987arguments = {988"__buf",989"src",990},991exclude = excludes_stack_overflow,992stackvars = wstring_stackvars,993init = wstring_init,994uses_len = true,995},996{997func = "wcpncpy",998buftype = "wchar_t[]",999arguments = {1000"__buf",1001"src",1002"__len",1003},1004exclude = excludes_stack_overflow,1005stackvars = wstring_stackvars,1006init = wstring_init,1007},1008{1009func = "wcscat",1010buftype = "wchar_t[]",1011arguments = {1012"__buf",1013"src",1014},1015exclude = excludes_stack_overflow,1016stackvars = wstring_stackvars,1017init = wstring_init,1018uses_len = true,1019},1020{1021func = "wcslcat",1022buftype = "wchar_t[]",1023arguments = {1024"__buf",1025"src",1026"__len",1027},1028exclude = excludes_stack_overflow,1029stackvars = wstring_stackvars,1030init = wstring_init,1031},1032{1033func = "wcsncat",1034buftype = "wchar_t[]",1035arguments = {1036"__buf",1037"src",1038"__len",1039},1040exclude = excludes_stack_overflow,1041stackvars = wstring_stackvars,1042init = wstring_init,1043},1044{1045func = "wcscpy",1046buftype = "wchar_t[]",1047arguments = {1048"__buf",1049"src",1050},1051exclude = excludes_stack_overflow,1052stackvars = wstring_stackvars,1053init = wstring_init,1054uses_len = true,1055},1056{1057func = "wcslcpy",1058buftype = "wchar_t[]",1059arguments = {1060"__buf",1061"src",1062"__len",1063},1064exclude = excludes_stack_overflow,1065stackvars = wstring_stackvars,1066init = wstring_init,1067},1068{1069func = "wcsncpy",1070buftype = "wchar_t[]",1071arguments = {1072"__buf",1073"src",1074"__len",1075},1076exclude = excludes_stack_overflow,1077stackvars = wstring_stackvars,1078init = wstring_init,1079},1080},1081}10821083local function write_test_boilerplate(fh, name, body, def)1084fh:write("ATF_TC(" .. name .. ");\n")1085fh:write("ATF_TC_HEAD(" .. name .. ", tc)\n")1086fh:write("{\n")1087if def.need_root then1088fh:write(" atf_tc_set_md_var(tc, \"require.user\", \"root\");\n")1089end1090fh:write("}\n")10911092fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")1093fh:write("{\n" .. body .. "\n}\n\n")1094return name1095end10961097local function generate_test_name(func, variant, disposition, heap)1098local basename = func1099if variant then1100basename = basename .. "_" .. variant1101end1102if heap then1103basename = basename .. "_heap"1104end1105if disposition < 0 then1106return basename .. "_before_end"1107elseif disposition == 0 then1108return basename .. "_end"1109else1110return basename .. "_after_end"1111end1112end11131114local function array_type(buftype)1115if not buftype:match("%[%]") then1116return nil1117end11181119return buftype:gsub("%[%]", "")1120end11211122local function configurable(def, idx)1123local cfgitem = def[idx]11241125if not cfgitem then1126return nil1127end11281129if type(cfgitem) == "function" then1130return cfgitem()1131end11321133return cfgitem1134end11351136local function generate_stackframe(buftype, bufsize, disposition, heap, def)1137local function len_offset(inverted)1138-- Tests that don't use __len in their arguments may use an1139-- inverted sense because we can't just specify a length that1140-- would induce an access just after the end. Instead, we have1141-- to manipulate the buffer size to be too short so that the1142-- function under test would write one too many.1143if disposition < 0 then1144return ((inverted and " + ") or " - ") .. "1"1145elseif disposition == 0 then1146return ""1147else1148return ((inverted and " - ") or " + ") .. "1"1149end1150end11511152local function test_uses_len()1153if def.uses_len then1154return true1155end11561157for _, arg in ipairs(def.arguments) do1158if arg:match("__len") or arg:match("__idx") then1159return true1160end1161end11621163return false1164end116511661167-- This is perhaps a little convoluted, but we toss the buffer into a1168-- struct on the stack to guarantee that we have at least one valid1169-- byte on either side of the buffer -- a measure to make sure that1170-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,1171-- rather than some other stack or memory protection.1172local vars = "\tstruct {\n"1173vars = vars .. "\t\tuint8_t padding_l;\n"11741175local uses_len = test_uses_len()1176local bufsize_offset = len_offset(not uses_len)1177local buftype_elem = array_type(buftype)1178local size_expr = bufsize11791180if not uses_len then1181-- If the length isn't in use, we have to vary the buffer size1182-- since the fortified function likely has some internal size1183-- constraint that it's supposed to be checking.1184size_expr = size_expr .. bufsize_offset1185end11861187if not heap and buftype_elem then1188-- Array type: size goes after identifier1189vars = vars .. "\t\t" .. buftype_elem ..1190" __buf[" .. size_expr .. "];\n"1191else1192local basic_type = buftype_elem or buftype11931194-- Heap tests obviously just put a pointer on the stack that1195-- points to our new allocation, but we leave it in the padded1196-- struct just to simplify our generator.1197if heap then1198basic_type = basic_type .. " *"1199end1200vars = vars .. "\t\t" .. basic_type .. " __buf;\n"1201end12021203-- padding_r is our just-past-the-end padding that we use to make sure1204-- that there's a valid portion after the buffer that isn't being1205-- included in our function calls. If we didn't have it, then we'd have1206-- a hard time feeling confident that an abort on the just-after tests1207-- isn't maybe from some other memory or stack protection.1208vars = vars .. "\t\tuint8_t padding_r;\n"1209vars = vars .. "\t} __stack;\n"12101211-- Not all tests will use __bufsz, but some do for, e.g., clearing1212-- memory..1213vars = vars .. "\tconst size_t __bufsz __unused = "1214if heap then1215local scalar = 11216if buftype_elem then1217scalar = size_expr1218end12191220vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"1221else1222vars = vars .. "sizeof(__stack.__buf);\n"1223end12241225vars = vars .. "\tconst size_t __len = " .. bufsize ..1226bufsize_offset .. ";\n"1227vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"12281229-- For overflow testing, we need to fork() because we're expecting the1230-- test to ultimately abort()/_exit(). Then we can collect the exit1231-- status and report appropriately.1232if disposition > 0 then1233vars = vars .. "\tpid_t __child;\n"1234vars = vars .. "\tint __status;\n"1235end12361237-- Any other stackvars defined by the test get placed after everything1238-- else.1239vars = vars .. (configurable(def, "stackvars") or "")12401241return vars1242end12431244local function write_test(fh, func, disposition, heap, def)1245local testname = generate_test_name(func, def.variant, disposition, heap)1246local buftype = def.buftype or "unsigned char[]"1247local bufsize = def.bufsize or 421248local body = ""12491250if def.exclude and def.exclude(disposition, heap) then1251return1252end12531254local function need_addr()1255return not (buftype:match("%[%]") or buftype:match("%*"))1256end12571258if heap then1259body = body .. "#define BUF __stack.__buf\n"1260else1261body = body .. "#define BUF &__stack.__buf\n"1262end12631264-- Setup the buffer1265body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..1266"\n"12671268-- Any early initialization goes before we would fork for the just-after1269-- tests, because they may want to skip the test based on some criteria1270-- and we can't propagate that up very easily once we're forked.1271local early_init = configurable(def, "early_init")1272body = body .. (early_init or "")1273if early_init then1274body = body .. "\n"1275end12761277-- Fork off, iff we're testing some access past the end of the buffer.1278if disposition > 0 then1279body = body .. [[1280__child = fork();1281ATF_REQUIRE(__child >= 0);1282if (__child > 0)1283goto monitor;12841285/* Child */1286disable_coredumps();1287]]1288end12891290local bufvar = "__stack.__buf"1291if heap then1292-- Buffer needs to be initialized because it's a heap allocation.1293body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"1294end12951296-- Non-early init happens just after the fork in the child, not the1297-- monitor. This is used to setup any other buffers we may need, for1298-- instance.1299local extra_init = configurable(def, "init")1300body = body .. (extra_init or "")13011302if heap or extra_init then1303body = body .. "\n"1304end13051306-- Setup the function call with arguments as described in the test1307-- definition.1308body = body .. "\t" .. func .. "("13091310for idx, arg in ipairs(def.arguments) do1311if idx > 1 then1312body = body .. ", "1313end13141315if arg == "__buf" then1316if not heap and need_addr() then1317body = body .. "&"1318end13191320body = body .. bufvar1321else1322local argname = arg13231324if def.value_of then1325argname = argname or def.value_of(arg)1326end13271328body = body .. argname1329end1330end13311332body = body .. ");\n"13331334-- Monitor stuff follows, for OOB access.1335if disposition <= 0 then1336goto skip1337end13381339body = body .. [[1340_exit(EX_SOFTWARE); /* Should have aborted. */13411342monitor:1343while (waitpid(__child, &__status, 0) != __child) {1344ATF_REQUIRE_EQ(EINTR, errno);1345}13461347if (!WIFSIGNALED(__status)) {1348switch (WEXITSTATUS(__status)) {1349case EX_SOFTWARE:1350atf_tc_fail("FORTIFY_SOURCE failed to abort");1351break;1352case EX_OSERR:1353atf_tc_fail("setrlimit(2) failed");1354break;1355default:1356atf_tc_fail("child exited with status %d",1357WEXITSTATUS(__status));1358}1359} else {1360ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));1361}1362]]13631364::skip::1365body = body .. "#undef BUF\n"1366return write_test_boilerplate(fh, testname, body, def)1367end13681369-- main()1370local tests1371local tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")1372for k, defs in pairs(all_tests) do1373if k == tcat then1374tests = defs1375break1376end1377end13781379assert(tests, "category " .. tcat .. " not found")13801381local fh = io.stdout1382fh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..1383tcat .. "\"` */\n\n")1384fh:write("#define _FORTIFY_SOURCE 2\n")1385fh:write("#define TMPFILE_SIZE (1024 * 32)\n")13861387fh:write("\n")1388for _, inc in ipairs(includes) do1389fh:write("#include <" .. inc .. ">\n")1390end13911392fh:write([[13931394static FILE * __unused1395new_fp(size_t __len)1396{1397static char fpbuf[LINE_MAX];1398FILE *fp;13991400ATF_REQUIRE(__len <= sizeof(fpbuf));14011402memset(fpbuf, 'A', sizeof(fpbuf) - 1);1403fpbuf[sizeof(fpbuf) - 1] = '\0';14041405fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");1406ATF_REQUIRE(fp != NULL);14071408return (fp);1409}14101411/*1412* Create a new symlink to use for readlink(2) style tests, we'll just use a1413* random target name to have something interesting to look at.1414*/1415static const char * __unused1416new_symlink(size_t __len)1417{1418static const char linkname[] = "link";1419char target[MAXNAMLEN];1420int error;14211422ATF_REQUIRE(__len <= sizeof(target));14231424arc4random_buf(target, sizeof(target));14251426error = unlink(linkname);1427ATF_REQUIRE(error == 0 || errno == ENOENT);14281429error = symlink(target, linkname);1430ATF_REQUIRE(error == 0);14311432return (linkname);1433}14341435/*1436* For our purposes, first descriptor will be the reader; we'll send both1437* raw data and a control message over it so that the result can be used for1438* any of our recv*() tests.1439*/1440static void __unused1441new_socket(int sock[2])1442{1443unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };1444static char sockbuf[256];1445ssize_t rv;1446size_t total = 0;1447struct msghdr hdr = { 0 };1448struct cmsghdr *cmsg;1449int error, fd;14501451error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);1452ATF_REQUIRE(error == 0);14531454while (total != sizeof(sockbuf)) {1455rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);14561457ATF_REQUIRE_MSG(rv > 0,1458"expected bytes sent, got %zd with %zu left (size %zu, total %zu)",1459rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);1460ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),1461"%zd exceeds total %zu", rv, sizeof(sockbuf));1462total += rv;1463}14641465hdr.msg_control = ctrl;1466hdr.msg_controllen = sizeof(ctrl);14671468cmsg = CMSG_FIRSTHDR(&hdr);1469cmsg->cmsg_level = SOL_SOCKET;1470cmsg->cmsg_type = SCM_RIGHTS;1471cmsg->cmsg_len = CMSG_LEN(sizeof(fd));1472fd = STDIN_FILENO;1473memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));14741475error = sendmsg(sock[1], &hdr, 0);1476ATF_REQUIRE(error != -1);1477}14781479/*1480* Constructs a tmpfile that we can use for testing read(2) and friends.1481*/1482static int __unused1483new_tmpfile(void)1484{1485char buf[1024];1486ssize_t rv;1487size_t written;1488int fd;14891490fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);1491ATF_REQUIRE(fd >= 0);14921493written = 0;1494while (written < TMPFILE_SIZE) {1495rv = write(fd, buf, sizeof(buf));1496ATF_REQUIRE(rv > 0);14971498written += rv;1499}15001501ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));1502return (fd);1503}15041505static void1506disable_coredumps(void)1507{1508struct rlimit rl = { 0 };15091510if (setrlimit(RLIMIT_CORE, &rl) == -1)1511_exit(EX_OSERR);1512}15131514/*1515* Replaces stdin with a file that we can actually read from, for tests where1516* we want a FILE * or fd that we can get data from.1517*/1518static void __unused1519replace_stdin(void)1520{1521int fd;15221523fd = new_tmpfile();15241525(void)dup2(fd, STDIN_FILENO);1526if (fd != STDIN_FILENO)1527close(fd);1528}15291530]])15311532if tcat == "unistd" then1533fh:write("#define JAIL_HOSTNAME \"" .. hostname .. "\"\n")1534fh:write("#define JAIL_DOMAINNAME \"" .. domainname .. "\"\n")1535fh:write([[1536static void1537dhost_jail(void)1538{1539struct iovec iov[4];1540int jid;15411542iov[0].iov_base = __DECONST(char *, "host.hostname");1543iov[0].iov_len = sizeof("host.hostname");1544iov[1].iov_base = __DECONST(char *, JAIL_HOSTNAME);1545iov[1].iov_len = sizeof(JAIL_HOSTNAME);1546iov[2].iov_base = __DECONST(char *, "host.domainname");1547iov[2].iov_len = sizeof("host.domainname");1548iov[3].iov_base = __DECONST(char *, JAIL_DOMAINNAME);1549iov[3].iov_len = sizeof(JAIL_DOMAINNAME);15501551jid = jail_set(iov, nitems(iov), JAIL_CREATE | JAIL_ATTACH);1552ATF_REQUIRE_MSG(jid > 0, "Jail creation failed: %s", strerror(errno));1553}15541555]])1556end15571558for _, def in pairs(tests) do1559local func = def.func1560local function write_tests(heap)1561-- Dispositions here are relative to the buffer size prescribed1562-- by the test definition.1563local dispositions = def.dispositions or { -1, 0, 1 }15641565for _, disposition in ipairs(dispositions) do1566tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)1567end1568end15691570write_tests(false)1571write_tests(true)1572end15731574fh:write("ATF_TP_ADD_TCS(tp)\n")1575fh:write("{\n")1576for idx = 1, #tests_added do1577fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")1578end1579fh:write("\treturn (atf_no_error());\n")1580fh:write("}\n")158115821583