xref: /freebsd/lib/libc/tests/secure/generate-fortify-tests.lua (revision 2aba0eea3ffffce74f9d8df20e0aaf49ea6d76c3)
1020d003cSKyle Evans#!/usr/libexec/flua
2020d003cSKyle Evans--
3020d003cSKyle Evans-- SPDX-License-Identifier: BSD-2-Clause
4020d003cSKyle Evans--
5020d003cSKyle Evans-- Copyright (c) 2024, Klara, Inc.
6020d003cSKyle Evans--
7020d003cSKyle Evans-- Redistribution and use in source and binary forms, with or without
8020d003cSKyle Evans-- modification, are permitted provided that the following conditions
9020d003cSKyle Evans-- are met:
10020d003cSKyle Evans-- 1. Redistributions of source code must retain the above copyright
11020d003cSKyle Evans--    notice, this list of conditions and the following disclaimer.
12020d003cSKyle Evans-- 2. Redistributions in binary form must reproduce the above copyright
13020d003cSKyle Evans--    notice, this list of conditions and the following disclaimer in the
14020d003cSKyle Evans--    documentation and/or other materials provided with the distribution.
15020d003cSKyle Evans--
16020d003cSKyle Evans-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17020d003cSKyle Evans-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18020d003cSKyle Evans-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19020d003cSKyle Evans-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20020d003cSKyle Evans-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21020d003cSKyle Evans-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22020d003cSKyle Evans-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23020d003cSKyle Evans-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24020d003cSKyle Evans-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25020d003cSKyle Evans-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26020d003cSKyle Evans-- SUCH DAMAGE.
27020d003cSKyle Evans--
28020d003cSKyle Evans
29020d003cSKyle Evans-- THEORY OF OPERATION
30020d003cSKyle Evans--
31020d003cSKyle Evans-- generate-fortify-tests.lua is intended to test fortified functions as found
32020d003cSKyle Evans-- mostly in the various headers in /usr/include/ssp.  Each fortified function
33020d003cSKyle Evans-- gets three basic tests:
34020d003cSKyle Evans--
35020d003cSKyle Evans--   1. Write just before the end of the buffer,
36020d003cSKyle Evans--   2. Write right at the end of the buffer,
37020d003cSKyle Evans--   3. Write just after the end of the buffer.
38020d003cSKyle Evans--
39020d003cSKyle Evans-- Each test is actually generated twice: once with a buffer on the stack, and
40020d003cSKyle Evans-- again with a buffer on the heap, to confirm that __builtin_object_size(3) can
41020d003cSKyle Evans-- deduce the buffer size in both scenarios.  The tests work by setting up the
42020d003cSKyle Evans-- stack with our buffer (and some padding on either side to avoid tripping any
43020d003cSKyle Evans-- other stack or memory protection), doing any initialization as described by
44020d003cSKyle Evans-- the test definition, then calling the fortified function with the buffer as
45020d003cSKyle Evans-- outlined by the test definition.
46020d003cSKyle Evans--
47020d003cSKyle Evans-- For the 'before' and 'at' the end tests, we're ensuring that valid writes
48020d003cSKyle Evans-- that are on the verge of being invalid aren't accidentally being detected as
49020d003cSKyle Evans-- invalid.
50020d003cSKyle Evans--
51020d003cSKyle Evans-- The 'after' test is the one that actually tests the functional benefit of
52020d003cSKyle Evans-- _FORTIFY_SOURCE by violating a boundary that should trigger an abort.  As
53020d003cSKyle Evans-- such, this test differs more from the other two in that it has to fork() off
54020d003cSKyle Evans-- the fortified function call so that we can monitor for a SIGABRT and
55020d003cSKyle Evans-- pass/fail the test at function end appropriately.
56020d003cSKyle Evans
57020d003cSKyle Evans-- Some tests, like the FD_*() macros, may define these differently.  For
58020d003cSKyle Evans-- instance, for fd sets we're varying the index we pass and not using arbitrary
59020d003cSKyle Evans-- buffers.  Other tests that don't use the length in any way may physically
60020d003cSKyle Evans-- vary the buffer size for each test case when we'd typically vary the length
61020d003cSKyle Evans-- we're requesting a write for.
62020d003cSKyle Evans
63020d003cSKyle Evanslocal includes = {
64020d003cSKyle Evans	"sys/param.h",
65062d9380SKyle Evans	"sys/random.h",
66020d003cSKyle Evans	"sys/resource.h",
67*2aba0eeaSKyle Evans	"sys/select.h",
68020d003cSKyle Evans	"sys/time.h",
691ace24b3SKyle Evans	"sys/uio.h",
70020d003cSKyle Evans	"sys/wait.h",
71020d003cSKyle Evans	"dirent.h",
72020d003cSKyle Evans	"errno.h",
73020d003cSKyle Evans	"fcntl.h",
74020d003cSKyle Evans	"limits.h",
7588276dfbSKyle Evans	"poll.h",
76020d003cSKyle Evans	"signal.h",
77020d003cSKyle Evans	"stdio.h",
78020d003cSKyle Evans	"stdlib.h",
79020d003cSKyle Evans	"string.h",
80020d003cSKyle Evans	"strings.h",
81020d003cSKyle Evans	"sysexits.h",
82020d003cSKyle Evans	"unistd.h",
83b53d7aa8SKyle Evans	"wchar.h",
84020d003cSKyle Evans	"atf-c.h",
85020d003cSKyle Evans}
86020d003cSKyle Evans
87020d003cSKyle Evanslocal tests_added = {}
88020d003cSKyle Evans
89020d003cSKyle Evans-- Some of these will need to be excluded because clang sees the wrong size when
90020d003cSKyle Evans-- an array is embedded inside a struct, we'll get something that looks more
91020d003cSKyle Evans-- like __builtin_object_size(ptr, 0) than it does the correct
92020d003cSKyle Evans-- __builtin_object_size(ptr, 1) (i.e., includes the padding after).  This is
93020d003cSKyle Evans-- almost certainly a bug in llvm.
94020d003cSKyle Evanslocal function excludes_stack_overflow(disposition, is_heap)
95020d003cSKyle Evans	return (not is_heap) and disposition > 0
96020d003cSKyle Evansend
97020d003cSKyle Evans
9888276dfbSKyle Evanslocal poll_init = [[
9988276dfbSKyle Evans	for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
10088276dfbSKyle Evans		__stack.__buf[i].fd = -1;
10188276dfbSKyle Evans	}
10288276dfbSKyle Evans]]
10388276dfbSKyle Evans
104020d003cSKyle Evanslocal printf_stackvars = "\tchar srcvar[__len + 10];\n"
105020d003cSKyle Evanslocal printf_init = [[
106020d003cSKyle Evans	memset(srcvar, 'A', sizeof(srcvar) - 1);
107020d003cSKyle Evans	srcvar[sizeof(srcvar) - 1] = '\0';
108020d003cSKyle Evans]]
109020d003cSKyle Evans
1101ace24b3SKyle Evanslocal readv_stackvars = "\tstruct iovec iov[1];\n"
1111ace24b3SKyle Evanslocal readv_init = [[
1121ace24b3SKyle Evans	iov[0].iov_base = __stack.__buf;
1131ace24b3SKyle Evans	iov[0].iov_len = __len;
1141ace24b3SKyle Evans
1151ace24b3SKyle Evans	replace_stdin();
1161ace24b3SKyle Evans]]
1171ace24b3SKyle Evans
118cf8e5289SKyle Evanslocal stdio_init = [[
119cf8e5289SKyle Evans	replace_stdin();
120cf8e5289SKyle Evans]]
121cf8e5289SKyle Evans
122020d003cSKyle Evanslocal string_stackvars = "\tchar src[__len];\n"
123020d003cSKyle Evanslocal string_init = [[
124020d003cSKyle Evans	memset(__stack.__buf, 0, __len);
125020d003cSKyle Evans	memset(src, 'A', __len - 1);
126020d003cSKyle Evans	src[__len - 1] = '\0';
127020d003cSKyle Evans]]
128020d003cSKyle Evans
129b53d7aa8SKyle Evanslocal wstring_stackvars = "\twchar_t src[__len];\n"
130b53d7aa8SKyle Evanslocal wstring_init = [[
131b53d7aa8SKyle Evans	wmemset(__stack.__buf, 0, __len);
132b53d7aa8SKyle Evans	wmemset(src, 'A', __len - 1);
133b53d7aa8SKyle Evans	src[__len - 1] = '\0';
134b53d7aa8SKyle Evans]]
135b53d7aa8SKyle Evans
136020d003cSKyle Evans-- Each test entry describes how to test a given function.  We need to know how
137020d003cSKyle Evans-- to construct the buffer, we need to know the argument set we're dealing with,
138020d003cSKyle Evans-- and we need to know what we're passing to each argument.  We could be passing
139020d003cSKyle Evans-- fixed values, or we could be passing the __buf under test.
140020d003cSKyle Evans--
141020d003cSKyle Evans-- definition:
142020d003cSKyle Evans--   func: name of the function under test to call
143020d003cSKyle Evans--   bufsize: size of buffer to generate, defaults to 42
144020d003cSKyle Evans--   buftype: type of buffer to generate, defaults to unsigned char[]
145020d003cSKyle Evans--   arguments: __buf, __len, or the name of a variable placed on the stack
146020d003cSKyle Evans--   exclude: a function(disposition, is_heap) that returns true if this combo
147020d003cSKyle Evans--     should be excluded.
148020d003cSKyle Evans--   stackvars: extra variables to be placed on the stack, should be a string
149020d003cSKyle Evans--     optionally formatted with tabs and newlines
150020d003cSKyle Evans--   init: extra code to inject just before the function call for initialization
151020d003cSKyle Evans--     of the buffer or any of the above-added stackvars; also a string
152020d003cSKyle Evans--   uses_len: bool-ish, necessary if arguments doesn't include either __idx or
153020d003cSKyle Evans--     or __len so that the test generator doesn't try to vary the size of the
154020d003cSKyle Evans--     buffer instead of just manipulating __idx/__len to try and induce an
155020d003cSKyle Evans--     overflow.
156020d003cSKyle Evans--
157020d003cSKyle Evans-- Most tests will just use the default bufsize/buftype, but under some
158020d003cSKyle Evans-- circumstances it's useful to use a different type (e.g., for alignment
159020d003cSKyle Evans-- requirements).
160020d003cSKyle Evanslocal all_tests = {
161062d9380SKyle Evans	random = {
162062d9380SKyle Evans		-- <sys/random.h>
163062d9380SKyle Evans		{
164062d9380SKyle Evans			func = "getrandom",
165062d9380SKyle Evans			arguments = {
166062d9380SKyle Evans				"__buf",
167062d9380SKyle Evans				"__len",
168062d9380SKyle Evans				"0",
169062d9380SKyle Evans			},
170062d9380SKyle Evans			exclude = excludes_stack_overflow,
171062d9380SKyle Evans		},
172062d9380SKyle Evans	},
173*2aba0eeaSKyle Evans	select = {
174*2aba0eeaSKyle Evans		-- <sys/select.h>
175*2aba0eeaSKyle Evans		{
176*2aba0eeaSKyle Evans			func = "FD_SET",
177*2aba0eeaSKyle Evans			bufsize = "FD_SETSIZE",
178*2aba0eeaSKyle Evans			buftype = "fd_set",
179*2aba0eeaSKyle Evans			arguments = {
180*2aba0eeaSKyle Evans				"__idx",
181*2aba0eeaSKyle Evans				"__buf",
182*2aba0eeaSKyle Evans			},
183*2aba0eeaSKyle Evans		},
184*2aba0eeaSKyle Evans		{
185*2aba0eeaSKyle Evans			func = "FD_CLR",
186*2aba0eeaSKyle Evans			bufsize = "FD_SETSIZE",
187*2aba0eeaSKyle Evans			buftype = "fd_set",
188*2aba0eeaSKyle Evans			arguments = {
189*2aba0eeaSKyle Evans				"__idx",
190*2aba0eeaSKyle Evans				"__buf",
191*2aba0eeaSKyle Evans			},
192*2aba0eeaSKyle Evans		},
193*2aba0eeaSKyle Evans		{
194*2aba0eeaSKyle Evans			func = "FD_ISSET",
195*2aba0eeaSKyle Evans			bufsize = "FD_SETSIZE",
196*2aba0eeaSKyle Evans			buftype = "fd_set",
197*2aba0eeaSKyle Evans			arguments = {
198*2aba0eeaSKyle Evans				"__idx",
199*2aba0eeaSKyle Evans				"__buf",
200*2aba0eeaSKyle Evans			},
201*2aba0eeaSKyle Evans		},
202*2aba0eeaSKyle Evans	},
2031ace24b3SKyle Evans	uio = {
2041ace24b3SKyle Evans		-- <sys/uio.h>
2051ace24b3SKyle Evans		{
2061ace24b3SKyle Evans			func = "readv",
2071ace24b3SKyle Evans			buftype = "struct iovec[]",
2081ace24b3SKyle Evans			bufsize = 2,
2091ace24b3SKyle Evans			arguments = {
2101ace24b3SKyle Evans				"STDIN_FILENO",
2111ace24b3SKyle Evans				"__buf",
2121ace24b3SKyle Evans				"__len",
2131ace24b3SKyle Evans			},
2141ace24b3SKyle Evans		},
2151ace24b3SKyle Evans		{
2161ace24b3SKyle Evans			func = "readv",
2171ace24b3SKyle Evans			variant = "iov",
2181ace24b3SKyle Evans			arguments = {
2191ace24b3SKyle Evans				"STDIN_FILENO",
2201ace24b3SKyle Evans				"iov",
2211ace24b3SKyle Evans				"nitems(iov)",
2221ace24b3SKyle Evans			},
2231ace24b3SKyle Evans			exclude = excludes_stack_overflow,
2241ace24b3SKyle Evans			stackvars = readv_stackvars,
2251ace24b3SKyle Evans			init = readv_init,
2261ace24b3SKyle Evans			uses_len = true,
2271ace24b3SKyle Evans		},
2281ace24b3SKyle Evans		{
2291ace24b3SKyle Evans			func = "preadv",
2301ace24b3SKyle Evans			buftype = "struct iovec[]",
2311ace24b3SKyle Evans			bufsize = 2,
2321ace24b3SKyle Evans			arguments = {
2331ace24b3SKyle Evans				"STDIN_FILENO",
2341ace24b3SKyle Evans				"__buf",
2351ace24b3SKyle Evans				"__len",
2361ace24b3SKyle Evans				"0",
2371ace24b3SKyle Evans			},
2381ace24b3SKyle Evans		},
2391ace24b3SKyle Evans		{
2401ace24b3SKyle Evans			func = "preadv",
2411ace24b3SKyle Evans			variant = "iov",
2421ace24b3SKyle Evans			arguments = {
2431ace24b3SKyle Evans				"STDIN_FILENO",
2441ace24b3SKyle Evans				"iov",
2451ace24b3SKyle Evans				"nitems(iov)",
2461ace24b3SKyle Evans				"0",
2471ace24b3SKyle Evans			},
2481ace24b3SKyle Evans			exclude = excludes_stack_overflow,
2491ace24b3SKyle Evans			stackvars = readv_stackvars,
2501ace24b3SKyle Evans			init = readv_init,
2511ace24b3SKyle Evans			uses_len = true,
2521ace24b3SKyle Evans		},
2531ace24b3SKyle Evans	},
25488276dfbSKyle Evans	poll = {
25588276dfbSKyle Evans		-- <poll.h>
25688276dfbSKyle Evans		{
25788276dfbSKyle Evans			func = "poll",
25888276dfbSKyle Evans			bufsize = "4",
25988276dfbSKyle Evans			buftype = "struct pollfd[]",
26088276dfbSKyle Evans			arguments = {
26188276dfbSKyle Evans				"__buf",
26288276dfbSKyle Evans				"__len",
26388276dfbSKyle Evans				"0",
26488276dfbSKyle Evans			},
26588276dfbSKyle Evans			init = poll_init,
26688276dfbSKyle Evans		},
26788276dfbSKyle Evans		{
26888276dfbSKyle Evans			func = "ppoll",
26988276dfbSKyle Evans			bufsize = "4",
27088276dfbSKyle Evans			buftype = "struct pollfd[]",
27188276dfbSKyle Evans			arguments = {
27288276dfbSKyle Evans				"__buf",
27388276dfbSKyle Evans				"__len",
27488276dfbSKyle Evans				"&tv",
27588276dfbSKyle Evans				"NULL",
27688276dfbSKyle Evans			},
27788276dfbSKyle Evans			stackvars = "\tstruct timespec tv = { 0 };\n",
27888276dfbSKyle Evans			init = poll_init,
27988276dfbSKyle Evans		},
28088276dfbSKyle Evans	},
281020d003cSKyle Evans	stdio = {
282020d003cSKyle Evans		-- <stdio.h>
283020d003cSKyle Evans		{
284cf8e5289SKyle Evans			func = "ctermid",
285cf8e5289SKyle Evans			bufsize = "L_ctermid",
286cf8e5289SKyle Evans			arguments = {
287cf8e5289SKyle Evans				"__buf",
288cf8e5289SKyle Evans			},
289cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
290cf8e5289SKyle Evans		},
291cf8e5289SKyle Evans		{
292cf8e5289SKyle Evans			func = "ctermid_r",
293cf8e5289SKyle Evans			bufsize = "L_ctermid",
294cf8e5289SKyle Evans			arguments = {
295cf8e5289SKyle Evans				"__buf",
296cf8e5289SKyle Evans			},
297cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
298cf8e5289SKyle Evans		},
299cf8e5289SKyle Evans		{
300cf8e5289SKyle Evans			func = "fread",
301cf8e5289SKyle Evans			arguments = {
302cf8e5289SKyle Evans				"__buf",
303cf8e5289SKyle Evans				"__len",
304cf8e5289SKyle Evans				"1",
305cf8e5289SKyle Evans				"stdin",
306cf8e5289SKyle Evans			},
307cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
308cf8e5289SKyle Evans			init = stdio_init,
309cf8e5289SKyle Evans		},
310cf8e5289SKyle Evans		{
311cf8e5289SKyle Evans			func = "fread_unlocked",
312cf8e5289SKyle Evans			arguments = {
313cf8e5289SKyle Evans				"__buf",
314cf8e5289SKyle Evans				"__len",
315cf8e5289SKyle Evans				"1",
316cf8e5289SKyle Evans				"stdin",
317cf8e5289SKyle Evans			},
318cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
319cf8e5289SKyle Evans			init = stdio_init,
320cf8e5289SKyle Evans		},
321cf8e5289SKyle Evans		{
322cf8e5289SKyle Evans			func = "gets_s",
323cf8e5289SKyle Evans			arguments = {
324cf8e5289SKyle Evans				"__buf",
325cf8e5289SKyle Evans				"__len",
326cf8e5289SKyle Evans			},
327cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
328cf8e5289SKyle Evans			init = stdio_init,
329cf8e5289SKyle Evans		},
330cf8e5289SKyle Evans		{
331020d003cSKyle Evans			func = "sprintf",
332020d003cSKyle Evans			arguments = {
333020d003cSKyle Evans				"__buf",
334020d003cSKyle Evans				"\"%.*s\"",
335020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
336020d003cSKyle Evans				"srcvar",
337020d003cSKyle Evans			},
338020d003cSKyle Evans			exclude = excludes_stack_overflow,
339020d003cSKyle Evans			stackvars = printf_stackvars,
340020d003cSKyle Evans			init = printf_init,
341020d003cSKyle Evans		},
342020d003cSKyle Evans		{
343020d003cSKyle Evans			func = "snprintf",
344020d003cSKyle Evans			arguments = {
345020d003cSKyle Evans				"__buf",
346020d003cSKyle Evans				"__len",
347020d003cSKyle Evans				"\"%.*s\"",
348020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
349020d003cSKyle Evans				"srcvar",
350020d003cSKyle Evans			},
351020d003cSKyle Evans			exclude = excludes_stack_overflow,
352020d003cSKyle Evans			stackvars = printf_stackvars,
353020d003cSKyle Evans			init = printf_init,
354020d003cSKyle Evans		},
355cf8e5289SKyle Evans		{
356cf8e5289SKyle Evans			func = "tmpnam",
357cf8e5289SKyle Evans			bufsize = "L_tmpnam",
358cf8e5289SKyle Evans			arguments = {
359cf8e5289SKyle Evans				"__buf",
360cf8e5289SKyle Evans			},
361cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
362cf8e5289SKyle Evans		},
363cf8e5289SKyle Evans		{
364cf8e5289SKyle Evans			func = "fgets",
365cf8e5289SKyle Evans			arguments = {
366cf8e5289SKyle Evans				"__buf",
367cf8e5289SKyle Evans				"__len",
368cf8e5289SKyle Evans				"fp",
369cf8e5289SKyle Evans			},
370cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
371cf8e5289SKyle Evans			stackvars = "\tFILE *fp;\n",
372cf8e5289SKyle Evans			init = [[
373cf8e5289SKyle Evans	fp = new_fp(__len);
374cf8e5289SKyle Evans]],
375cf8e5289SKyle Evans		},
376020d003cSKyle Evans	},
377d0b74459SKyle Evans	stdlib = {
378d0b74459SKyle Evans		-- <stdlib.h>
379d0b74459SKyle Evans		{
380d0b74459SKyle Evans			func = "arc4random_buf",
381d0b74459SKyle Evans			arguments = {
382d0b74459SKyle Evans				"__buf",
383d0b74459SKyle Evans				"__len",
384d0b74459SKyle Evans			},
385d0b74459SKyle Evans			exclude = excludes_stack_overflow,
386d0b74459SKyle Evans		},
387d0b74459SKyle Evans		{
388d0b74459SKyle Evans			func = "realpath",
389d0b74459SKyle Evans			bufsize = "PATH_MAX",
390d0b74459SKyle Evans			arguments = {
391d0b74459SKyle Evans				"\".\"",
392d0b74459SKyle Evans				"__buf",
393d0b74459SKyle Evans			},
394d0b74459SKyle Evans			exclude = excludes_stack_overflow,
395d0b74459SKyle Evans		},
396d0b74459SKyle Evans	},
397020d003cSKyle Evans	string = {
398020d003cSKyle Evans		-- <string.h>
399020d003cSKyle Evans		{
400020d003cSKyle Evans			func = "memcpy",
401020d003cSKyle Evans			arguments = {
402020d003cSKyle Evans				"__buf",
403020d003cSKyle Evans				"src",
404020d003cSKyle Evans				"__len",
405020d003cSKyle Evans			},
406020d003cSKyle Evans			exclude = excludes_stack_overflow,
407020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
408020d003cSKyle Evans		},
409020d003cSKyle Evans		{
410cf8e5289SKyle Evans			func = "mempcpy",
411cf8e5289SKyle Evans			arguments = {
412cf8e5289SKyle Evans				"__buf",
413cf8e5289SKyle Evans				"src",
414cf8e5289SKyle Evans				"__len",
415cf8e5289SKyle Evans			},
416cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
417cf8e5289SKyle Evans			stackvars = "\tchar src[__len + 10];\n",
418cf8e5289SKyle Evans		},
419cf8e5289SKyle Evans		{
420020d003cSKyle Evans			func = "memmove",
421020d003cSKyle Evans			arguments = {
422020d003cSKyle Evans				"__buf",
423020d003cSKyle Evans				"src",
424020d003cSKyle Evans				"__len",
425020d003cSKyle Evans			},
426020d003cSKyle Evans			exclude = excludes_stack_overflow,
427020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
428020d003cSKyle Evans		},
429020d003cSKyle Evans		{
430020d003cSKyle Evans			func = "memset",
431020d003cSKyle Evans			arguments = {
432020d003cSKyle Evans				"__buf",
433020d003cSKyle Evans				"0",
434020d003cSKyle Evans				"__len",
435020d003cSKyle Evans			},
436020d003cSKyle Evans			exclude = excludes_stack_overflow,
437020d003cSKyle Evans		},
438020d003cSKyle Evans		{
439020d003cSKyle Evans			func = "stpcpy",
440020d003cSKyle Evans			arguments = {
441020d003cSKyle Evans				"__buf",
442020d003cSKyle Evans				"src",
443020d003cSKyle Evans			},
444020d003cSKyle Evans			exclude = excludes_stack_overflow,
445020d003cSKyle Evans			stackvars = string_stackvars,
446020d003cSKyle Evans			init = string_init,
447020d003cSKyle Evans			uses_len = true,
448020d003cSKyle Evans		},
449020d003cSKyle Evans		{
450020d003cSKyle Evans			func = "stpncpy",
451020d003cSKyle Evans			arguments = {
452020d003cSKyle Evans				"__buf",
453020d003cSKyle Evans				"src",
454020d003cSKyle Evans				"__len",
455020d003cSKyle Evans			},
456020d003cSKyle Evans			exclude = excludes_stack_overflow,
457020d003cSKyle Evans			stackvars = string_stackvars,
458020d003cSKyle Evans			init = string_init,
459020d003cSKyle Evans		},
460020d003cSKyle Evans		{
461020d003cSKyle Evans			func = "strcat",
462020d003cSKyle Evans			arguments = {
463020d003cSKyle Evans				"__buf",
464020d003cSKyle Evans				"src",
465020d003cSKyle Evans			},
466020d003cSKyle Evans			exclude = excludes_stack_overflow,
467020d003cSKyle Evans			stackvars = string_stackvars,
468020d003cSKyle Evans			init = string_init,
469020d003cSKyle Evans			uses_len = true,
470020d003cSKyle Evans		},
471020d003cSKyle Evans		{
472cf8e5289SKyle Evans			func = "strlcat",
473cf8e5289SKyle Evans			arguments = {
474cf8e5289SKyle Evans				"__buf",
475cf8e5289SKyle Evans				"src",
476cf8e5289SKyle Evans				"__len",
477cf8e5289SKyle Evans			},
478cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
479cf8e5289SKyle Evans			stackvars = string_stackvars,
480cf8e5289SKyle Evans			init = string_init,
481cf8e5289SKyle Evans		},
482cf8e5289SKyle Evans		{
483020d003cSKyle Evans			func = "strncat",
484020d003cSKyle Evans			arguments = {
485020d003cSKyle Evans				"__buf",
486020d003cSKyle Evans				"src",
487020d003cSKyle Evans				"__len",
488020d003cSKyle Evans			},
489020d003cSKyle Evans			exclude = excludes_stack_overflow,
490020d003cSKyle Evans			stackvars = string_stackvars,
491020d003cSKyle Evans			init = string_init,
492020d003cSKyle Evans		},
493020d003cSKyle Evans		{
494020d003cSKyle Evans			func = "strcpy",
495020d003cSKyle Evans			arguments = {
496020d003cSKyle Evans				"__buf",
497020d003cSKyle Evans				"src",
498020d003cSKyle Evans			},
499020d003cSKyle Evans			exclude = excludes_stack_overflow,
500020d003cSKyle Evans			stackvars = string_stackvars,
501020d003cSKyle Evans			init = string_init,
502020d003cSKyle Evans			uses_len = true,
503020d003cSKyle Evans		},
504020d003cSKyle Evans		{
505cf8e5289SKyle Evans			func = "strlcpy",
506cf8e5289SKyle Evans			arguments = {
507cf8e5289SKyle Evans				"__buf",
508cf8e5289SKyle Evans				"src",
509cf8e5289SKyle Evans				"__len",
510cf8e5289SKyle Evans			},
511cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
512cf8e5289SKyle Evans			stackvars = string_stackvars,
513cf8e5289SKyle Evans			init = string_init,
514cf8e5289SKyle Evans		},
515cf8e5289SKyle Evans		{
516020d003cSKyle Evans			func = "strncpy",
517020d003cSKyle Evans			arguments = {
518020d003cSKyle Evans				"__buf",
519020d003cSKyle Evans				"src",
520020d003cSKyle Evans				"__len",
521020d003cSKyle Evans			},
522020d003cSKyle Evans			exclude = excludes_stack_overflow,
523020d003cSKyle Evans			stackvars = string_stackvars,
524020d003cSKyle Evans			init = string_init,
525020d003cSKyle Evans		},
526020d003cSKyle Evans	},
527020d003cSKyle Evans	strings = {
528020d003cSKyle Evans		-- <strings.h>
529020d003cSKyle Evans		{
530020d003cSKyle Evans			func = "bcopy",
531020d003cSKyle Evans			arguments = {
532020d003cSKyle Evans				"src",
533020d003cSKyle Evans				"__buf",
534020d003cSKyle Evans				"__len",
535020d003cSKyle Evans			},
536020d003cSKyle Evans			exclude = excludes_stack_overflow,
537020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
538020d003cSKyle Evans		},
539020d003cSKyle Evans		{
540020d003cSKyle Evans			func = "bzero",
541020d003cSKyle Evans			arguments = {
542020d003cSKyle Evans				"__buf",
543020d003cSKyle Evans				"__len",
544020d003cSKyle Evans			},
545020d003cSKyle Evans			exclude = excludes_stack_overflow,
546020d003cSKyle Evans		},
547cf8e5289SKyle Evans		{
548cf8e5289SKyle Evans			func = "explicit_bzero",
549cf8e5289SKyle Evans			arguments = {
550cf8e5289SKyle Evans				"__buf",
551cf8e5289SKyle Evans				"__len",
552cf8e5289SKyle Evans			},
553cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
554cf8e5289SKyle Evans		},
555020d003cSKyle Evans	},
556020d003cSKyle Evans	unistd = {
557020d003cSKyle Evans		-- <unistd.h>
558020d003cSKyle Evans		{
559020d003cSKyle Evans			func = "getcwd",
560020d003cSKyle Evans			bufsize = "8",
561020d003cSKyle Evans			arguments = {
562020d003cSKyle Evans				"__buf",
563020d003cSKyle Evans				"__len",
564020d003cSKyle Evans			},
565020d003cSKyle Evans			exclude = excludes_stack_overflow,
566020d003cSKyle Evans		},
567020d003cSKyle Evans		{
568cf8e5289SKyle Evans			func = "getgrouplist",
569cf8e5289SKyle Evans			bufsize = "4",
570cf8e5289SKyle Evans			buftype = "gid_t[]",
571cf8e5289SKyle Evans			arguments = {
572cf8e5289SKyle Evans				"\"root\"",
573cf8e5289SKyle Evans				"0",
574cf8e5289SKyle Evans				"__buf",
575cf8e5289SKyle Evans				"&intlen",
576cf8e5289SKyle Evans			},
577cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
578cf8e5289SKyle Evans			stackvars = "\tint intlen = (int)__len;\n",
579cf8e5289SKyle Evans			uses_len = true,
580cf8e5289SKyle Evans		},
581cf8e5289SKyle Evans		{
582cf8e5289SKyle Evans			func = "getgroups",
583cf8e5289SKyle Evans			bufsize = "4",
584cf8e5289SKyle Evans			buftype = "gid_t[]",
585cf8e5289SKyle Evans			arguments = {
586cf8e5289SKyle Evans				"__len",
587cf8e5289SKyle Evans				"__buf",
588cf8e5289SKyle Evans			},
589cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
590cf8e5289SKyle Evans		},
591cf8e5289SKyle Evans		{
592cf8e5289SKyle Evans			func = "getloginclass",
593cf8e5289SKyle Evans			arguments = {
594cf8e5289SKyle Evans				"__buf",
595cf8e5289SKyle Evans				"__len",
596cf8e5289SKyle Evans			},
597cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
598cf8e5289SKyle Evans		},
599cf8e5289SKyle Evans		{
600cf8e5289SKyle Evans			func = "pread",
601cf8e5289SKyle Evans			bufsize = "41",
602cf8e5289SKyle Evans			arguments = {
603cf8e5289SKyle Evans				"fd",
604cf8e5289SKyle Evans				"__buf",
605cf8e5289SKyle Evans				"__len",
606cf8e5289SKyle Evans				"0",
607cf8e5289SKyle Evans			},
608cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
609cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
610cf8e5289SKyle Evans			init = [[
611cf8e5289SKyle Evans	fd = new_tmpfile();	/* Cannot fail */
612cf8e5289SKyle Evans]],
613cf8e5289SKyle Evans		},
614cf8e5289SKyle Evans		{
615020d003cSKyle Evans			func = "read",
616020d003cSKyle Evans			bufsize = "41",
617020d003cSKyle Evans			arguments = {
618020d003cSKyle Evans				"fd",
619020d003cSKyle Evans				"__buf",
620020d003cSKyle Evans				"__len",
621020d003cSKyle Evans			},
622020d003cSKyle Evans			exclude = excludes_stack_overflow,
623020d003cSKyle Evans			stackvars = "\tint fd;\n",
624020d003cSKyle Evans			init = [[
625020d003cSKyle Evans	fd = new_tmpfile();	/* Cannot fail */
626020d003cSKyle Evans]],
627020d003cSKyle Evans		},
628020d003cSKyle Evans		{
629020d003cSKyle Evans			func = "readlink",
630020d003cSKyle Evans			arguments = {
631020d003cSKyle Evans				"path",
632020d003cSKyle Evans				"__buf",
633020d003cSKyle Evans				"__len",
634020d003cSKyle Evans			},
635020d003cSKyle Evans			exclude = excludes_stack_overflow,
636020d003cSKyle Evans			stackvars = "\tconst char *path;\n",
637020d003cSKyle Evans			init = [[
638020d003cSKyle Evans	path = new_symlink(__len);		/* Cannot fail */
639020d003cSKyle Evans]],
640020d003cSKyle Evans		},
641cf8e5289SKyle Evans		{
642cf8e5289SKyle Evans			func = "readlinkat",
643cf8e5289SKyle Evans			arguments = {
644cf8e5289SKyle Evans				"AT_FDCWD",
645cf8e5289SKyle Evans				"path",
646cf8e5289SKyle Evans				"__buf",
647cf8e5289SKyle Evans				"__len",
648cf8e5289SKyle Evans			},
649cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
650cf8e5289SKyle Evans			stackvars = "\tconst char *path;\n",
651cf8e5289SKyle Evans			init = [[
652cf8e5289SKyle Evans	path = new_symlink(__len);		/* Cannot fail */
653cf8e5289SKyle Evans]],
654cf8e5289SKyle Evans		},
655cf8e5289SKyle Evans		{
656cf8e5289SKyle Evans			func = "getdomainname",
657cf8e5289SKyle Evans			bufsize = "4",
658cf8e5289SKyle Evans			arguments = {
659cf8e5289SKyle Evans				"__buf",
660cf8e5289SKyle Evans				"__len",
661cf8e5289SKyle Evans			},
662cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
663cf8e5289SKyle Evans			stackvars = "\tchar sysdomain[256];\n",
664cf8e5289SKyle Evans			early_init = [[
665cf8e5289SKyle Evans	(void)getdomainname(sysdomain, __len);
666cf8e5289SKyle Evans	if (strlen(sysdomain) <= __len)
667cf8e5289SKyle Evans		atf_tc_skip("domain name too short for testing");
668cf8e5289SKyle Evans]]
669cf8e5289SKyle Evans		},
670cf8e5289SKyle Evans		{
671cf8e5289SKyle Evans			func = "getentropy",
672cf8e5289SKyle Evans			arguments = {
673cf8e5289SKyle Evans				"__buf",
674cf8e5289SKyle Evans				"__len",
675cf8e5289SKyle Evans			},
676cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
677cf8e5289SKyle Evans		},
678cf8e5289SKyle Evans		{
679cf8e5289SKyle Evans			func = "gethostname",
680cf8e5289SKyle Evans			bufsize = "4",
681cf8e5289SKyle Evans			arguments = {
682cf8e5289SKyle Evans				"__buf",
683cf8e5289SKyle Evans				"__len",
684cf8e5289SKyle Evans			},
685cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
686cf8e5289SKyle Evans			stackvars = [[
687cf8e5289SKyle Evans	char syshost[256];
688cf8e5289SKyle Evans	int error;
689cf8e5289SKyle Evans]],
690cf8e5289SKyle Evans			early_init = [[
691cf8e5289SKyle Evans	error = gethostname(syshost, __len);
692cf8e5289SKyle Evans	if (error != 0 || strlen(syshost) <= __len)
693cf8e5289SKyle Evans		atf_tc_skip("hostname too short for testing");
694cf8e5289SKyle Evans]]
695cf8e5289SKyle Evans		},
696cf8e5289SKyle Evans		{
697cf8e5289SKyle Evans			func = "getlogin_r",
698cf8e5289SKyle Evans			bufsize = "MAXLOGNAME + 1",
699cf8e5289SKyle Evans			arguments = {
700cf8e5289SKyle Evans				"__buf",
701cf8e5289SKyle Evans				"__len",
702cf8e5289SKyle Evans			},
703cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
704cf8e5289SKyle Evans		},
705cf8e5289SKyle Evans		{
706cf8e5289SKyle Evans			func = "ttyname_r",
707cf8e5289SKyle Evans			arguments = {
708cf8e5289SKyle Evans				"fd",
709cf8e5289SKyle Evans				"__buf",
710cf8e5289SKyle Evans				"__len",
711cf8e5289SKyle Evans			},
712cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
713cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
714cf8e5289SKyle Evans			early_init = [[
715cf8e5289SKyle Evans	fd = STDIN_FILENO;
716cf8e5289SKyle Evans	if (!isatty(fd))
717cf8e5289SKyle Evans		atf_tc_skip("stdin is not an fd");
718cf8e5289SKyle Evans]]
719cf8e5289SKyle Evans		},
720020d003cSKyle Evans	},
721b53d7aa8SKyle Evans	wchar = {
722b53d7aa8SKyle Evans		-- <wchar.h>
723b53d7aa8SKyle Evans		{
724b53d7aa8SKyle Evans			func = "wmemcpy",
725b53d7aa8SKyle Evans			buftype = "wchar_t[]",
726b53d7aa8SKyle Evans			arguments = {
727b53d7aa8SKyle Evans				"__buf",
728b53d7aa8SKyle Evans				"src",
729b53d7aa8SKyle Evans				"__len",
730b53d7aa8SKyle Evans			},
731b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
732b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
733b53d7aa8SKyle Evans		},
734b53d7aa8SKyle Evans		{
735b53d7aa8SKyle Evans			func = "wmempcpy",
736b53d7aa8SKyle Evans			buftype = "wchar_t[]",
737b53d7aa8SKyle Evans			arguments = {
738b53d7aa8SKyle Evans				"__buf",
739b53d7aa8SKyle Evans				"src",
740b53d7aa8SKyle Evans				"__len",
741b53d7aa8SKyle Evans			},
742b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
743b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
744b53d7aa8SKyle Evans		},
745b53d7aa8SKyle Evans		{
746b53d7aa8SKyle Evans			func = "wmemmove",
747b53d7aa8SKyle Evans			buftype = "wchar_t[]",
748b53d7aa8SKyle Evans			arguments = {
749b53d7aa8SKyle Evans				"__buf",
750b53d7aa8SKyle Evans				"src",
751b53d7aa8SKyle Evans				"__len",
752b53d7aa8SKyle Evans			},
753b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
754b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
755b53d7aa8SKyle Evans		},
756b53d7aa8SKyle Evans		{
757b53d7aa8SKyle Evans			func = "wmemset",
758b53d7aa8SKyle Evans			buftype = "wchar_t[]",
759b53d7aa8SKyle Evans			arguments = {
760b53d7aa8SKyle Evans				"__buf",
761b53d7aa8SKyle Evans				"L'0'",
762b53d7aa8SKyle Evans				"__len",
763b53d7aa8SKyle Evans			},
764b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
765b53d7aa8SKyle Evans		},
766b53d7aa8SKyle Evans		{
767b53d7aa8SKyle Evans			func = "wcpcpy",
768b53d7aa8SKyle Evans			buftype = "wchar_t[]",
769b53d7aa8SKyle Evans			arguments = {
770b53d7aa8SKyle Evans				"__buf",
771b53d7aa8SKyle Evans				"src",
772b53d7aa8SKyle Evans			},
773b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
774b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
775b53d7aa8SKyle Evans			init = wstring_init,
776b53d7aa8SKyle Evans			uses_len = true,
777b53d7aa8SKyle Evans		},
778b53d7aa8SKyle Evans		{
779b53d7aa8SKyle Evans			func = "wcpncpy",
780b53d7aa8SKyle Evans			buftype = "wchar_t[]",
781b53d7aa8SKyle Evans			arguments = {
782b53d7aa8SKyle Evans				"__buf",
783b53d7aa8SKyle Evans				"src",
784b53d7aa8SKyle Evans				"__len",
785b53d7aa8SKyle Evans			},
786b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
787b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
788b53d7aa8SKyle Evans			init = wstring_init,
789b53d7aa8SKyle Evans		},
790b53d7aa8SKyle Evans		{
791b53d7aa8SKyle Evans			func = "wcscat",
792b53d7aa8SKyle Evans			buftype = "wchar_t[]",
793b53d7aa8SKyle Evans			arguments = {
794b53d7aa8SKyle Evans				"__buf",
795b53d7aa8SKyle Evans				"src",
796b53d7aa8SKyle Evans			},
797b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
798b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
799b53d7aa8SKyle Evans			init = wstring_init,
800b53d7aa8SKyle Evans			uses_len = true,
801b53d7aa8SKyle Evans		},
802b53d7aa8SKyle Evans		{
803b53d7aa8SKyle Evans			func = "wcslcat",
804b53d7aa8SKyle Evans			buftype = "wchar_t[]",
805b53d7aa8SKyle Evans			arguments = {
806b53d7aa8SKyle Evans				"__buf",
807b53d7aa8SKyle Evans				"src",
808b53d7aa8SKyle Evans				"__len",
809b53d7aa8SKyle Evans			},
810b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
811b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
812b53d7aa8SKyle Evans			init = wstring_init,
813b53d7aa8SKyle Evans		},
814b53d7aa8SKyle Evans		{
815b53d7aa8SKyle Evans			func = "wcsncat",
816b53d7aa8SKyle Evans			buftype = "wchar_t[]",
817b53d7aa8SKyle Evans			arguments = {
818b53d7aa8SKyle Evans				"__buf",
819b53d7aa8SKyle Evans				"src",
820b53d7aa8SKyle Evans				"__len",
821b53d7aa8SKyle Evans			},
822b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
823b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
824b53d7aa8SKyle Evans			init = wstring_init,
825b53d7aa8SKyle Evans		},
826b53d7aa8SKyle Evans		{
827b53d7aa8SKyle Evans			func = "wcscpy",
828b53d7aa8SKyle Evans			buftype = "wchar_t[]",
829b53d7aa8SKyle Evans			arguments = {
830b53d7aa8SKyle Evans				"__buf",
831b53d7aa8SKyle Evans				"src",
832b53d7aa8SKyle Evans			},
833b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
834b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
835b53d7aa8SKyle Evans			init = wstring_init,
836b53d7aa8SKyle Evans			uses_len = true,
837b53d7aa8SKyle Evans		},
838b53d7aa8SKyle Evans		{
839b53d7aa8SKyle Evans			func = "wcslcpy",
840b53d7aa8SKyle Evans			buftype = "wchar_t[]",
841b53d7aa8SKyle Evans			arguments = {
842b53d7aa8SKyle Evans				"__buf",
843b53d7aa8SKyle Evans				"src",
844b53d7aa8SKyle Evans				"__len",
845b53d7aa8SKyle Evans			},
846b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
847b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
848b53d7aa8SKyle Evans			init = wstring_init,
849b53d7aa8SKyle Evans		},
850b53d7aa8SKyle Evans		{
851b53d7aa8SKyle Evans			func = "wcsncpy",
852b53d7aa8SKyle Evans			buftype = "wchar_t[]",
853b53d7aa8SKyle Evans			arguments = {
854b53d7aa8SKyle Evans				"__buf",
855b53d7aa8SKyle Evans				"src",
856b53d7aa8SKyle Evans				"__len",
857b53d7aa8SKyle Evans			},
858b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
859b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
860b53d7aa8SKyle Evans			init = wstring_init,
861b53d7aa8SKyle Evans		},
862b53d7aa8SKyle Evans	},
863020d003cSKyle Evans}
864020d003cSKyle Evans
865020d003cSKyle Evanslocal function write_test_boilerplate(fh, name, body)
866020d003cSKyle Evans	fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
867020d003cSKyle Evans	fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
868020d003cSKyle Evans	fh:write("{\n" .. body .. "\n}\n\n")
869020d003cSKyle Evans	return name
870020d003cSKyle Evansend
871020d003cSKyle Evans
872020d003cSKyle Evanslocal function generate_test_name(func, variant, disposition, heap)
873020d003cSKyle Evans	local basename = func
874020d003cSKyle Evans	if variant then
875020d003cSKyle Evans		basename = basename .. "_" .. variant
876020d003cSKyle Evans	end
877020d003cSKyle Evans	if heap then
878020d003cSKyle Evans		basename = basename .. "_heap"
879020d003cSKyle Evans	end
880020d003cSKyle Evans	if disposition < 0 then
881020d003cSKyle Evans		return basename .. "_before_end"
882020d003cSKyle Evans	elseif disposition == 0 then
883020d003cSKyle Evans		return basename .. "_end"
884020d003cSKyle Evans	else
885020d003cSKyle Evans		return basename .. "_after_end"
886020d003cSKyle Evans	end
887020d003cSKyle Evansend
888020d003cSKyle Evans
889020d003cSKyle Evanslocal function array_type(buftype)
890020d003cSKyle Evans	if not buftype:match("%[%]") then
891020d003cSKyle Evans		return nil
892020d003cSKyle Evans	end
893020d003cSKyle Evans
894020d003cSKyle Evans	return buftype:gsub("%[%]", "")
895020d003cSKyle Evansend
896020d003cSKyle Evans
897020d003cSKyle Evanslocal function configurable(def, idx)
898020d003cSKyle Evans	local cfgitem = def[idx]
899020d003cSKyle Evans
900020d003cSKyle Evans	if not cfgitem then
901020d003cSKyle Evans		return nil
902020d003cSKyle Evans	end
903020d003cSKyle Evans
904020d003cSKyle Evans	if type(cfgitem) == "function" then
905020d003cSKyle Evans		return cfgitem()
906020d003cSKyle Evans	end
907020d003cSKyle Evans
908020d003cSKyle Evans	return cfgitem
909020d003cSKyle Evansend
910020d003cSKyle Evans
911020d003cSKyle Evanslocal function generate_stackframe(buftype, bufsize, disposition, heap, def)
912020d003cSKyle Evans	local function len_offset(inverted, disposition)
913020d003cSKyle Evans		-- Tests that don't use __len in their arguments may use an
914020d003cSKyle Evans		-- inverted sense because we can't just specify a length that
915020d003cSKyle Evans		-- would induce an access just after the end.  Instead, we have
916020d003cSKyle Evans		-- to manipulate the buffer size to be too short so that the
917020d003cSKyle Evans		-- function under test would write one too many.
918020d003cSKyle Evans		if disposition < 0 then
919020d003cSKyle Evans			return ((inverted and " + ") or " - ") .. "1"
920020d003cSKyle Evans		elseif disposition == 0 then
921020d003cSKyle Evans			return ""
922020d003cSKyle Evans		else
923020d003cSKyle Evans			return ((inverted and " - ") or " + ") .. "1"
924020d003cSKyle Evans		end
925020d003cSKyle Evans	end
926020d003cSKyle Evans
927020d003cSKyle Evans	local function test_uses_len(def)
928020d003cSKyle Evans		if def.uses_len then
929020d003cSKyle Evans			return true
930020d003cSKyle Evans		end
931020d003cSKyle Evans
932020d003cSKyle Evans		for _, arg in ipairs(def.arguments) do
933020d003cSKyle Evans			if arg:match("__len") or arg:match("__idx") then
934020d003cSKyle Evans				return true
935020d003cSKyle Evans			end
936020d003cSKyle Evans		end
937020d003cSKyle Evans
938020d003cSKyle Evans		return false
939020d003cSKyle Evans	end
940020d003cSKyle Evans
941020d003cSKyle Evans
942020d003cSKyle Evans	-- This is perhaps a little convoluted, but we toss the buffer into a
943020d003cSKyle Evans	-- struct on the stack to guarantee that we have at least one valid
944020d003cSKyle Evans	-- byte on either side of the buffer -- a measure to make sure that
945020d003cSKyle Evans	-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
946020d003cSKyle Evans	-- rather than some other stack or memory protection.
947020d003cSKyle Evans	local vars = "\tstruct {\n"
948020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_l;\n"
949020d003cSKyle Evans
950020d003cSKyle Evans	local uses_len = test_uses_len(def)
951020d003cSKyle Evans	local bufsize_offset = len_offset(not uses_len, disposition)
952020d003cSKyle Evans	local buftype_elem = array_type(buftype)
953020d003cSKyle Evans	local size_expr = bufsize
954020d003cSKyle Evans
955020d003cSKyle Evans	if not uses_len then
956020d003cSKyle Evans		-- If the length isn't in use, we have to vary the buffer size
957020d003cSKyle Evans		-- since the fortified function likely has some internal size
958020d003cSKyle Evans		-- constraint that it's supposed to be checking.
959020d003cSKyle Evans		size_expr = size_expr .. bufsize_offset
960020d003cSKyle Evans	end
961020d003cSKyle Evans
962020d003cSKyle Evans	if not heap and buftype_elem then
963020d003cSKyle Evans		-- Array type: size goes after identifier
964020d003cSKyle Evans		vars = vars .. "\t\t" .. buftype_elem ..
965020d003cSKyle Evans		    " __buf[" .. size_expr .. "];\n"
966020d003cSKyle Evans	else
967020d003cSKyle Evans		local basic_type = buftype_elem or buftype
968020d003cSKyle Evans
969020d003cSKyle Evans		-- Heap tests obviously just put a pointer on the stack that
970020d003cSKyle Evans		-- points to our new allocation, but we leave it in the padded
971020d003cSKyle Evans		-- struct just to simplify our generator.
972020d003cSKyle Evans		if heap then
973020d003cSKyle Evans			basic_type = basic_type .. " *"
974020d003cSKyle Evans		end
975020d003cSKyle Evans		vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
976020d003cSKyle Evans	end
977020d003cSKyle Evans
978020d003cSKyle Evans	-- padding_r is our just-past-the-end padding that we use to make sure
979020d003cSKyle Evans	-- that there's a valid portion after the buffer that isn't being
980020d003cSKyle Evans	-- included in our function calls.  If we didn't have it, then we'd have
981020d003cSKyle Evans	-- a hard time feeling confident that an abort on the just-after tests
982020d003cSKyle Evans	-- isn't maybe from some other memory or stack protection.
983020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_r;\n"
984020d003cSKyle Evans	vars = vars .. "\t} __stack;\n"
985020d003cSKyle Evans
986020d003cSKyle Evans	-- Not all tests will use __bufsz, but some do for, e.g., clearing
987020d003cSKyle Evans	-- memory..
988020d003cSKyle Evans	vars = vars .. "\tconst size_t __bufsz __unused = "
989020d003cSKyle Evans	if heap then
990020d003cSKyle Evans		local scalar = 1
991020d003cSKyle Evans		if buftype_elem then
992020d003cSKyle Evans			scalar = size_expr
993020d003cSKyle Evans		end
994020d003cSKyle Evans
995020d003cSKyle Evans		vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
996020d003cSKyle Evans	else
997020d003cSKyle Evans		vars = vars .. "sizeof(__stack.__buf);\n"
998020d003cSKyle Evans	end
999020d003cSKyle Evans
1000020d003cSKyle Evans	vars = vars .. "\tconst size_t __len = " .. bufsize ..
1001020d003cSKyle Evans	    bufsize_offset .. ";\n"
1002020d003cSKyle Evans	vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
1003020d003cSKyle Evans
1004020d003cSKyle Evans	-- For overflow testing, we need to fork() because we're expecting the
1005020d003cSKyle Evans	-- test to ultimately abort()/_exit().  Then we can collect the exit
1006020d003cSKyle Evans	-- status and report appropriately.
1007020d003cSKyle Evans	if disposition > 0 then
1008020d003cSKyle Evans		vars = vars .. "\tpid_t __child;\n"
1009020d003cSKyle Evans		vars = vars .. "\tint __status;\n"
1010020d003cSKyle Evans	end
1011020d003cSKyle Evans
1012020d003cSKyle Evans	-- Any other stackvars defined by the test get placed after everything
1013020d003cSKyle Evans	-- else.
1014020d003cSKyle Evans	vars = vars .. (configurable(def, "stackvars") or "")
1015020d003cSKyle Evans
1016020d003cSKyle Evans	return vars
1017020d003cSKyle Evansend
1018020d003cSKyle Evans
1019020d003cSKyle Evanslocal function write_test(fh, func, disposition, heap, def)
1020020d003cSKyle Evans	local testname = generate_test_name(func, def.variant, disposition, heap)
1021020d003cSKyle Evans	local buftype = def.buftype or "unsigned char[]"
1022020d003cSKyle Evans	local bufsize = def.bufsize or 42
1023020d003cSKyle Evans	local body = ""
1024020d003cSKyle Evans
1025020d003cSKyle Evans	if def.exclude and def.exclude(disposition, heap) then
1026020d003cSKyle Evans		return
1027020d003cSKyle Evans	end
1028020d003cSKyle Evans
1029020d003cSKyle Evans	local function need_addr(buftype)
1030020d003cSKyle Evans		return not (buftype:match("%[%]") or buftype:match("%*"))
1031020d003cSKyle Evans	end
1032020d003cSKyle Evans
1033020d003cSKyle Evans	if heap then
1034020d003cSKyle Evans		body = body .. "#define BUF __stack.__buf\n"
1035020d003cSKyle Evans	else
1036020d003cSKyle Evans		body = body .. "#define BUF &__stack.__buf\n"
1037020d003cSKyle Evans	end
1038020d003cSKyle Evans
1039020d003cSKyle Evans	-- Setup the buffer
1040020d003cSKyle Evans	body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
1041020d003cSKyle Evans	    "\n"
1042020d003cSKyle Evans
1043020d003cSKyle Evans	-- Any early initialization goes before we would fork for the just-after
1044020d003cSKyle Evans	-- tests, because they may want to skip the test based on some criteria
1045020d003cSKyle Evans	-- and we can't propagate that up very easily once we're forked.
1046020d003cSKyle Evans	local early_init = configurable(def, "early_init")
1047020d003cSKyle Evans	body = body .. (early_init or "")
1048020d003cSKyle Evans	if early_init then
1049020d003cSKyle Evans		body = body .. "\n"
1050020d003cSKyle Evans	end
1051020d003cSKyle Evans
1052020d003cSKyle Evans	-- Fork off, iff we're testing some access past the end of the buffer.
1053020d003cSKyle Evans	if disposition > 0 then
1054020d003cSKyle Evans		body = body .. [[
1055020d003cSKyle Evans	__child = fork();
1056020d003cSKyle Evans	ATF_REQUIRE(__child >= 0);
1057020d003cSKyle Evans	if (__child > 0)
1058020d003cSKyle Evans		goto monitor;
1059020d003cSKyle Evans
1060020d003cSKyle Evans	/* Child */
1061020d003cSKyle Evans	disable_coredumps();
1062020d003cSKyle Evans]]
1063020d003cSKyle Evans	end
1064020d003cSKyle Evans
1065020d003cSKyle Evans	local bufvar = "__stack.__buf"
1066020d003cSKyle Evans	if heap then
1067020d003cSKyle Evans		-- Buffer needs to be initialized because it's a heap allocation.
1068020d003cSKyle Evans		body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
1069020d003cSKyle Evans	end
1070020d003cSKyle Evans
1071020d003cSKyle Evans	-- Non-early init happens just after the fork in the child, not the
1072020d003cSKyle Evans	-- monitor.  This is used to setup any other buffers we may need, for
1073020d003cSKyle Evans	-- instance.
1074020d003cSKyle Evans	local extra_init = configurable(def, "init")
1075020d003cSKyle Evans	body = body .. (extra_init or "")
1076020d003cSKyle Evans
1077020d003cSKyle Evans	if heap or extra_init then
1078020d003cSKyle Evans		body = body .. "\n"
1079020d003cSKyle Evans	end
1080020d003cSKyle Evans
1081020d003cSKyle Evans	-- Setup the function call with arguments as described in the test
1082020d003cSKyle Evans	-- definition.
1083020d003cSKyle Evans	body = body .. "\t" .. func .. "("
1084020d003cSKyle Evans
1085020d003cSKyle Evans	for idx, arg in ipairs(def.arguments) do
1086020d003cSKyle Evans		if idx > 1 then
1087020d003cSKyle Evans			body = body .. ", "
1088020d003cSKyle Evans		end
1089020d003cSKyle Evans
1090020d003cSKyle Evans		if arg == "__buf" then
1091020d003cSKyle Evans			if not heap and need_addr(buftype) then
1092020d003cSKyle Evans				body = body .. "&"
1093020d003cSKyle Evans			end
1094020d003cSKyle Evans
1095020d003cSKyle Evans			body = body .. bufvar
1096020d003cSKyle Evans		else
1097020d003cSKyle Evans			local argname = arg
1098020d003cSKyle Evans
1099020d003cSKyle Evans			if def.value_of then
1100020d003cSKyle Evans				argname = argname or def.value_of(arg)
1101020d003cSKyle Evans			end
1102020d003cSKyle Evans
1103020d003cSKyle Evans			body = body .. argname
1104020d003cSKyle Evans		end
1105020d003cSKyle Evans	end
1106020d003cSKyle Evans
1107020d003cSKyle Evans	body = body .. ");\n"
1108020d003cSKyle Evans
1109020d003cSKyle Evans	-- Monitor stuff follows, for OOB access.
1110020d003cSKyle Evans	if disposition <= 0 then
1111020d003cSKyle Evans		goto skip
1112020d003cSKyle Evans	end
1113020d003cSKyle Evans
1114020d003cSKyle Evans	body = body .. [[
1115020d003cSKyle Evans	_exit(EX_SOFTWARE);	/* Should have aborted. */
1116020d003cSKyle Evans
1117020d003cSKyle Evansmonitor:
1118020d003cSKyle Evans	while (waitpid(__child, &__status, 0) != __child) {
1119020d003cSKyle Evans		ATF_REQUIRE_EQ(EINTR, errno);
1120020d003cSKyle Evans	}
1121020d003cSKyle Evans
1122020d003cSKyle Evans	if (!WIFSIGNALED(__status)) {
1123020d003cSKyle Evans		switch (WEXITSTATUS(__status)) {
1124020d003cSKyle Evans		case EX_SOFTWARE:
1125020d003cSKyle Evans			atf_tc_fail("FORTIFY_SOURCE failed to abort");
1126020d003cSKyle Evans			break;
1127020d003cSKyle Evans		case EX_OSERR:
1128020d003cSKyle Evans			atf_tc_fail("setrlimit(2) failed");
1129020d003cSKyle Evans			break;
1130020d003cSKyle Evans		default:
1131020d003cSKyle Evans			atf_tc_fail("child exited with status %d",
1132020d003cSKyle Evans			    WEXITSTATUS(__status));
1133020d003cSKyle Evans		}
1134020d003cSKyle Evans	} else {
1135020d003cSKyle Evans		ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
1136020d003cSKyle Evans	}
1137020d003cSKyle Evans]]
1138020d003cSKyle Evans
1139020d003cSKyle Evans::skip::
1140020d003cSKyle Evans	body = body .. "#undef BUF\n"
1141020d003cSKyle Evans	return write_test_boilerplate(fh, testname, body)
1142020d003cSKyle Evansend
1143020d003cSKyle Evans
1144020d003cSKyle Evans-- main()
1145020d003cSKyle Evanslocal tests
1146020d003cSKyle Evanslocal tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
1147020d003cSKyle Evansfor k, defs in pairs(all_tests) do
1148020d003cSKyle Evans	if k == tcat then
1149020d003cSKyle Evans		tests = defs
1150020d003cSKyle Evans		break
1151020d003cSKyle Evans	end
1152020d003cSKyle Evansend
1153020d003cSKyle Evans
1154020d003cSKyle Evansassert(tests, "category " .. tcat .. " not found")
1155020d003cSKyle Evans
1156020d003cSKyle Evanslocal fh = io.stdout
1157020d003cSKyle Evansfh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
1158020d003cSKyle Evans    tcat .. "\"` */\n\n")
1159020d003cSKyle Evansfh:write("#define	_FORTIFY_SOURCE	2\n")
1160020d003cSKyle Evansfh:write("#define	TMPFILE_SIZE	(1024 * 32)\n")
1161020d003cSKyle Evans
1162020d003cSKyle Evansfh:write("\n")
1163020d003cSKyle Evansfor _, inc in ipairs(includes) do
1164020d003cSKyle Evans	fh:write("#include <" .. inc .. ">\n")
1165020d003cSKyle Evansend
1166020d003cSKyle Evans
1167020d003cSKyle Evansfh:write([[
1168020d003cSKyle Evans
1169cf8e5289SKyle Evansstatic FILE * __unused
1170cf8e5289SKyle Evansnew_fp(size_t __len)
1171cf8e5289SKyle Evans{
1172cf8e5289SKyle Evans	static char fpbuf[LINE_MAX];
1173cf8e5289SKyle Evans	FILE *fp;
1174cf8e5289SKyle Evans
1175cf8e5289SKyle Evans	ATF_REQUIRE(__len <= sizeof(fpbuf));
1176cf8e5289SKyle Evans
1177cf8e5289SKyle Evans	memset(fpbuf, 'A', sizeof(fpbuf) - 1);
1178cf8e5289SKyle Evans	fpbuf[sizeof(fpbuf) - 1] = '\0';
1179cf8e5289SKyle Evans
1180cf8e5289SKyle Evans	fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
1181cf8e5289SKyle Evans	ATF_REQUIRE(fp != NULL);
1182cf8e5289SKyle Evans
1183cf8e5289SKyle Evans	return (fp);
1184cf8e5289SKyle Evans}
1185cf8e5289SKyle Evans
1186020d003cSKyle Evans/*
1187020d003cSKyle Evans * Create a new symlink to use for readlink(2) style tests, we'll just use a
1188020d003cSKyle Evans * random target name to have something interesting to look at.
1189020d003cSKyle Evans */
1190020d003cSKyle Evansstatic const char * __unused
1191020d003cSKyle Evansnew_symlink(size_t __len)
1192020d003cSKyle Evans{
1193020d003cSKyle Evans	static const char linkname[] = "link";
1194020d003cSKyle Evans	char target[MAXNAMLEN];
1195020d003cSKyle Evans	int error;
1196020d003cSKyle Evans
1197020d003cSKyle Evans	ATF_REQUIRE(__len <= sizeof(target));
1198020d003cSKyle Evans
1199020d003cSKyle Evans	arc4random_buf(target, sizeof(target));
1200020d003cSKyle Evans
1201020d003cSKyle Evans	error = unlink(linkname);
1202020d003cSKyle Evans	ATF_REQUIRE(error == 0 || errno == ENOENT);
1203020d003cSKyle Evans
1204020d003cSKyle Evans	error = symlink(target, linkname);
1205020d003cSKyle Evans	ATF_REQUIRE(error == 0);
1206020d003cSKyle Evans
1207020d003cSKyle Evans	return (linkname);
1208020d003cSKyle Evans}
1209020d003cSKyle Evans
1210020d003cSKyle Evans/*
1211020d003cSKyle Evans * Constructs a tmpfile that we can use for testing read(2) and friends.
1212020d003cSKyle Evans */
1213020d003cSKyle Evansstatic int __unused
1214020d003cSKyle Evansnew_tmpfile(void)
1215020d003cSKyle Evans{
1216020d003cSKyle Evans	char buf[1024];
1217020d003cSKyle Evans	ssize_t rv;
1218020d003cSKyle Evans	size_t written;
1219020d003cSKyle Evans	int fd;
1220020d003cSKyle Evans
1221020d003cSKyle Evans	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
1222020d003cSKyle Evans	ATF_REQUIRE(fd >= 0);
1223020d003cSKyle Evans
1224020d003cSKyle Evans	written = 0;
1225020d003cSKyle Evans	while (written < TMPFILE_SIZE) {
1226020d003cSKyle Evans		rv = write(fd, buf, sizeof(buf));
1227020d003cSKyle Evans		ATF_REQUIRE(rv > 0);
1228020d003cSKyle Evans
1229020d003cSKyle Evans		written += rv;
1230020d003cSKyle Evans	}
1231020d003cSKyle Evans
1232020d003cSKyle Evans	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
1233020d003cSKyle Evans	return (fd);
1234020d003cSKyle Evans}
1235020d003cSKyle Evans
1236020d003cSKyle Evansstatic void
1237020d003cSKyle Evansdisable_coredumps(void)
1238020d003cSKyle Evans{
1239020d003cSKyle Evans	struct rlimit rl = { 0 };
1240020d003cSKyle Evans
1241020d003cSKyle Evans	if (setrlimit(RLIMIT_CORE, &rl) == -1)
1242020d003cSKyle Evans		_exit(EX_OSERR);
1243020d003cSKyle Evans}
1244020d003cSKyle Evans
1245cf8e5289SKyle Evans/*
1246cf8e5289SKyle Evans * Replaces stdin with a file that we can actually read from, for tests where
1247cf8e5289SKyle Evans * we want a FILE * or fd that we can get data from.
1248cf8e5289SKyle Evans */
1249cf8e5289SKyle Evansstatic void __unused
1250cf8e5289SKyle Evansreplace_stdin(void)
1251cf8e5289SKyle Evans{
1252cf8e5289SKyle Evans	int fd;
1253cf8e5289SKyle Evans
1254cf8e5289SKyle Evans	fd = new_tmpfile();
1255cf8e5289SKyle Evans
1256cf8e5289SKyle Evans	(void)dup2(fd, STDIN_FILENO);
1257cf8e5289SKyle Evans	if (fd != STDIN_FILENO)
1258cf8e5289SKyle Evans		close(fd);
1259cf8e5289SKyle Evans}
1260cf8e5289SKyle Evans
1261020d003cSKyle Evans]])
1262020d003cSKyle Evans
1263020d003cSKyle Evansfor _, def in pairs(tests) do
1264020d003cSKyle Evans	local func = def.func
1265020d003cSKyle Evans	local function write_tests(heap)
1266020d003cSKyle Evans		-- Dispositions here are relative to the buffer size prescribed
1267020d003cSKyle Evans		-- by the test definition.
1268020d003cSKyle Evans		local dispositions = def.dispositions or { -1, 0, 1 }
1269020d003cSKyle Evans
1270020d003cSKyle Evans		for _, disposition in ipairs(dispositions) do
1271020d003cSKyle Evans			tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
1272020d003cSKyle Evans		end
1273020d003cSKyle Evans	end
1274020d003cSKyle Evans
1275020d003cSKyle Evans	write_tests(false)
1276020d003cSKyle Evans	write_tests(true)
1277020d003cSKyle Evansend
1278020d003cSKyle Evans
1279020d003cSKyle Evansfh:write("ATF_TP_ADD_TCS(tp)\n")
1280020d003cSKyle Evansfh:write("{\n")
1281020d003cSKyle Evansfor idx = 1, #tests_added do
1282020d003cSKyle Evans	fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
1283020d003cSKyle Evansend
1284020d003cSKyle Evansfh:write("\treturn (atf_no_error());\n")
1285020d003cSKyle Evansfh:write("}\n")
1286