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