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