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