xref: /freebsd/lib/libc/tests/secure/generate-fortify-tests.lua (revision 1ace24b30cad59c8ae509d0c690aa55b8df73136)
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",
67020d003cSKyle Evans	"sys/time.h",
68*1ace24b3SKyle Evans	"sys/uio.h",
69020d003cSKyle Evans	"sys/wait.h",
70020d003cSKyle Evans	"dirent.h",
71020d003cSKyle Evans	"errno.h",
72020d003cSKyle Evans	"fcntl.h",
73020d003cSKyle Evans	"limits.h",
7488276dfbSKyle Evans	"poll.h",
75020d003cSKyle Evans	"signal.h",
76020d003cSKyle Evans	"stdio.h",
77020d003cSKyle Evans	"stdlib.h",
78020d003cSKyle Evans	"string.h",
79020d003cSKyle Evans	"strings.h",
80020d003cSKyle Evans	"sysexits.h",
81020d003cSKyle Evans	"unistd.h",
82b53d7aa8SKyle Evans	"wchar.h",
83020d003cSKyle Evans	"atf-c.h",
84020d003cSKyle Evans}
85020d003cSKyle Evans
86020d003cSKyle Evanslocal tests_added = {}
87020d003cSKyle Evans
88020d003cSKyle Evans-- Some of these will need to be excluded because clang sees the wrong size when
89020d003cSKyle Evans-- an array is embedded inside a struct, we'll get something that looks more
90020d003cSKyle Evans-- like __builtin_object_size(ptr, 0) than it does the correct
91020d003cSKyle Evans-- __builtin_object_size(ptr, 1) (i.e., includes the padding after).  This is
92020d003cSKyle Evans-- almost certainly a bug in llvm.
93020d003cSKyle Evanslocal function excludes_stack_overflow(disposition, is_heap)
94020d003cSKyle Evans	return (not is_heap) and disposition > 0
95020d003cSKyle Evansend
96020d003cSKyle Evans
9788276dfbSKyle Evanslocal poll_init = [[
9888276dfbSKyle Evans	for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
9988276dfbSKyle Evans		__stack.__buf[i].fd = -1;
10088276dfbSKyle Evans	}
10188276dfbSKyle Evans]]
10288276dfbSKyle Evans
103020d003cSKyle Evanslocal printf_stackvars = "\tchar srcvar[__len + 10];\n"
104020d003cSKyle Evanslocal printf_init = [[
105020d003cSKyle Evans	memset(srcvar, 'A', sizeof(srcvar) - 1);
106020d003cSKyle Evans	srcvar[sizeof(srcvar) - 1] = '\0';
107020d003cSKyle Evans]]
108020d003cSKyle Evans
109*1ace24b3SKyle Evanslocal readv_stackvars = "\tstruct iovec iov[1];\n"
110*1ace24b3SKyle Evanslocal readv_init = [[
111*1ace24b3SKyle Evans	iov[0].iov_base = __stack.__buf;
112*1ace24b3SKyle Evans	iov[0].iov_len = __len;
113*1ace24b3SKyle Evans
114*1ace24b3SKyle Evans	replace_stdin();
115*1ace24b3SKyle Evans]]
116*1ace24b3SKyle Evans
117cf8e5289SKyle Evanslocal stdio_init = [[
118cf8e5289SKyle Evans	replace_stdin();
119cf8e5289SKyle Evans]]
120cf8e5289SKyle Evans
121020d003cSKyle Evanslocal string_stackvars = "\tchar src[__len];\n"
122020d003cSKyle Evanslocal string_init = [[
123020d003cSKyle Evans	memset(__stack.__buf, 0, __len);
124020d003cSKyle Evans	memset(src, 'A', __len - 1);
125020d003cSKyle Evans	src[__len - 1] = '\0';
126020d003cSKyle Evans]]
127020d003cSKyle Evans
128b53d7aa8SKyle Evanslocal wstring_stackvars = "\twchar_t src[__len];\n"
129b53d7aa8SKyle Evanslocal wstring_init = [[
130b53d7aa8SKyle Evans	wmemset(__stack.__buf, 0, __len);
131b53d7aa8SKyle Evans	wmemset(src, 'A', __len - 1);
132b53d7aa8SKyle Evans	src[__len - 1] = '\0';
133b53d7aa8SKyle Evans]]
134b53d7aa8SKyle Evans
135020d003cSKyle Evans-- Each test entry describes how to test a given function.  We need to know how
136020d003cSKyle Evans-- to construct the buffer, we need to know the argument set we're dealing with,
137020d003cSKyle Evans-- and we need to know what we're passing to each argument.  We could be passing
138020d003cSKyle Evans-- fixed values, or we could be passing the __buf under test.
139020d003cSKyle Evans--
140020d003cSKyle Evans-- definition:
141020d003cSKyle Evans--   func: name of the function under test to call
142020d003cSKyle Evans--   bufsize: size of buffer to generate, defaults to 42
143020d003cSKyle Evans--   buftype: type of buffer to generate, defaults to unsigned char[]
144020d003cSKyle Evans--   arguments: __buf, __len, or the name of a variable placed on the stack
145020d003cSKyle Evans--   exclude: a function(disposition, is_heap) that returns true if this combo
146020d003cSKyle Evans--     should be excluded.
147020d003cSKyle Evans--   stackvars: extra variables to be placed on the stack, should be a string
148020d003cSKyle Evans--     optionally formatted with tabs and newlines
149020d003cSKyle Evans--   init: extra code to inject just before the function call for initialization
150020d003cSKyle Evans--     of the buffer or any of the above-added stackvars; also a string
151020d003cSKyle Evans--   uses_len: bool-ish, necessary if arguments doesn't include either __idx or
152020d003cSKyle Evans--     or __len so that the test generator doesn't try to vary the size of the
153020d003cSKyle Evans--     buffer instead of just manipulating __idx/__len to try and induce an
154020d003cSKyle Evans--     overflow.
155020d003cSKyle Evans--
156020d003cSKyle Evans-- Most tests will just use the default bufsize/buftype, but under some
157020d003cSKyle Evans-- circumstances it's useful to use a different type (e.g., for alignment
158020d003cSKyle Evans-- requirements).
159020d003cSKyle Evanslocal all_tests = {
160062d9380SKyle Evans	random = {
161062d9380SKyle Evans		-- <sys/random.h>
162062d9380SKyle Evans		{
163062d9380SKyle Evans			func = "getrandom",
164062d9380SKyle Evans			arguments = {
165062d9380SKyle Evans				"__buf",
166062d9380SKyle Evans				"__len",
167062d9380SKyle Evans				"0",
168062d9380SKyle Evans			},
169062d9380SKyle Evans			exclude = excludes_stack_overflow,
170062d9380SKyle Evans		},
171062d9380SKyle Evans	},
172*1ace24b3SKyle Evans	uio = {
173*1ace24b3SKyle Evans		-- <sys/uio.h>
174*1ace24b3SKyle Evans		{
175*1ace24b3SKyle Evans			func = "readv",
176*1ace24b3SKyle Evans			buftype = "struct iovec[]",
177*1ace24b3SKyle Evans			bufsize = 2,
178*1ace24b3SKyle Evans			arguments = {
179*1ace24b3SKyle Evans				"STDIN_FILENO",
180*1ace24b3SKyle Evans				"__buf",
181*1ace24b3SKyle Evans				"__len",
182*1ace24b3SKyle Evans			},
183*1ace24b3SKyle Evans		},
184*1ace24b3SKyle Evans		{
185*1ace24b3SKyle Evans			func = "readv",
186*1ace24b3SKyle Evans			variant = "iov",
187*1ace24b3SKyle Evans			arguments = {
188*1ace24b3SKyle Evans				"STDIN_FILENO",
189*1ace24b3SKyle Evans				"iov",
190*1ace24b3SKyle Evans				"nitems(iov)",
191*1ace24b3SKyle Evans			},
192*1ace24b3SKyle Evans			exclude = excludes_stack_overflow,
193*1ace24b3SKyle Evans			stackvars = readv_stackvars,
194*1ace24b3SKyle Evans			init = readv_init,
195*1ace24b3SKyle Evans			uses_len = true,
196*1ace24b3SKyle Evans		},
197*1ace24b3SKyle Evans		{
198*1ace24b3SKyle Evans			func = "preadv",
199*1ace24b3SKyle Evans			buftype = "struct iovec[]",
200*1ace24b3SKyle Evans			bufsize = 2,
201*1ace24b3SKyle Evans			arguments = {
202*1ace24b3SKyle Evans				"STDIN_FILENO",
203*1ace24b3SKyle Evans				"__buf",
204*1ace24b3SKyle Evans				"__len",
205*1ace24b3SKyle Evans				"0",
206*1ace24b3SKyle Evans			},
207*1ace24b3SKyle Evans		},
208*1ace24b3SKyle Evans		{
209*1ace24b3SKyle Evans			func = "preadv",
210*1ace24b3SKyle Evans			variant = "iov",
211*1ace24b3SKyle Evans			arguments = {
212*1ace24b3SKyle Evans				"STDIN_FILENO",
213*1ace24b3SKyle Evans				"iov",
214*1ace24b3SKyle Evans				"nitems(iov)",
215*1ace24b3SKyle Evans				"0",
216*1ace24b3SKyle Evans			},
217*1ace24b3SKyle Evans			exclude = excludes_stack_overflow,
218*1ace24b3SKyle Evans			stackvars = readv_stackvars,
219*1ace24b3SKyle Evans			init = readv_init,
220*1ace24b3SKyle Evans			uses_len = true,
221*1ace24b3SKyle Evans		},
222*1ace24b3SKyle Evans	},
22388276dfbSKyle Evans	poll = {
22488276dfbSKyle Evans		-- <poll.h>
22588276dfbSKyle Evans		{
22688276dfbSKyle Evans			func = "poll",
22788276dfbSKyle Evans			bufsize = "4",
22888276dfbSKyle Evans			buftype = "struct pollfd[]",
22988276dfbSKyle Evans			arguments = {
23088276dfbSKyle Evans				"__buf",
23188276dfbSKyle Evans				"__len",
23288276dfbSKyle Evans				"0",
23388276dfbSKyle Evans			},
23488276dfbSKyle Evans			init = poll_init,
23588276dfbSKyle Evans		},
23688276dfbSKyle Evans		{
23788276dfbSKyle Evans			func = "ppoll",
23888276dfbSKyle Evans			bufsize = "4",
23988276dfbSKyle Evans			buftype = "struct pollfd[]",
24088276dfbSKyle Evans			arguments = {
24188276dfbSKyle Evans				"__buf",
24288276dfbSKyle Evans				"__len",
24388276dfbSKyle Evans				"&tv",
24488276dfbSKyle Evans				"NULL",
24588276dfbSKyle Evans			},
24688276dfbSKyle Evans			stackvars = "\tstruct timespec tv = { 0 };\n",
24788276dfbSKyle Evans			init = poll_init,
24888276dfbSKyle Evans		},
24988276dfbSKyle Evans	},
250020d003cSKyle Evans	stdio = {
251020d003cSKyle Evans		-- <stdio.h>
252020d003cSKyle Evans		{
253cf8e5289SKyle Evans			func = "ctermid",
254cf8e5289SKyle Evans			bufsize = "L_ctermid",
255cf8e5289SKyle Evans			arguments = {
256cf8e5289SKyle Evans				"__buf",
257cf8e5289SKyle Evans			},
258cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
259cf8e5289SKyle Evans		},
260cf8e5289SKyle Evans		{
261cf8e5289SKyle Evans			func = "ctermid_r",
262cf8e5289SKyle Evans			bufsize = "L_ctermid",
263cf8e5289SKyle Evans			arguments = {
264cf8e5289SKyle Evans				"__buf",
265cf8e5289SKyle Evans			},
266cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
267cf8e5289SKyle Evans		},
268cf8e5289SKyle Evans		{
269cf8e5289SKyle Evans			func = "fread",
270cf8e5289SKyle Evans			arguments = {
271cf8e5289SKyle Evans				"__buf",
272cf8e5289SKyle Evans				"__len",
273cf8e5289SKyle Evans				"1",
274cf8e5289SKyle Evans				"stdin",
275cf8e5289SKyle Evans			},
276cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
277cf8e5289SKyle Evans			init = stdio_init,
278cf8e5289SKyle Evans		},
279cf8e5289SKyle Evans		{
280cf8e5289SKyle Evans			func = "fread_unlocked",
281cf8e5289SKyle Evans			arguments = {
282cf8e5289SKyle Evans				"__buf",
283cf8e5289SKyle Evans				"__len",
284cf8e5289SKyle Evans				"1",
285cf8e5289SKyle Evans				"stdin",
286cf8e5289SKyle Evans			},
287cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
288cf8e5289SKyle Evans			init = stdio_init,
289cf8e5289SKyle Evans		},
290cf8e5289SKyle Evans		{
291cf8e5289SKyle Evans			func = "gets_s",
292cf8e5289SKyle Evans			arguments = {
293cf8e5289SKyle Evans				"__buf",
294cf8e5289SKyle Evans				"__len",
295cf8e5289SKyle Evans			},
296cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
297cf8e5289SKyle Evans			init = stdio_init,
298cf8e5289SKyle Evans		},
299cf8e5289SKyle Evans		{
300020d003cSKyle Evans			func = "sprintf",
301020d003cSKyle Evans			arguments = {
302020d003cSKyle Evans				"__buf",
303020d003cSKyle Evans				"\"%.*s\"",
304020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
305020d003cSKyle Evans				"srcvar",
306020d003cSKyle Evans			},
307020d003cSKyle Evans			exclude = excludes_stack_overflow,
308020d003cSKyle Evans			stackvars = printf_stackvars,
309020d003cSKyle Evans			init = printf_init,
310020d003cSKyle Evans		},
311020d003cSKyle Evans		{
312020d003cSKyle Evans			func = "snprintf",
313020d003cSKyle Evans			arguments = {
314020d003cSKyle Evans				"__buf",
315020d003cSKyle Evans				"__len",
316020d003cSKyle Evans				"\"%.*s\"",
317020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
318020d003cSKyle Evans				"srcvar",
319020d003cSKyle Evans			},
320020d003cSKyle Evans			exclude = excludes_stack_overflow,
321020d003cSKyle Evans			stackvars = printf_stackvars,
322020d003cSKyle Evans			init = printf_init,
323020d003cSKyle Evans		},
324cf8e5289SKyle Evans		{
325cf8e5289SKyle Evans			func = "tmpnam",
326cf8e5289SKyle Evans			bufsize = "L_tmpnam",
327cf8e5289SKyle Evans			arguments = {
328cf8e5289SKyle Evans				"__buf",
329cf8e5289SKyle Evans			},
330cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
331cf8e5289SKyle Evans		},
332cf8e5289SKyle Evans		{
333cf8e5289SKyle Evans			func = "fgets",
334cf8e5289SKyle Evans			arguments = {
335cf8e5289SKyle Evans				"__buf",
336cf8e5289SKyle Evans				"__len",
337cf8e5289SKyle Evans				"fp",
338cf8e5289SKyle Evans			},
339cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
340cf8e5289SKyle Evans			stackvars = "\tFILE *fp;\n",
341cf8e5289SKyle Evans			init = [[
342cf8e5289SKyle Evans	fp = new_fp(__len);
343cf8e5289SKyle Evans]],
344cf8e5289SKyle Evans		},
345020d003cSKyle Evans	},
346d0b74459SKyle Evans	stdlib = {
347d0b74459SKyle Evans		-- <stdlib.h>
348d0b74459SKyle Evans		{
349d0b74459SKyle Evans			func = "arc4random_buf",
350d0b74459SKyle Evans			arguments = {
351d0b74459SKyle Evans				"__buf",
352d0b74459SKyle Evans				"__len",
353d0b74459SKyle Evans			},
354d0b74459SKyle Evans			exclude = excludes_stack_overflow,
355d0b74459SKyle Evans		},
356d0b74459SKyle Evans		{
357d0b74459SKyle Evans			func = "realpath",
358d0b74459SKyle Evans			bufsize = "PATH_MAX",
359d0b74459SKyle Evans			arguments = {
360d0b74459SKyle Evans				"\".\"",
361d0b74459SKyle Evans				"__buf",
362d0b74459SKyle Evans			},
363d0b74459SKyle Evans			exclude = excludes_stack_overflow,
364d0b74459SKyle Evans		},
365d0b74459SKyle Evans	},
366020d003cSKyle Evans	string = {
367020d003cSKyle Evans		-- <string.h>
368020d003cSKyle Evans		{
369020d003cSKyle Evans			func = "memcpy",
370020d003cSKyle Evans			arguments = {
371020d003cSKyle Evans				"__buf",
372020d003cSKyle Evans				"src",
373020d003cSKyle Evans				"__len",
374020d003cSKyle Evans			},
375020d003cSKyle Evans			exclude = excludes_stack_overflow,
376020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
377020d003cSKyle Evans		},
378020d003cSKyle Evans		{
379cf8e5289SKyle Evans			func = "mempcpy",
380cf8e5289SKyle Evans			arguments = {
381cf8e5289SKyle Evans				"__buf",
382cf8e5289SKyle Evans				"src",
383cf8e5289SKyle Evans				"__len",
384cf8e5289SKyle Evans			},
385cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
386cf8e5289SKyle Evans			stackvars = "\tchar src[__len + 10];\n",
387cf8e5289SKyle Evans		},
388cf8e5289SKyle Evans		{
389020d003cSKyle Evans			func = "memmove",
390020d003cSKyle Evans			arguments = {
391020d003cSKyle Evans				"__buf",
392020d003cSKyle Evans				"src",
393020d003cSKyle Evans				"__len",
394020d003cSKyle Evans			},
395020d003cSKyle Evans			exclude = excludes_stack_overflow,
396020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
397020d003cSKyle Evans		},
398020d003cSKyle Evans		{
399020d003cSKyle Evans			func = "memset",
400020d003cSKyle Evans			arguments = {
401020d003cSKyle Evans				"__buf",
402020d003cSKyle Evans				"0",
403020d003cSKyle Evans				"__len",
404020d003cSKyle Evans			},
405020d003cSKyle Evans			exclude = excludes_stack_overflow,
406020d003cSKyle Evans		},
407020d003cSKyle Evans		{
408020d003cSKyle Evans			func = "stpcpy",
409020d003cSKyle Evans			arguments = {
410020d003cSKyle Evans				"__buf",
411020d003cSKyle Evans				"src",
412020d003cSKyle Evans			},
413020d003cSKyle Evans			exclude = excludes_stack_overflow,
414020d003cSKyle Evans			stackvars = string_stackvars,
415020d003cSKyle Evans			init = string_init,
416020d003cSKyle Evans			uses_len = true,
417020d003cSKyle Evans		},
418020d003cSKyle Evans		{
419020d003cSKyle Evans			func = "stpncpy",
420020d003cSKyle Evans			arguments = {
421020d003cSKyle Evans				"__buf",
422020d003cSKyle Evans				"src",
423020d003cSKyle Evans				"__len",
424020d003cSKyle Evans			},
425020d003cSKyle Evans			exclude = excludes_stack_overflow,
426020d003cSKyle Evans			stackvars = string_stackvars,
427020d003cSKyle Evans			init = string_init,
428020d003cSKyle Evans		},
429020d003cSKyle Evans		{
430020d003cSKyle Evans			func = "strcat",
431020d003cSKyle Evans			arguments = {
432020d003cSKyle Evans				"__buf",
433020d003cSKyle Evans				"src",
434020d003cSKyle Evans			},
435020d003cSKyle Evans			exclude = excludes_stack_overflow,
436020d003cSKyle Evans			stackvars = string_stackvars,
437020d003cSKyle Evans			init = string_init,
438020d003cSKyle Evans			uses_len = true,
439020d003cSKyle Evans		},
440020d003cSKyle Evans		{
441cf8e5289SKyle Evans			func = "strlcat",
442cf8e5289SKyle Evans			arguments = {
443cf8e5289SKyle Evans				"__buf",
444cf8e5289SKyle Evans				"src",
445cf8e5289SKyle Evans				"__len",
446cf8e5289SKyle Evans			},
447cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
448cf8e5289SKyle Evans			stackvars = string_stackvars,
449cf8e5289SKyle Evans			init = string_init,
450cf8e5289SKyle Evans		},
451cf8e5289SKyle Evans		{
452020d003cSKyle Evans			func = "strncat",
453020d003cSKyle Evans			arguments = {
454020d003cSKyle Evans				"__buf",
455020d003cSKyle Evans				"src",
456020d003cSKyle Evans				"__len",
457020d003cSKyle Evans			},
458020d003cSKyle Evans			exclude = excludes_stack_overflow,
459020d003cSKyle Evans			stackvars = string_stackvars,
460020d003cSKyle Evans			init = string_init,
461020d003cSKyle Evans		},
462020d003cSKyle Evans		{
463020d003cSKyle Evans			func = "strcpy",
464020d003cSKyle Evans			arguments = {
465020d003cSKyle Evans				"__buf",
466020d003cSKyle Evans				"src",
467020d003cSKyle Evans			},
468020d003cSKyle Evans			exclude = excludes_stack_overflow,
469020d003cSKyle Evans			stackvars = string_stackvars,
470020d003cSKyle Evans			init = string_init,
471020d003cSKyle Evans			uses_len = true,
472020d003cSKyle Evans		},
473020d003cSKyle Evans		{
474cf8e5289SKyle Evans			func = "strlcpy",
475cf8e5289SKyle Evans			arguments = {
476cf8e5289SKyle Evans				"__buf",
477cf8e5289SKyle Evans				"src",
478cf8e5289SKyle Evans				"__len",
479cf8e5289SKyle Evans			},
480cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
481cf8e5289SKyle Evans			stackvars = string_stackvars,
482cf8e5289SKyle Evans			init = string_init,
483cf8e5289SKyle Evans		},
484cf8e5289SKyle Evans		{
485020d003cSKyle Evans			func = "strncpy",
486020d003cSKyle Evans			arguments = {
487020d003cSKyle Evans				"__buf",
488020d003cSKyle Evans				"src",
489020d003cSKyle Evans				"__len",
490020d003cSKyle Evans			},
491020d003cSKyle Evans			exclude = excludes_stack_overflow,
492020d003cSKyle Evans			stackvars = string_stackvars,
493020d003cSKyle Evans			init = string_init,
494020d003cSKyle Evans		},
495020d003cSKyle Evans	},
496020d003cSKyle Evans	strings = {
497020d003cSKyle Evans		-- <strings.h>
498020d003cSKyle Evans		{
499020d003cSKyle Evans			func = "bcopy",
500020d003cSKyle Evans			arguments = {
501020d003cSKyle Evans				"src",
502020d003cSKyle Evans				"__buf",
503020d003cSKyle Evans				"__len",
504020d003cSKyle Evans			},
505020d003cSKyle Evans			exclude = excludes_stack_overflow,
506020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
507020d003cSKyle Evans		},
508020d003cSKyle Evans		{
509020d003cSKyle Evans			func = "bzero",
510020d003cSKyle Evans			arguments = {
511020d003cSKyle Evans				"__buf",
512020d003cSKyle Evans				"__len",
513020d003cSKyle Evans			},
514020d003cSKyle Evans			exclude = excludes_stack_overflow,
515020d003cSKyle Evans		},
516cf8e5289SKyle Evans		{
517cf8e5289SKyle Evans			func = "explicit_bzero",
518cf8e5289SKyle Evans			arguments = {
519cf8e5289SKyle Evans				"__buf",
520cf8e5289SKyle Evans				"__len",
521cf8e5289SKyle Evans			},
522cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
523cf8e5289SKyle Evans		},
524020d003cSKyle Evans	},
525020d003cSKyle Evans	unistd = {
526020d003cSKyle Evans		-- <unistd.h>
527020d003cSKyle Evans		{
528020d003cSKyle Evans			func = "getcwd",
529020d003cSKyle Evans			bufsize = "8",
530020d003cSKyle Evans			arguments = {
531020d003cSKyle Evans				"__buf",
532020d003cSKyle Evans				"__len",
533020d003cSKyle Evans			},
534020d003cSKyle Evans			exclude = excludes_stack_overflow,
535020d003cSKyle Evans		},
536020d003cSKyle Evans		{
537cf8e5289SKyle Evans			func = "getgrouplist",
538cf8e5289SKyle Evans			bufsize = "4",
539cf8e5289SKyle Evans			buftype = "gid_t[]",
540cf8e5289SKyle Evans			arguments = {
541cf8e5289SKyle Evans				"\"root\"",
542cf8e5289SKyle Evans				"0",
543cf8e5289SKyle Evans				"__buf",
544cf8e5289SKyle Evans				"&intlen",
545cf8e5289SKyle Evans			},
546cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
547cf8e5289SKyle Evans			stackvars = "\tint intlen = (int)__len;\n",
548cf8e5289SKyle Evans			uses_len = true,
549cf8e5289SKyle Evans		},
550cf8e5289SKyle Evans		{
551cf8e5289SKyle Evans			func = "getgroups",
552cf8e5289SKyle Evans			bufsize = "4",
553cf8e5289SKyle Evans			buftype = "gid_t[]",
554cf8e5289SKyle Evans			arguments = {
555cf8e5289SKyle Evans				"__len",
556cf8e5289SKyle Evans				"__buf",
557cf8e5289SKyle Evans			},
558cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
559cf8e5289SKyle Evans		},
560cf8e5289SKyle Evans		{
561cf8e5289SKyle Evans			func = "getloginclass",
562cf8e5289SKyle Evans			arguments = {
563cf8e5289SKyle Evans				"__buf",
564cf8e5289SKyle Evans				"__len",
565cf8e5289SKyle Evans			},
566cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
567cf8e5289SKyle Evans		},
568cf8e5289SKyle Evans		{
569cf8e5289SKyle Evans			func = "pread",
570cf8e5289SKyle Evans			bufsize = "41",
571cf8e5289SKyle Evans			arguments = {
572cf8e5289SKyle Evans				"fd",
573cf8e5289SKyle Evans				"__buf",
574cf8e5289SKyle Evans				"__len",
575cf8e5289SKyle Evans				"0",
576cf8e5289SKyle Evans			},
577cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
578cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
579cf8e5289SKyle Evans			init = [[
580cf8e5289SKyle Evans	fd = new_tmpfile();	/* Cannot fail */
581cf8e5289SKyle Evans]],
582cf8e5289SKyle Evans		},
583cf8e5289SKyle Evans		{
584020d003cSKyle Evans			func = "read",
585020d003cSKyle Evans			bufsize = "41",
586020d003cSKyle Evans			arguments = {
587020d003cSKyle Evans				"fd",
588020d003cSKyle Evans				"__buf",
589020d003cSKyle Evans				"__len",
590020d003cSKyle Evans			},
591020d003cSKyle Evans			exclude = excludes_stack_overflow,
592020d003cSKyle Evans			stackvars = "\tint fd;\n",
593020d003cSKyle Evans			init = [[
594020d003cSKyle Evans	fd = new_tmpfile();	/* Cannot fail */
595020d003cSKyle Evans]],
596020d003cSKyle Evans		},
597020d003cSKyle Evans		{
598020d003cSKyle Evans			func = "readlink",
599020d003cSKyle Evans			arguments = {
600020d003cSKyle Evans				"path",
601020d003cSKyle Evans				"__buf",
602020d003cSKyle Evans				"__len",
603020d003cSKyle Evans			},
604020d003cSKyle Evans			exclude = excludes_stack_overflow,
605020d003cSKyle Evans			stackvars = "\tconst char *path;\n",
606020d003cSKyle Evans			init = [[
607020d003cSKyle Evans	path = new_symlink(__len);		/* Cannot fail */
608020d003cSKyle Evans]],
609020d003cSKyle Evans		},
610cf8e5289SKyle Evans		{
611cf8e5289SKyle Evans			func = "readlinkat",
612cf8e5289SKyle Evans			arguments = {
613cf8e5289SKyle Evans				"AT_FDCWD",
614cf8e5289SKyle Evans				"path",
615cf8e5289SKyle Evans				"__buf",
616cf8e5289SKyle Evans				"__len",
617cf8e5289SKyle Evans			},
618cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
619cf8e5289SKyle Evans			stackvars = "\tconst char *path;\n",
620cf8e5289SKyle Evans			init = [[
621cf8e5289SKyle Evans	path = new_symlink(__len);		/* Cannot fail */
622cf8e5289SKyle Evans]],
623cf8e5289SKyle Evans		},
624cf8e5289SKyle Evans		{
625cf8e5289SKyle Evans			func = "getdomainname",
626cf8e5289SKyle Evans			bufsize = "4",
627cf8e5289SKyle Evans			arguments = {
628cf8e5289SKyle Evans				"__buf",
629cf8e5289SKyle Evans				"__len",
630cf8e5289SKyle Evans			},
631cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
632cf8e5289SKyle Evans			stackvars = "\tchar sysdomain[256];\n",
633cf8e5289SKyle Evans			early_init = [[
634cf8e5289SKyle Evans	(void)getdomainname(sysdomain, __len);
635cf8e5289SKyle Evans	if (strlen(sysdomain) <= __len)
636cf8e5289SKyle Evans		atf_tc_skip("domain name too short for testing");
637cf8e5289SKyle Evans]]
638cf8e5289SKyle Evans		},
639cf8e5289SKyle Evans		{
640cf8e5289SKyle Evans			func = "getentropy",
641cf8e5289SKyle Evans			arguments = {
642cf8e5289SKyle Evans				"__buf",
643cf8e5289SKyle Evans				"__len",
644cf8e5289SKyle Evans			},
645cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
646cf8e5289SKyle Evans		},
647cf8e5289SKyle Evans		{
648cf8e5289SKyle Evans			func = "gethostname",
649cf8e5289SKyle Evans			bufsize = "4",
650cf8e5289SKyle Evans			arguments = {
651cf8e5289SKyle Evans				"__buf",
652cf8e5289SKyle Evans				"__len",
653cf8e5289SKyle Evans			},
654cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
655cf8e5289SKyle Evans			stackvars = [[
656cf8e5289SKyle Evans	char syshost[256];
657cf8e5289SKyle Evans	int error;
658cf8e5289SKyle Evans]],
659cf8e5289SKyle Evans			early_init = [[
660cf8e5289SKyle Evans	error = gethostname(syshost, __len);
661cf8e5289SKyle Evans	if (error != 0 || strlen(syshost) <= __len)
662cf8e5289SKyle Evans		atf_tc_skip("hostname too short for testing");
663cf8e5289SKyle Evans]]
664cf8e5289SKyle Evans		},
665cf8e5289SKyle Evans		{
666cf8e5289SKyle Evans			func = "getlogin_r",
667cf8e5289SKyle Evans			bufsize = "MAXLOGNAME + 1",
668cf8e5289SKyle Evans			arguments = {
669cf8e5289SKyle Evans				"__buf",
670cf8e5289SKyle Evans				"__len",
671cf8e5289SKyle Evans			},
672cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
673cf8e5289SKyle Evans		},
674cf8e5289SKyle Evans		{
675cf8e5289SKyle Evans			func = "ttyname_r",
676cf8e5289SKyle Evans			arguments = {
677cf8e5289SKyle Evans				"fd",
678cf8e5289SKyle Evans				"__buf",
679cf8e5289SKyle Evans				"__len",
680cf8e5289SKyle Evans			},
681cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
682cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
683cf8e5289SKyle Evans			early_init = [[
684cf8e5289SKyle Evans	fd = STDIN_FILENO;
685cf8e5289SKyle Evans	if (!isatty(fd))
686cf8e5289SKyle Evans		atf_tc_skip("stdin is not an fd");
687cf8e5289SKyle Evans]]
688cf8e5289SKyle Evans		},
689020d003cSKyle Evans	},
690b53d7aa8SKyle Evans	wchar = {
691b53d7aa8SKyle Evans		-- <wchar.h>
692b53d7aa8SKyle Evans		{
693b53d7aa8SKyle Evans			func = "wmemcpy",
694b53d7aa8SKyle Evans			buftype = "wchar_t[]",
695b53d7aa8SKyle Evans			arguments = {
696b53d7aa8SKyle Evans				"__buf",
697b53d7aa8SKyle Evans				"src",
698b53d7aa8SKyle Evans				"__len",
699b53d7aa8SKyle Evans			},
700b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
701b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
702b53d7aa8SKyle Evans		},
703b53d7aa8SKyle Evans		{
704b53d7aa8SKyle Evans			func = "wmempcpy",
705b53d7aa8SKyle Evans			buftype = "wchar_t[]",
706b53d7aa8SKyle Evans			arguments = {
707b53d7aa8SKyle Evans				"__buf",
708b53d7aa8SKyle Evans				"src",
709b53d7aa8SKyle Evans				"__len",
710b53d7aa8SKyle Evans			},
711b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
712b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
713b53d7aa8SKyle Evans		},
714b53d7aa8SKyle Evans		{
715b53d7aa8SKyle Evans			func = "wmemmove",
716b53d7aa8SKyle Evans			buftype = "wchar_t[]",
717b53d7aa8SKyle Evans			arguments = {
718b53d7aa8SKyle Evans				"__buf",
719b53d7aa8SKyle Evans				"src",
720b53d7aa8SKyle Evans				"__len",
721b53d7aa8SKyle Evans			},
722b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
723b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
724b53d7aa8SKyle Evans		},
725b53d7aa8SKyle Evans		{
726b53d7aa8SKyle Evans			func = "wmemset",
727b53d7aa8SKyle Evans			buftype = "wchar_t[]",
728b53d7aa8SKyle Evans			arguments = {
729b53d7aa8SKyle Evans				"__buf",
730b53d7aa8SKyle Evans				"L'0'",
731b53d7aa8SKyle Evans				"__len",
732b53d7aa8SKyle Evans			},
733b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
734b53d7aa8SKyle Evans		},
735b53d7aa8SKyle Evans		{
736b53d7aa8SKyle Evans			func = "wcpcpy",
737b53d7aa8SKyle Evans			buftype = "wchar_t[]",
738b53d7aa8SKyle Evans			arguments = {
739b53d7aa8SKyle Evans				"__buf",
740b53d7aa8SKyle Evans				"src",
741b53d7aa8SKyle Evans			},
742b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
743b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
744b53d7aa8SKyle Evans			init = wstring_init,
745b53d7aa8SKyle Evans			uses_len = true,
746b53d7aa8SKyle Evans		},
747b53d7aa8SKyle Evans		{
748b53d7aa8SKyle Evans			func = "wcpncpy",
749b53d7aa8SKyle Evans			buftype = "wchar_t[]",
750b53d7aa8SKyle Evans			arguments = {
751b53d7aa8SKyle Evans				"__buf",
752b53d7aa8SKyle Evans				"src",
753b53d7aa8SKyle Evans				"__len",
754b53d7aa8SKyle Evans			},
755b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
756b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
757b53d7aa8SKyle Evans			init = wstring_init,
758b53d7aa8SKyle Evans		},
759b53d7aa8SKyle Evans		{
760b53d7aa8SKyle Evans			func = "wcscat",
761b53d7aa8SKyle Evans			buftype = "wchar_t[]",
762b53d7aa8SKyle Evans			arguments = {
763b53d7aa8SKyle Evans				"__buf",
764b53d7aa8SKyle Evans				"src",
765b53d7aa8SKyle Evans			},
766b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
767b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
768b53d7aa8SKyle Evans			init = wstring_init,
769b53d7aa8SKyle Evans			uses_len = true,
770b53d7aa8SKyle Evans		},
771b53d7aa8SKyle Evans		{
772b53d7aa8SKyle Evans			func = "wcslcat",
773b53d7aa8SKyle Evans			buftype = "wchar_t[]",
774b53d7aa8SKyle Evans			arguments = {
775b53d7aa8SKyle Evans				"__buf",
776b53d7aa8SKyle Evans				"src",
777b53d7aa8SKyle Evans				"__len",
778b53d7aa8SKyle Evans			},
779b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
780b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
781b53d7aa8SKyle Evans			init = wstring_init,
782b53d7aa8SKyle Evans		},
783b53d7aa8SKyle Evans		{
784b53d7aa8SKyle Evans			func = "wcsncat",
785b53d7aa8SKyle Evans			buftype = "wchar_t[]",
786b53d7aa8SKyle Evans			arguments = {
787b53d7aa8SKyle Evans				"__buf",
788b53d7aa8SKyle Evans				"src",
789b53d7aa8SKyle Evans				"__len",
790b53d7aa8SKyle Evans			},
791b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
792b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
793b53d7aa8SKyle Evans			init = wstring_init,
794b53d7aa8SKyle Evans		},
795b53d7aa8SKyle Evans		{
796b53d7aa8SKyle Evans			func = "wcscpy",
797b53d7aa8SKyle Evans			buftype = "wchar_t[]",
798b53d7aa8SKyle Evans			arguments = {
799b53d7aa8SKyle Evans				"__buf",
800b53d7aa8SKyle Evans				"src",
801b53d7aa8SKyle Evans			},
802b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
803b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
804b53d7aa8SKyle Evans			init = wstring_init,
805b53d7aa8SKyle Evans			uses_len = true,
806b53d7aa8SKyle Evans		},
807b53d7aa8SKyle Evans		{
808b53d7aa8SKyle Evans			func = "wcslcpy",
809b53d7aa8SKyle Evans			buftype = "wchar_t[]",
810b53d7aa8SKyle Evans			arguments = {
811b53d7aa8SKyle Evans				"__buf",
812b53d7aa8SKyle Evans				"src",
813b53d7aa8SKyle Evans				"__len",
814b53d7aa8SKyle Evans			},
815b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
816b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
817b53d7aa8SKyle Evans			init = wstring_init,
818b53d7aa8SKyle Evans		},
819b53d7aa8SKyle Evans		{
820b53d7aa8SKyle Evans			func = "wcsncpy",
821b53d7aa8SKyle Evans			buftype = "wchar_t[]",
822b53d7aa8SKyle Evans			arguments = {
823b53d7aa8SKyle Evans				"__buf",
824b53d7aa8SKyle Evans				"src",
825b53d7aa8SKyle Evans				"__len",
826b53d7aa8SKyle Evans			},
827b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
828b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
829b53d7aa8SKyle Evans			init = wstring_init,
830b53d7aa8SKyle Evans		},
831b53d7aa8SKyle Evans	},
832020d003cSKyle Evans}
833020d003cSKyle Evans
834020d003cSKyle Evanslocal function write_test_boilerplate(fh, name, body)
835020d003cSKyle Evans	fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
836020d003cSKyle Evans	fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
837020d003cSKyle Evans	fh:write("{\n" .. body .. "\n}\n\n")
838020d003cSKyle Evans	return name
839020d003cSKyle Evansend
840020d003cSKyle Evans
841020d003cSKyle Evanslocal function generate_test_name(func, variant, disposition, heap)
842020d003cSKyle Evans	local basename = func
843020d003cSKyle Evans	if variant then
844020d003cSKyle Evans		basename = basename .. "_" .. variant
845020d003cSKyle Evans	end
846020d003cSKyle Evans	if heap then
847020d003cSKyle Evans		basename = basename .. "_heap"
848020d003cSKyle Evans	end
849020d003cSKyle Evans	if disposition < 0 then
850020d003cSKyle Evans		return basename .. "_before_end"
851020d003cSKyle Evans	elseif disposition == 0 then
852020d003cSKyle Evans		return basename .. "_end"
853020d003cSKyle Evans	else
854020d003cSKyle Evans		return basename .. "_after_end"
855020d003cSKyle Evans	end
856020d003cSKyle Evansend
857020d003cSKyle Evans
858020d003cSKyle Evanslocal function array_type(buftype)
859020d003cSKyle Evans	if not buftype:match("%[%]") then
860020d003cSKyle Evans		return nil
861020d003cSKyle Evans	end
862020d003cSKyle Evans
863020d003cSKyle Evans	return buftype:gsub("%[%]", "")
864020d003cSKyle Evansend
865020d003cSKyle Evans
866020d003cSKyle Evanslocal function configurable(def, idx)
867020d003cSKyle Evans	local cfgitem = def[idx]
868020d003cSKyle Evans
869020d003cSKyle Evans	if not cfgitem then
870020d003cSKyle Evans		return nil
871020d003cSKyle Evans	end
872020d003cSKyle Evans
873020d003cSKyle Evans	if type(cfgitem) == "function" then
874020d003cSKyle Evans		return cfgitem()
875020d003cSKyle Evans	end
876020d003cSKyle Evans
877020d003cSKyle Evans	return cfgitem
878020d003cSKyle Evansend
879020d003cSKyle Evans
880020d003cSKyle Evanslocal function generate_stackframe(buftype, bufsize, disposition, heap, def)
881020d003cSKyle Evans	local function len_offset(inverted, disposition)
882020d003cSKyle Evans		-- Tests that don't use __len in their arguments may use an
883020d003cSKyle Evans		-- inverted sense because we can't just specify a length that
884020d003cSKyle Evans		-- would induce an access just after the end.  Instead, we have
885020d003cSKyle Evans		-- to manipulate the buffer size to be too short so that the
886020d003cSKyle Evans		-- function under test would write one too many.
887020d003cSKyle Evans		if disposition < 0 then
888020d003cSKyle Evans			return ((inverted and " + ") or " - ") .. "1"
889020d003cSKyle Evans		elseif disposition == 0 then
890020d003cSKyle Evans			return ""
891020d003cSKyle Evans		else
892020d003cSKyle Evans			return ((inverted and " - ") or " + ") .. "1"
893020d003cSKyle Evans		end
894020d003cSKyle Evans	end
895020d003cSKyle Evans
896020d003cSKyle Evans	local function test_uses_len(def)
897020d003cSKyle Evans		if def.uses_len then
898020d003cSKyle Evans			return true
899020d003cSKyle Evans		end
900020d003cSKyle Evans
901020d003cSKyle Evans		for _, arg in ipairs(def.arguments) do
902020d003cSKyle Evans			if arg:match("__len") or arg:match("__idx") then
903020d003cSKyle Evans				return true
904020d003cSKyle Evans			end
905020d003cSKyle Evans		end
906020d003cSKyle Evans
907020d003cSKyle Evans		return false
908020d003cSKyle Evans	end
909020d003cSKyle Evans
910020d003cSKyle Evans
911020d003cSKyle Evans	-- This is perhaps a little convoluted, but we toss the buffer into a
912020d003cSKyle Evans	-- struct on the stack to guarantee that we have at least one valid
913020d003cSKyle Evans	-- byte on either side of the buffer -- a measure to make sure that
914020d003cSKyle Evans	-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
915020d003cSKyle Evans	-- rather than some other stack or memory protection.
916020d003cSKyle Evans	local vars = "\tstruct {\n"
917020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_l;\n"
918020d003cSKyle Evans
919020d003cSKyle Evans	local uses_len = test_uses_len(def)
920020d003cSKyle Evans	local bufsize_offset = len_offset(not uses_len, disposition)
921020d003cSKyle Evans	local buftype_elem = array_type(buftype)
922020d003cSKyle Evans	local size_expr = bufsize
923020d003cSKyle Evans
924020d003cSKyle Evans	if not uses_len then
925020d003cSKyle Evans		-- If the length isn't in use, we have to vary the buffer size
926020d003cSKyle Evans		-- since the fortified function likely has some internal size
927020d003cSKyle Evans		-- constraint that it's supposed to be checking.
928020d003cSKyle Evans		size_expr = size_expr .. bufsize_offset
929020d003cSKyle Evans	end
930020d003cSKyle Evans
931020d003cSKyle Evans	if not heap and buftype_elem then
932020d003cSKyle Evans		-- Array type: size goes after identifier
933020d003cSKyle Evans		vars = vars .. "\t\t" .. buftype_elem ..
934020d003cSKyle Evans		    " __buf[" .. size_expr .. "];\n"
935020d003cSKyle Evans	else
936020d003cSKyle Evans		local basic_type = buftype_elem or buftype
937020d003cSKyle Evans
938020d003cSKyle Evans		-- Heap tests obviously just put a pointer on the stack that
939020d003cSKyle Evans		-- points to our new allocation, but we leave it in the padded
940020d003cSKyle Evans		-- struct just to simplify our generator.
941020d003cSKyle Evans		if heap then
942020d003cSKyle Evans			basic_type = basic_type .. " *"
943020d003cSKyle Evans		end
944020d003cSKyle Evans		vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
945020d003cSKyle Evans	end
946020d003cSKyle Evans
947020d003cSKyle Evans	-- padding_r is our just-past-the-end padding that we use to make sure
948020d003cSKyle Evans	-- that there's a valid portion after the buffer that isn't being
949020d003cSKyle Evans	-- included in our function calls.  If we didn't have it, then we'd have
950020d003cSKyle Evans	-- a hard time feeling confident that an abort on the just-after tests
951020d003cSKyle Evans	-- isn't maybe from some other memory or stack protection.
952020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_r;\n"
953020d003cSKyle Evans	vars = vars .. "\t} __stack;\n"
954020d003cSKyle Evans
955020d003cSKyle Evans	-- Not all tests will use __bufsz, but some do for, e.g., clearing
956020d003cSKyle Evans	-- memory..
957020d003cSKyle Evans	vars = vars .. "\tconst size_t __bufsz __unused = "
958020d003cSKyle Evans	if heap then
959020d003cSKyle Evans		local scalar = 1
960020d003cSKyle Evans		if buftype_elem then
961020d003cSKyle Evans			scalar = size_expr
962020d003cSKyle Evans		end
963020d003cSKyle Evans
964020d003cSKyle Evans		vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
965020d003cSKyle Evans	else
966020d003cSKyle Evans		vars = vars .. "sizeof(__stack.__buf);\n"
967020d003cSKyle Evans	end
968020d003cSKyle Evans
969020d003cSKyle Evans	vars = vars .. "\tconst size_t __len = " .. bufsize ..
970020d003cSKyle Evans	    bufsize_offset .. ";\n"
971020d003cSKyle Evans	vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
972020d003cSKyle Evans
973020d003cSKyle Evans	-- For overflow testing, we need to fork() because we're expecting the
974020d003cSKyle Evans	-- test to ultimately abort()/_exit().  Then we can collect the exit
975020d003cSKyle Evans	-- status and report appropriately.
976020d003cSKyle Evans	if disposition > 0 then
977020d003cSKyle Evans		vars = vars .. "\tpid_t __child;\n"
978020d003cSKyle Evans		vars = vars .. "\tint __status;\n"
979020d003cSKyle Evans	end
980020d003cSKyle Evans
981020d003cSKyle Evans	-- Any other stackvars defined by the test get placed after everything
982020d003cSKyle Evans	-- else.
983020d003cSKyle Evans	vars = vars .. (configurable(def, "stackvars") or "")
984020d003cSKyle Evans
985020d003cSKyle Evans	return vars
986020d003cSKyle Evansend
987020d003cSKyle Evans
988020d003cSKyle Evanslocal function write_test(fh, func, disposition, heap, def)
989020d003cSKyle Evans	local testname = generate_test_name(func, def.variant, disposition, heap)
990020d003cSKyle Evans	local buftype = def.buftype or "unsigned char[]"
991020d003cSKyle Evans	local bufsize = def.bufsize or 42
992020d003cSKyle Evans	local body = ""
993020d003cSKyle Evans
994020d003cSKyle Evans	if def.exclude and def.exclude(disposition, heap) then
995020d003cSKyle Evans		return
996020d003cSKyle Evans	end
997020d003cSKyle Evans
998020d003cSKyle Evans	local function need_addr(buftype)
999020d003cSKyle Evans		return not (buftype:match("%[%]") or buftype:match("%*"))
1000020d003cSKyle Evans	end
1001020d003cSKyle Evans
1002020d003cSKyle Evans	if heap then
1003020d003cSKyle Evans		body = body .. "#define BUF __stack.__buf\n"
1004020d003cSKyle Evans	else
1005020d003cSKyle Evans		body = body .. "#define BUF &__stack.__buf\n"
1006020d003cSKyle Evans	end
1007020d003cSKyle Evans
1008020d003cSKyle Evans	-- Setup the buffer
1009020d003cSKyle Evans	body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
1010020d003cSKyle Evans	    "\n"
1011020d003cSKyle Evans
1012020d003cSKyle Evans	-- Any early initialization goes before we would fork for the just-after
1013020d003cSKyle Evans	-- tests, because they may want to skip the test based on some criteria
1014020d003cSKyle Evans	-- and we can't propagate that up very easily once we're forked.
1015020d003cSKyle Evans	local early_init = configurable(def, "early_init")
1016020d003cSKyle Evans	body = body .. (early_init or "")
1017020d003cSKyle Evans	if early_init then
1018020d003cSKyle Evans		body = body .. "\n"
1019020d003cSKyle Evans	end
1020020d003cSKyle Evans
1021020d003cSKyle Evans	-- Fork off, iff we're testing some access past the end of the buffer.
1022020d003cSKyle Evans	if disposition > 0 then
1023020d003cSKyle Evans		body = body .. [[
1024020d003cSKyle Evans	__child = fork();
1025020d003cSKyle Evans	ATF_REQUIRE(__child >= 0);
1026020d003cSKyle Evans	if (__child > 0)
1027020d003cSKyle Evans		goto monitor;
1028020d003cSKyle Evans
1029020d003cSKyle Evans	/* Child */
1030020d003cSKyle Evans	disable_coredumps();
1031020d003cSKyle Evans]]
1032020d003cSKyle Evans	end
1033020d003cSKyle Evans
1034020d003cSKyle Evans	local bufvar = "__stack.__buf"
1035020d003cSKyle Evans	if heap then
1036020d003cSKyle Evans		-- Buffer needs to be initialized because it's a heap allocation.
1037020d003cSKyle Evans		body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
1038020d003cSKyle Evans	end
1039020d003cSKyle Evans
1040020d003cSKyle Evans	-- Non-early init happens just after the fork in the child, not the
1041020d003cSKyle Evans	-- monitor.  This is used to setup any other buffers we may need, for
1042020d003cSKyle Evans	-- instance.
1043020d003cSKyle Evans	local extra_init = configurable(def, "init")
1044020d003cSKyle Evans	body = body .. (extra_init or "")
1045020d003cSKyle Evans
1046020d003cSKyle Evans	if heap or extra_init then
1047020d003cSKyle Evans		body = body .. "\n"
1048020d003cSKyle Evans	end
1049020d003cSKyle Evans
1050020d003cSKyle Evans	-- Setup the function call with arguments as described in the test
1051020d003cSKyle Evans	-- definition.
1052020d003cSKyle Evans	body = body .. "\t" .. func .. "("
1053020d003cSKyle Evans
1054020d003cSKyle Evans	for idx, arg in ipairs(def.arguments) do
1055020d003cSKyle Evans		if idx > 1 then
1056020d003cSKyle Evans			body = body .. ", "
1057020d003cSKyle Evans		end
1058020d003cSKyle Evans
1059020d003cSKyle Evans		if arg == "__buf" then
1060020d003cSKyle Evans			if not heap and need_addr(buftype) then
1061020d003cSKyle Evans				body = body .. "&"
1062020d003cSKyle Evans			end
1063020d003cSKyle Evans
1064020d003cSKyle Evans			body = body .. bufvar
1065020d003cSKyle Evans		else
1066020d003cSKyle Evans			local argname = arg
1067020d003cSKyle Evans
1068020d003cSKyle Evans			if def.value_of then
1069020d003cSKyle Evans				argname = argname or def.value_of(arg)
1070020d003cSKyle Evans			end
1071020d003cSKyle Evans
1072020d003cSKyle Evans			body = body .. argname
1073020d003cSKyle Evans		end
1074020d003cSKyle Evans	end
1075020d003cSKyle Evans
1076020d003cSKyle Evans	body = body .. ");\n"
1077020d003cSKyle Evans
1078020d003cSKyle Evans	-- Monitor stuff follows, for OOB access.
1079020d003cSKyle Evans	if disposition <= 0 then
1080020d003cSKyle Evans		goto skip
1081020d003cSKyle Evans	end
1082020d003cSKyle Evans
1083020d003cSKyle Evans	body = body .. [[
1084020d003cSKyle Evans	_exit(EX_SOFTWARE);	/* Should have aborted. */
1085020d003cSKyle Evans
1086020d003cSKyle Evansmonitor:
1087020d003cSKyle Evans	while (waitpid(__child, &__status, 0) != __child) {
1088020d003cSKyle Evans		ATF_REQUIRE_EQ(EINTR, errno);
1089020d003cSKyle Evans	}
1090020d003cSKyle Evans
1091020d003cSKyle Evans	if (!WIFSIGNALED(__status)) {
1092020d003cSKyle Evans		switch (WEXITSTATUS(__status)) {
1093020d003cSKyle Evans		case EX_SOFTWARE:
1094020d003cSKyle Evans			atf_tc_fail("FORTIFY_SOURCE failed to abort");
1095020d003cSKyle Evans			break;
1096020d003cSKyle Evans		case EX_OSERR:
1097020d003cSKyle Evans			atf_tc_fail("setrlimit(2) failed");
1098020d003cSKyle Evans			break;
1099020d003cSKyle Evans		default:
1100020d003cSKyle Evans			atf_tc_fail("child exited with status %d",
1101020d003cSKyle Evans			    WEXITSTATUS(__status));
1102020d003cSKyle Evans		}
1103020d003cSKyle Evans	} else {
1104020d003cSKyle Evans		ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
1105020d003cSKyle Evans	}
1106020d003cSKyle Evans]]
1107020d003cSKyle Evans
1108020d003cSKyle Evans::skip::
1109020d003cSKyle Evans	body = body .. "#undef BUF\n"
1110020d003cSKyle Evans	return write_test_boilerplate(fh, testname, body)
1111020d003cSKyle Evansend
1112020d003cSKyle Evans
1113020d003cSKyle Evans-- main()
1114020d003cSKyle Evanslocal tests
1115020d003cSKyle Evanslocal tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
1116020d003cSKyle Evansfor k, defs in pairs(all_tests) do
1117020d003cSKyle Evans	if k == tcat then
1118020d003cSKyle Evans		tests = defs
1119020d003cSKyle Evans		break
1120020d003cSKyle Evans	end
1121020d003cSKyle Evansend
1122020d003cSKyle Evans
1123020d003cSKyle Evansassert(tests, "category " .. tcat .. " not found")
1124020d003cSKyle Evans
1125020d003cSKyle Evanslocal fh = io.stdout
1126020d003cSKyle Evansfh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
1127020d003cSKyle Evans    tcat .. "\"` */\n\n")
1128020d003cSKyle Evansfh:write("#define	_FORTIFY_SOURCE	2\n")
1129020d003cSKyle Evansfh:write("#define	TMPFILE_SIZE	(1024 * 32)\n")
1130020d003cSKyle Evans
1131020d003cSKyle Evansfh:write("\n")
1132020d003cSKyle Evansfor _, inc in ipairs(includes) do
1133020d003cSKyle Evans	fh:write("#include <" .. inc .. ">\n")
1134020d003cSKyle Evansend
1135020d003cSKyle Evans
1136020d003cSKyle Evansfh:write([[
1137020d003cSKyle Evans
1138cf8e5289SKyle Evansstatic FILE * __unused
1139cf8e5289SKyle Evansnew_fp(size_t __len)
1140cf8e5289SKyle Evans{
1141cf8e5289SKyle Evans	static char fpbuf[LINE_MAX];
1142cf8e5289SKyle Evans	FILE *fp;
1143cf8e5289SKyle Evans
1144cf8e5289SKyle Evans	ATF_REQUIRE(__len <= sizeof(fpbuf));
1145cf8e5289SKyle Evans
1146cf8e5289SKyle Evans	memset(fpbuf, 'A', sizeof(fpbuf) - 1);
1147cf8e5289SKyle Evans	fpbuf[sizeof(fpbuf) - 1] = '\0';
1148cf8e5289SKyle Evans
1149cf8e5289SKyle Evans	fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
1150cf8e5289SKyle Evans	ATF_REQUIRE(fp != NULL);
1151cf8e5289SKyle Evans
1152cf8e5289SKyle Evans	return (fp);
1153cf8e5289SKyle Evans}
1154cf8e5289SKyle Evans
1155020d003cSKyle Evans/*
1156020d003cSKyle Evans * Create a new symlink to use for readlink(2) style tests, we'll just use a
1157020d003cSKyle Evans * random target name to have something interesting to look at.
1158020d003cSKyle Evans */
1159020d003cSKyle Evansstatic const char * __unused
1160020d003cSKyle Evansnew_symlink(size_t __len)
1161020d003cSKyle Evans{
1162020d003cSKyle Evans	static const char linkname[] = "link";
1163020d003cSKyle Evans	char target[MAXNAMLEN];
1164020d003cSKyle Evans	int error;
1165020d003cSKyle Evans
1166020d003cSKyle Evans	ATF_REQUIRE(__len <= sizeof(target));
1167020d003cSKyle Evans
1168020d003cSKyle Evans	arc4random_buf(target, sizeof(target));
1169020d003cSKyle Evans
1170020d003cSKyle Evans	error = unlink(linkname);
1171020d003cSKyle Evans	ATF_REQUIRE(error == 0 || errno == ENOENT);
1172020d003cSKyle Evans
1173020d003cSKyle Evans	error = symlink(target, linkname);
1174020d003cSKyle Evans	ATF_REQUIRE(error == 0);
1175020d003cSKyle Evans
1176020d003cSKyle Evans	return (linkname);
1177020d003cSKyle Evans}
1178020d003cSKyle Evans
1179020d003cSKyle Evans/*
1180020d003cSKyle Evans * Constructs a tmpfile that we can use for testing read(2) and friends.
1181020d003cSKyle Evans */
1182020d003cSKyle Evansstatic int __unused
1183020d003cSKyle Evansnew_tmpfile(void)
1184020d003cSKyle Evans{
1185020d003cSKyle Evans	char buf[1024];
1186020d003cSKyle Evans	ssize_t rv;
1187020d003cSKyle Evans	size_t written;
1188020d003cSKyle Evans	int fd;
1189020d003cSKyle Evans
1190020d003cSKyle Evans	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
1191020d003cSKyle Evans	ATF_REQUIRE(fd >= 0);
1192020d003cSKyle Evans
1193020d003cSKyle Evans	written = 0;
1194020d003cSKyle Evans	while (written < TMPFILE_SIZE) {
1195020d003cSKyle Evans		rv = write(fd, buf, sizeof(buf));
1196020d003cSKyle Evans		ATF_REQUIRE(rv > 0);
1197020d003cSKyle Evans
1198020d003cSKyle Evans		written += rv;
1199020d003cSKyle Evans	}
1200020d003cSKyle Evans
1201020d003cSKyle Evans	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
1202020d003cSKyle Evans	return (fd);
1203020d003cSKyle Evans}
1204020d003cSKyle Evans
1205020d003cSKyle Evansstatic void
1206020d003cSKyle Evansdisable_coredumps(void)
1207020d003cSKyle Evans{
1208020d003cSKyle Evans	struct rlimit rl = { 0 };
1209020d003cSKyle Evans
1210020d003cSKyle Evans	if (setrlimit(RLIMIT_CORE, &rl) == -1)
1211020d003cSKyle Evans		_exit(EX_OSERR);
1212020d003cSKyle Evans}
1213020d003cSKyle Evans
1214cf8e5289SKyle Evans/*
1215cf8e5289SKyle Evans * Replaces stdin with a file that we can actually read from, for tests where
1216cf8e5289SKyle Evans * we want a FILE * or fd that we can get data from.
1217cf8e5289SKyle Evans */
1218cf8e5289SKyle Evansstatic void __unused
1219cf8e5289SKyle Evansreplace_stdin(void)
1220cf8e5289SKyle Evans{
1221cf8e5289SKyle Evans	int fd;
1222cf8e5289SKyle Evans
1223cf8e5289SKyle Evans	fd = new_tmpfile();
1224cf8e5289SKyle Evans
1225cf8e5289SKyle Evans	(void)dup2(fd, STDIN_FILENO);
1226cf8e5289SKyle Evans	if (fd != STDIN_FILENO)
1227cf8e5289SKyle Evans		close(fd);
1228cf8e5289SKyle Evans}
1229cf8e5289SKyle Evans
1230020d003cSKyle Evans]])
1231020d003cSKyle Evans
1232020d003cSKyle Evansfor _, def in pairs(tests) do
1233020d003cSKyle Evans	local func = def.func
1234020d003cSKyle Evans	local function write_tests(heap)
1235020d003cSKyle Evans		-- Dispositions here are relative to the buffer size prescribed
1236020d003cSKyle Evans		-- by the test definition.
1237020d003cSKyle Evans		local dispositions = def.dispositions or { -1, 0, 1 }
1238020d003cSKyle Evans
1239020d003cSKyle Evans		for _, disposition in ipairs(dispositions) do
1240020d003cSKyle Evans			tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
1241020d003cSKyle Evans		end
1242020d003cSKyle Evans	end
1243020d003cSKyle Evans
1244020d003cSKyle Evans	write_tests(false)
1245020d003cSKyle Evans	write_tests(true)
1246020d003cSKyle Evansend
1247020d003cSKyle Evans
1248020d003cSKyle Evansfh:write("ATF_TP_ADD_TCS(tp)\n")
1249020d003cSKyle Evansfh:write("{\n")
1250020d003cSKyle Evansfor idx = 1, #tests_added do
1251020d003cSKyle Evans	fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
1252020d003cSKyle Evansend
1253020d003cSKyle Evansfh:write("\treturn (atf_no_error());\n")
1254020d003cSKyle Evansfh:write("}\n")
1255