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