xref: /freebsd/lib/libc/tests/secure/generate-fortify-tests.lua (revision b53d7aa88fd5a5e8a67dd71ea7332fa473595d28)
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",
65020d003cSKyle Evans	"sys/resource.h",
66020d003cSKyle Evans	"sys/time.h",
67020d003cSKyle Evans	"sys/wait.h",
68020d003cSKyle Evans	"dirent.h",
69020d003cSKyle Evans	"errno.h",
70020d003cSKyle Evans	"fcntl.h",
71020d003cSKyle Evans	"limits.h",
7288276dfbSKyle Evans	"poll.h",
73020d003cSKyle Evans	"signal.h",
74020d003cSKyle Evans	"stdio.h",
75020d003cSKyle Evans	"stdlib.h",
76020d003cSKyle Evans	"string.h",
77020d003cSKyle Evans	"strings.h",
78020d003cSKyle Evans	"sysexits.h",
79020d003cSKyle Evans	"unistd.h",
80*b53d7aa8SKyle Evans	"wchar.h",
81020d003cSKyle Evans	"atf-c.h",
82020d003cSKyle Evans}
83020d003cSKyle Evans
84020d003cSKyle Evanslocal tests_added = {}
85020d003cSKyle Evans
86020d003cSKyle Evans-- Some of these will need to be excluded because clang sees the wrong size when
87020d003cSKyle Evans-- an array is embedded inside a struct, we'll get something that looks more
88020d003cSKyle Evans-- like __builtin_object_size(ptr, 0) than it does the correct
89020d003cSKyle Evans-- __builtin_object_size(ptr, 1) (i.e., includes the padding after).  This is
90020d003cSKyle Evans-- almost certainly a bug in llvm.
91020d003cSKyle Evanslocal function excludes_stack_overflow(disposition, is_heap)
92020d003cSKyle Evans	return (not is_heap) and disposition > 0
93020d003cSKyle Evansend
94020d003cSKyle Evans
9588276dfbSKyle Evanslocal poll_init = [[
9688276dfbSKyle Evans	for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
9788276dfbSKyle Evans		__stack.__buf[i].fd = -1;
9888276dfbSKyle Evans	}
9988276dfbSKyle Evans]]
10088276dfbSKyle Evans
101020d003cSKyle Evanslocal printf_stackvars = "\tchar srcvar[__len + 10];\n"
102020d003cSKyle Evanslocal printf_init = [[
103020d003cSKyle Evans	memset(srcvar, 'A', sizeof(srcvar) - 1);
104020d003cSKyle Evans	srcvar[sizeof(srcvar) - 1] = '\0';
105020d003cSKyle Evans]]
106020d003cSKyle Evans
107cf8e5289SKyle Evanslocal stdio_init = [[
108cf8e5289SKyle Evans	replace_stdin();
109cf8e5289SKyle Evans]]
110cf8e5289SKyle Evans
111020d003cSKyle Evanslocal string_stackvars = "\tchar src[__len];\n"
112020d003cSKyle Evanslocal string_init = [[
113020d003cSKyle Evans	memset(__stack.__buf, 0, __len);
114020d003cSKyle Evans	memset(src, 'A', __len - 1);
115020d003cSKyle Evans	src[__len - 1] = '\0';
116020d003cSKyle Evans]]
117020d003cSKyle Evans
118*b53d7aa8SKyle Evanslocal wstring_stackvars = "\twchar_t src[__len];\n"
119*b53d7aa8SKyle Evanslocal wstring_init = [[
120*b53d7aa8SKyle Evans	wmemset(__stack.__buf, 0, __len);
121*b53d7aa8SKyle Evans	wmemset(src, 'A', __len - 1);
122*b53d7aa8SKyle Evans	src[__len - 1] = '\0';
123*b53d7aa8SKyle Evans]]
124*b53d7aa8SKyle Evans
125020d003cSKyle Evans-- Each test entry describes how to test a given function.  We need to know how
126020d003cSKyle Evans-- to construct the buffer, we need to know the argument set we're dealing with,
127020d003cSKyle Evans-- and we need to know what we're passing to each argument.  We could be passing
128020d003cSKyle Evans-- fixed values, or we could be passing the __buf under test.
129020d003cSKyle Evans--
130020d003cSKyle Evans-- definition:
131020d003cSKyle Evans--   func: name of the function under test to call
132020d003cSKyle Evans--   bufsize: size of buffer to generate, defaults to 42
133020d003cSKyle Evans--   buftype: type of buffer to generate, defaults to unsigned char[]
134020d003cSKyle Evans--   arguments: __buf, __len, or the name of a variable placed on the stack
135020d003cSKyle Evans--   exclude: a function(disposition, is_heap) that returns true if this combo
136020d003cSKyle Evans--     should be excluded.
137020d003cSKyle Evans--   stackvars: extra variables to be placed on the stack, should be a string
138020d003cSKyle Evans--     optionally formatted with tabs and newlines
139020d003cSKyle Evans--   init: extra code to inject just before the function call for initialization
140020d003cSKyle Evans--     of the buffer or any of the above-added stackvars; also a string
141020d003cSKyle Evans--   uses_len: bool-ish, necessary if arguments doesn't include either __idx or
142020d003cSKyle Evans--     or __len so that the test generator doesn't try to vary the size of the
143020d003cSKyle Evans--     buffer instead of just manipulating __idx/__len to try and induce an
144020d003cSKyle Evans--     overflow.
145020d003cSKyle Evans--
146020d003cSKyle Evans-- Most tests will just use the default bufsize/buftype, but under some
147020d003cSKyle Evans-- circumstances it's useful to use a different type (e.g., for alignment
148020d003cSKyle Evans-- requirements).
149020d003cSKyle Evanslocal all_tests = {
15088276dfbSKyle Evans	poll = {
15188276dfbSKyle Evans		-- <poll.h>
15288276dfbSKyle Evans		{
15388276dfbSKyle Evans			func = "poll",
15488276dfbSKyle Evans			bufsize = "4",
15588276dfbSKyle Evans			buftype = "struct pollfd[]",
15688276dfbSKyle Evans			arguments = {
15788276dfbSKyle Evans				"__buf",
15888276dfbSKyle Evans				"__len",
15988276dfbSKyle Evans				"0",
16088276dfbSKyle Evans			},
16188276dfbSKyle Evans			init = poll_init,
16288276dfbSKyle Evans		},
16388276dfbSKyle Evans		{
16488276dfbSKyle Evans			func = "ppoll",
16588276dfbSKyle Evans			bufsize = "4",
16688276dfbSKyle Evans			buftype = "struct pollfd[]",
16788276dfbSKyle Evans			arguments = {
16888276dfbSKyle Evans				"__buf",
16988276dfbSKyle Evans				"__len",
17088276dfbSKyle Evans				"&tv",
17188276dfbSKyle Evans				"NULL",
17288276dfbSKyle Evans			},
17388276dfbSKyle Evans			stackvars = "\tstruct timespec tv = { 0 };\n",
17488276dfbSKyle Evans			init = poll_init,
17588276dfbSKyle Evans		},
17688276dfbSKyle Evans	},
177020d003cSKyle Evans	stdio = {
178020d003cSKyle Evans		-- <stdio.h>
179020d003cSKyle Evans		{
180cf8e5289SKyle Evans			func = "ctermid",
181cf8e5289SKyle Evans			bufsize = "L_ctermid",
182cf8e5289SKyle Evans			arguments = {
183cf8e5289SKyle Evans				"__buf",
184cf8e5289SKyle Evans			},
185cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
186cf8e5289SKyle Evans		},
187cf8e5289SKyle Evans		{
188cf8e5289SKyle Evans			func = "ctermid_r",
189cf8e5289SKyle Evans			bufsize = "L_ctermid",
190cf8e5289SKyle Evans			arguments = {
191cf8e5289SKyle Evans				"__buf",
192cf8e5289SKyle Evans			},
193cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
194cf8e5289SKyle Evans		},
195cf8e5289SKyle Evans		{
196cf8e5289SKyle Evans			func = "fread",
197cf8e5289SKyle Evans			arguments = {
198cf8e5289SKyle Evans				"__buf",
199cf8e5289SKyle Evans				"__len",
200cf8e5289SKyle Evans				"1",
201cf8e5289SKyle Evans				"stdin",
202cf8e5289SKyle Evans			},
203cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
204cf8e5289SKyle Evans			init = stdio_init,
205cf8e5289SKyle Evans		},
206cf8e5289SKyle Evans		{
207cf8e5289SKyle Evans			func = "fread_unlocked",
208cf8e5289SKyle Evans			arguments = {
209cf8e5289SKyle Evans				"__buf",
210cf8e5289SKyle Evans				"__len",
211cf8e5289SKyle Evans				"1",
212cf8e5289SKyle Evans				"stdin",
213cf8e5289SKyle Evans			},
214cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
215cf8e5289SKyle Evans			init = stdio_init,
216cf8e5289SKyle Evans		},
217cf8e5289SKyle Evans		{
218cf8e5289SKyle Evans			func = "gets_s",
219cf8e5289SKyle Evans			arguments = {
220cf8e5289SKyle Evans				"__buf",
221cf8e5289SKyle Evans				"__len",
222cf8e5289SKyle Evans			},
223cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
224cf8e5289SKyle Evans			init = stdio_init,
225cf8e5289SKyle Evans		},
226cf8e5289SKyle Evans		{
227020d003cSKyle Evans			func = "sprintf",
228020d003cSKyle Evans			arguments = {
229020d003cSKyle Evans				"__buf",
230020d003cSKyle Evans				"\"%.*s\"",
231020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
232020d003cSKyle Evans				"srcvar",
233020d003cSKyle Evans			},
234020d003cSKyle Evans			exclude = excludes_stack_overflow,
235020d003cSKyle Evans			stackvars = printf_stackvars,
236020d003cSKyle Evans			init = printf_init,
237020d003cSKyle Evans		},
238020d003cSKyle Evans		{
239020d003cSKyle Evans			func = "snprintf",
240020d003cSKyle Evans			arguments = {
241020d003cSKyle Evans				"__buf",
242020d003cSKyle Evans				"__len",
243020d003cSKyle Evans				"\"%.*s\"",
244020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
245020d003cSKyle Evans				"srcvar",
246020d003cSKyle Evans			},
247020d003cSKyle Evans			exclude = excludes_stack_overflow,
248020d003cSKyle Evans			stackvars = printf_stackvars,
249020d003cSKyle Evans			init = printf_init,
250020d003cSKyle Evans		},
251cf8e5289SKyle Evans		{
252cf8e5289SKyle Evans			func = "tmpnam",
253cf8e5289SKyle Evans			bufsize = "L_tmpnam",
254cf8e5289SKyle Evans			arguments = {
255cf8e5289SKyle Evans				"__buf",
256cf8e5289SKyle Evans			},
257cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
258cf8e5289SKyle Evans		},
259cf8e5289SKyle Evans		{
260cf8e5289SKyle Evans			func = "fgets",
261cf8e5289SKyle Evans			arguments = {
262cf8e5289SKyle Evans				"__buf",
263cf8e5289SKyle Evans				"__len",
264cf8e5289SKyle Evans				"fp",
265cf8e5289SKyle Evans			},
266cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
267cf8e5289SKyle Evans			stackvars = "\tFILE *fp;\n",
268cf8e5289SKyle Evans			init = [[
269cf8e5289SKyle Evans	fp = new_fp(__len);
270cf8e5289SKyle Evans]],
271cf8e5289SKyle Evans		},
272020d003cSKyle Evans	},
273d0b74459SKyle Evans	stdlib = {
274d0b74459SKyle Evans		-- <stdlib.h>
275d0b74459SKyle Evans		{
276d0b74459SKyle Evans			func = "arc4random_buf",
277d0b74459SKyle Evans			arguments = {
278d0b74459SKyle Evans				"__buf",
279d0b74459SKyle Evans				"__len",
280d0b74459SKyle Evans			},
281d0b74459SKyle Evans			exclude = excludes_stack_overflow,
282d0b74459SKyle Evans		},
283d0b74459SKyle Evans		{
284d0b74459SKyle Evans			func = "realpath",
285d0b74459SKyle Evans			bufsize = "PATH_MAX",
286d0b74459SKyle Evans			arguments = {
287d0b74459SKyle Evans				"\".\"",
288d0b74459SKyle Evans				"__buf",
289d0b74459SKyle Evans			},
290d0b74459SKyle Evans			exclude = excludes_stack_overflow,
291d0b74459SKyle Evans		},
292d0b74459SKyle Evans	},
293020d003cSKyle Evans	string = {
294020d003cSKyle Evans		-- <string.h>
295020d003cSKyle Evans		{
296020d003cSKyle Evans			func = "memcpy",
297020d003cSKyle Evans			arguments = {
298020d003cSKyle Evans				"__buf",
299020d003cSKyle Evans				"src",
300020d003cSKyle Evans				"__len",
301020d003cSKyle Evans			},
302020d003cSKyle Evans			exclude = excludes_stack_overflow,
303020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
304020d003cSKyle Evans		},
305020d003cSKyle Evans		{
306cf8e5289SKyle Evans			func = "mempcpy",
307cf8e5289SKyle Evans			arguments = {
308cf8e5289SKyle Evans				"__buf",
309cf8e5289SKyle Evans				"src",
310cf8e5289SKyle Evans				"__len",
311cf8e5289SKyle Evans			},
312cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
313cf8e5289SKyle Evans			stackvars = "\tchar src[__len + 10];\n",
314cf8e5289SKyle Evans		},
315cf8e5289SKyle Evans		{
316020d003cSKyle Evans			func = "memmove",
317020d003cSKyle Evans			arguments = {
318020d003cSKyle Evans				"__buf",
319020d003cSKyle Evans				"src",
320020d003cSKyle Evans				"__len",
321020d003cSKyle Evans			},
322020d003cSKyle Evans			exclude = excludes_stack_overflow,
323020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
324020d003cSKyle Evans		},
325020d003cSKyle Evans		{
326020d003cSKyle Evans			func = "memset",
327020d003cSKyle Evans			arguments = {
328020d003cSKyle Evans				"__buf",
329020d003cSKyle Evans				"0",
330020d003cSKyle Evans				"__len",
331020d003cSKyle Evans			},
332020d003cSKyle Evans			exclude = excludes_stack_overflow,
333020d003cSKyle Evans		},
334020d003cSKyle Evans		{
335020d003cSKyle Evans			func = "stpcpy",
336020d003cSKyle Evans			arguments = {
337020d003cSKyle Evans				"__buf",
338020d003cSKyle Evans				"src",
339020d003cSKyle Evans			},
340020d003cSKyle Evans			exclude = excludes_stack_overflow,
341020d003cSKyle Evans			stackvars = string_stackvars,
342020d003cSKyle Evans			init = string_init,
343020d003cSKyle Evans			uses_len = true,
344020d003cSKyle Evans		},
345020d003cSKyle Evans		{
346020d003cSKyle Evans			func = "stpncpy",
347020d003cSKyle Evans			arguments = {
348020d003cSKyle Evans				"__buf",
349020d003cSKyle Evans				"src",
350020d003cSKyle Evans				"__len",
351020d003cSKyle Evans			},
352020d003cSKyle Evans			exclude = excludes_stack_overflow,
353020d003cSKyle Evans			stackvars = string_stackvars,
354020d003cSKyle Evans			init = string_init,
355020d003cSKyle Evans		},
356020d003cSKyle Evans		{
357020d003cSKyle Evans			func = "strcat",
358020d003cSKyle Evans			arguments = {
359020d003cSKyle Evans				"__buf",
360020d003cSKyle Evans				"src",
361020d003cSKyle Evans			},
362020d003cSKyle Evans			exclude = excludes_stack_overflow,
363020d003cSKyle Evans			stackvars = string_stackvars,
364020d003cSKyle Evans			init = string_init,
365020d003cSKyle Evans			uses_len = true,
366020d003cSKyle Evans		},
367020d003cSKyle Evans		{
368cf8e5289SKyle Evans			func = "strlcat",
369cf8e5289SKyle Evans			arguments = {
370cf8e5289SKyle Evans				"__buf",
371cf8e5289SKyle Evans				"src",
372cf8e5289SKyle Evans				"__len",
373cf8e5289SKyle Evans			},
374cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
375cf8e5289SKyle Evans			stackvars = string_stackvars,
376cf8e5289SKyle Evans			init = string_init,
377cf8e5289SKyle Evans		},
378cf8e5289SKyle Evans		{
379020d003cSKyle Evans			func = "strncat",
380020d003cSKyle Evans			arguments = {
381020d003cSKyle Evans				"__buf",
382020d003cSKyle Evans				"src",
383020d003cSKyle Evans				"__len",
384020d003cSKyle Evans			},
385020d003cSKyle Evans			exclude = excludes_stack_overflow,
386020d003cSKyle Evans			stackvars = string_stackvars,
387020d003cSKyle Evans			init = string_init,
388020d003cSKyle Evans		},
389020d003cSKyle Evans		{
390020d003cSKyle Evans			func = "strcpy",
391020d003cSKyle Evans			arguments = {
392020d003cSKyle Evans				"__buf",
393020d003cSKyle Evans				"src",
394020d003cSKyle Evans			},
395020d003cSKyle Evans			exclude = excludes_stack_overflow,
396020d003cSKyle Evans			stackvars = string_stackvars,
397020d003cSKyle Evans			init = string_init,
398020d003cSKyle Evans			uses_len = true,
399020d003cSKyle Evans		},
400020d003cSKyle Evans		{
401cf8e5289SKyle Evans			func = "strlcpy",
402cf8e5289SKyle Evans			arguments = {
403cf8e5289SKyle Evans				"__buf",
404cf8e5289SKyle Evans				"src",
405cf8e5289SKyle Evans				"__len",
406cf8e5289SKyle Evans			},
407cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
408cf8e5289SKyle Evans			stackvars = string_stackvars,
409cf8e5289SKyle Evans			init = string_init,
410cf8e5289SKyle Evans		},
411cf8e5289SKyle Evans		{
412020d003cSKyle Evans			func = "strncpy",
413020d003cSKyle Evans			arguments = {
414020d003cSKyle Evans				"__buf",
415020d003cSKyle Evans				"src",
416020d003cSKyle Evans				"__len",
417020d003cSKyle Evans			},
418020d003cSKyle Evans			exclude = excludes_stack_overflow,
419020d003cSKyle Evans			stackvars = string_stackvars,
420020d003cSKyle Evans			init = string_init,
421020d003cSKyle Evans		},
422020d003cSKyle Evans	},
423020d003cSKyle Evans	strings = {
424020d003cSKyle Evans		-- <strings.h>
425020d003cSKyle Evans		{
426020d003cSKyle Evans			func = "bcopy",
427020d003cSKyle Evans			arguments = {
428020d003cSKyle Evans				"src",
429020d003cSKyle Evans				"__buf",
430020d003cSKyle Evans				"__len",
431020d003cSKyle Evans			},
432020d003cSKyle Evans			exclude = excludes_stack_overflow,
433020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
434020d003cSKyle Evans		},
435020d003cSKyle Evans		{
436020d003cSKyle Evans			func = "bzero",
437020d003cSKyle Evans			arguments = {
438020d003cSKyle Evans				"__buf",
439020d003cSKyle Evans				"__len",
440020d003cSKyle Evans			},
441020d003cSKyle Evans			exclude = excludes_stack_overflow,
442020d003cSKyle Evans		},
443cf8e5289SKyle Evans		{
444cf8e5289SKyle Evans			func = "explicit_bzero",
445cf8e5289SKyle Evans			arguments = {
446cf8e5289SKyle Evans				"__buf",
447cf8e5289SKyle Evans				"__len",
448cf8e5289SKyle Evans			},
449cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
450cf8e5289SKyle Evans		},
451020d003cSKyle Evans	},
452020d003cSKyle Evans	unistd = {
453020d003cSKyle Evans		-- <unistd.h>
454020d003cSKyle Evans		{
455020d003cSKyle Evans			func = "getcwd",
456020d003cSKyle Evans			bufsize = "8",
457020d003cSKyle Evans			arguments = {
458020d003cSKyle Evans				"__buf",
459020d003cSKyle Evans				"__len",
460020d003cSKyle Evans			},
461020d003cSKyle Evans			exclude = excludes_stack_overflow,
462020d003cSKyle Evans		},
463020d003cSKyle Evans		{
464cf8e5289SKyle Evans			func = "getgrouplist",
465cf8e5289SKyle Evans			bufsize = "4",
466cf8e5289SKyle Evans			buftype = "gid_t[]",
467cf8e5289SKyle Evans			arguments = {
468cf8e5289SKyle Evans				"\"root\"",
469cf8e5289SKyle Evans				"0",
470cf8e5289SKyle Evans				"__buf",
471cf8e5289SKyle Evans				"&intlen",
472cf8e5289SKyle Evans			},
473cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
474cf8e5289SKyle Evans			stackvars = "\tint intlen = (int)__len;\n",
475cf8e5289SKyle Evans			uses_len = true,
476cf8e5289SKyle Evans		},
477cf8e5289SKyle Evans		{
478cf8e5289SKyle Evans			func = "getgroups",
479cf8e5289SKyle Evans			bufsize = "4",
480cf8e5289SKyle Evans			buftype = "gid_t[]",
481cf8e5289SKyle Evans			arguments = {
482cf8e5289SKyle Evans				"__len",
483cf8e5289SKyle Evans				"__buf",
484cf8e5289SKyle Evans			},
485cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
486cf8e5289SKyle Evans		},
487cf8e5289SKyle Evans		{
488cf8e5289SKyle Evans			func = "getloginclass",
489cf8e5289SKyle Evans			arguments = {
490cf8e5289SKyle Evans				"__buf",
491cf8e5289SKyle Evans				"__len",
492cf8e5289SKyle Evans			},
493cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
494cf8e5289SKyle Evans		},
495cf8e5289SKyle Evans		{
496cf8e5289SKyle Evans			func = "pread",
497cf8e5289SKyle Evans			bufsize = "41",
498cf8e5289SKyle Evans			arguments = {
499cf8e5289SKyle Evans				"fd",
500cf8e5289SKyle Evans				"__buf",
501cf8e5289SKyle Evans				"__len",
502cf8e5289SKyle Evans				"0",
503cf8e5289SKyle Evans			},
504cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
505cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
506cf8e5289SKyle Evans			init = [[
507cf8e5289SKyle Evans	fd = new_tmpfile();	/* Cannot fail */
508cf8e5289SKyle Evans]],
509cf8e5289SKyle Evans		},
510cf8e5289SKyle Evans		{
511020d003cSKyle Evans			func = "read",
512020d003cSKyle Evans			bufsize = "41",
513020d003cSKyle Evans			arguments = {
514020d003cSKyle Evans				"fd",
515020d003cSKyle Evans				"__buf",
516020d003cSKyle Evans				"__len",
517020d003cSKyle Evans			},
518020d003cSKyle Evans			exclude = excludes_stack_overflow,
519020d003cSKyle Evans			stackvars = "\tint fd;\n",
520020d003cSKyle Evans			init = [[
521020d003cSKyle Evans	fd = new_tmpfile();	/* Cannot fail */
522020d003cSKyle Evans]],
523020d003cSKyle Evans		},
524020d003cSKyle Evans		{
525020d003cSKyle Evans			func = "readlink",
526020d003cSKyle Evans			arguments = {
527020d003cSKyle Evans				"path",
528020d003cSKyle Evans				"__buf",
529020d003cSKyle Evans				"__len",
530020d003cSKyle Evans			},
531020d003cSKyle Evans			exclude = excludes_stack_overflow,
532020d003cSKyle Evans			stackvars = "\tconst char *path;\n",
533020d003cSKyle Evans			init = [[
534020d003cSKyle Evans	path = new_symlink(__len);		/* Cannot fail */
535020d003cSKyle Evans]],
536020d003cSKyle Evans		},
537cf8e5289SKyle Evans		{
538cf8e5289SKyle Evans			func = "readlinkat",
539cf8e5289SKyle Evans			arguments = {
540cf8e5289SKyle Evans				"AT_FDCWD",
541cf8e5289SKyle Evans				"path",
542cf8e5289SKyle Evans				"__buf",
543cf8e5289SKyle Evans				"__len",
544cf8e5289SKyle Evans			},
545cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
546cf8e5289SKyle Evans			stackvars = "\tconst char *path;\n",
547cf8e5289SKyle Evans			init = [[
548cf8e5289SKyle Evans	path = new_symlink(__len);		/* Cannot fail */
549cf8e5289SKyle Evans]],
550cf8e5289SKyle Evans		},
551cf8e5289SKyle Evans		{
552cf8e5289SKyle Evans			func = "getdomainname",
553cf8e5289SKyle Evans			bufsize = "4",
554cf8e5289SKyle Evans			arguments = {
555cf8e5289SKyle Evans				"__buf",
556cf8e5289SKyle Evans				"__len",
557cf8e5289SKyle Evans			},
558cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
559cf8e5289SKyle Evans			stackvars = "\tchar sysdomain[256];\n",
560cf8e5289SKyle Evans			early_init = [[
561cf8e5289SKyle Evans	(void)getdomainname(sysdomain, __len);
562cf8e5289SKyle Evans	if (strlen(sysdomain) <= __len)
563cf8e5289SKyle Evans		atf_tc_skip("domain name too short for testing");
564cf8e5289SKyle Evans]]
565cf8e5289SKyle Evans		},
566cf8e5289SKyle Evans		{
567cf8e5289SKyle Evans			func = "getentropy",
568cf8e5289SKyle Evans			arguments = {
569cf8e5289SKyle Evans				"__buf",
570cf8e5289SKyle Evans				"__len",
571cf8e5289SKyle Evans			},
572cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
573cf8e5289SKyle Evans		},
574cf8e5289SKyle Evans		{
575cf8e5289SKyle Evans			func = "gethostname",
576cf8e5289SKyle Evans			bufsize = "4",
577cf8e5289SKyle Evans			arguments = {
578cf8e5289SKyle Evans				"__buf",
579cf8e5289SKyle Evans				"__len",
580cf8e5289SKyle Evans			},
581cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
582cf8e5289SKyle Evans			stackvars = [[
583cf8e5289SKyle Evans	char syshost[256];
584cf8e5289SKyle Evans	int error;
585cf8e5289SKyle Evans]],
586cf8e5289SKyle Evans			early_init = [[
587cf8e5289SKyle Evans	error = gethostname(syshost, __len);
588cf8e5289SKyle Evans	if (error != 0 || strlen(syshost) <= __len)
589cf8e5289SKyle Evans		atf_tc_skip("hostname too short for testing");
590cf8e5289SKyle Evans]]
591cf8e5289SKyle Evans		},
592cf8e5289SKyle Evans		{
593cf8e5289SKyle Evans			func = "getlogin_r",
594cf8e5289SKyle Evans			bufsize = "MAXLOGNAME + 1",
595cf8e5289SKyle Evans			arguments = {
596cf8e5289SKyle Evans				"__buf",
597cf8e5289SKyle Evans				"__len",
598cf8e5289SKyle Evans			},
599cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
600cf8e5289SKyle Evans		},
601cf8e5289SKyle Evans		{
602cf8e5289SKyle Evans			func = "ttyname_r",
603cf8e5289SKyle Evans			arguments = {
604cf8e5289SKyle Evans				"fd",
605cf8e5289SKyle Evans				"__buf",
606cf8e5289SKyle Evans				"__len",
607cf8e5289SKyle Evans			},
608cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
609cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
610cf8e5289SKyle Evans			early_init = [[
611cf8e5289SKyle Evans	fd = STDIN_FILENO;
612cf8e5289SKyle Evans	if (!isatty(fd))
613cf8e5289SKyle Evans		atf_tc_skip("stdin is not an fd");
614cf8e5289SKyle Evans]]
615cf8e5289SKyle Evans		},
616020d003cSKyle Evans	},
617*b53d7aa8SKyle Evans	wchar = {
618*b53d7aa8SKyle Evans		-- <wchar.h>
619*b53d7aa8SKyle Evans		{
620*b53d7aa8SKyle Evans			func = "wmemcpy",
621*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
622*b53d7aa8SKyle Evans			arguments = {
623*b53d7aa8SKyle Evans				"__buf",
624*b53d7aa8SKyle Evans				"src",
625*b53d7aa8SKyle Evans				"__len",
626*b53d7aa8SKyle Evans			},
627*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
628*b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
629*b53d7aa8SKyle Evans		},
630*b53d7aa8SKyle Evans		{
631*b53d7aa8SKyle Evans			func = "wmempcpy",
632*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
633*b53d7aa8SKyle Evans			arguments = {
634*b53d7aa8SKyle Evans				"__buf",
635*b53d7aa8SKyle Evans				"src",
636*b53d7aa8SKyle Evans				"__len",
637*b53d7aa8SKyle Evans			},
638*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
639*b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
640*b53d7aa8SKyle Evans		},
641*b53d7aa8SKyle Evans		{
642*b53d7aa8SKyle Evans			func = "wmemmove",
643*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
644*b53d7aa8SKyle Evans			arguments = {
645*b53d7aa8SKyle Evans				"__buf",
646*b53d7aa8SKyle Evans				"src",
647*b53d7aa8SKyle Evans				"__len",
648*b53d7aa8SKyle Evans			},
649*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
650*b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
651*b53d7aa8SKyle Evans		},
652*b53d7aa8SKyle Evans		{
653*b53d7aa8SKyle Evans			func = "wmemset",
654*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
655*b53d7aa8SKyle Evans			arguments = {
656*b53d7aa8SKyle Evans				"__buf",
657*b53d7aa8SKyle Evans				"L'0'",
658*b53d7aa8SKyle Evans				"__len",
659*b53d7aa8SKyle Evans			},
660*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
661*b53d7aa8SKyle Evans		},
662*b53d7aa8SKyle Evans		{
663*b53d7aa8SKyle Evans			func = "wcpcpy",
664*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
665*b53d7aa8SKyle Evans			arguments = {
666*b53d7aa8SKyle Evans				"__buf",
667*b53d7aa8SKyle Evans				"src",
668*b53d7aa8SKyle Evans			},
669*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
670*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
671*b53d7aa8SKyle Evans			init = wstring_init,
672*b53d7aa8SKyle Evans			uses_len = true,
673*b53d7aa8SKyle Evans		},
674*b53d7aa8SKyle Evans		{
675*b53d7aa8SKyle Evans			func = "wcpncpy",
676*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
677*b53d7aa8SKyle Evans			arguments = {
678*b53d7aa8SKyle Evans				"__buf",
679*b53d7aa8SKyle Evans				"src",
680*b53d7aa8SKyle Evans				"__len",
681*b53d7aa8SKyle Evans			},
682*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
683*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
684*b53d7aa8SKyle Evans			init = wstring_init,
685*b53d7aa8SKyle Evans		},
686*b53d7aa8SKyle Evans		{
687*b53d7aa8SKyle Evans			func = "wcscat",
688*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
689*b53d7aa8SKyle Evans			arguments = {
690*b53d7aa8SKyle Evans				"__buf",
691*b53d7aa8SKyle Evans				"src",
692*b53d7aa8SKyle Evans			},
693*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
694*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
695*b53d7aa8SKyle Evans			init = wstring_init,
696*b53d7aa8SKyle Evans			uses_len = true,
697*b53d7aa8SKyle Evans		},
698*b53d7aa8SKyle Evans		{
699*b53d7aa8SKyle Evans			func = "wcslcat",
700*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
701*b53d7aa8SKyle Evans			arguments = {
702*b53d7aa8SKyle Evans				"__buf",
703*b53d7aa8SKyle Evans				"src",
704*b53d7aa8SKyle Evans				"__len",
705*b53d7aa8SKyle Evans			},
706*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
707*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
708*b53d7aa8SKyle Evans			init = wstring_init,
709*b53d7aa8SKyle Evans		},
710*b53d7aa8SKyle Evans		{
711*b53d7aa8SKyle Evans			func = "wcsncat",
712*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
713*b53d7aa8SKyle Evans			arguments = {
714*b53d7aa8SKyle Evans				"__buf",
715*b53d7aa8SKyle Evans				"src",
716*b53d7aa8SKyle Evans				"__len",
717*b53d7aa8SKyle Evans			},
718*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
719*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
720*b53d7aa8SKyle Evans			init = wstring_init,
721*b53d7aa8SKyle Evans		},
722*b53d7aa8SKyle Evans		{
723*b53d7aa8SKyle Evans			func = "wcscpy",
724*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
725*b53d7aa8SKyle Evans			arguments = {
726*b53d7aa8SKyle Evans				"__buf",
727*b53d7aa8SKyle Evans				"src",
728*b53d7aa8SKyle Evans			},
729*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
730*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
731*b53d7aa8SKyle Evans			init = wstring_init,
732*b53d7aa8SKyle Evans			uses_len = true,
733*b53d7aa8SKyle Evans		},
734*b53d7aa8SKyle Evans		{
735*b53d7aa8SKyle Evans			func = "wcslcpy",
736*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
737*b53d7aa8SKyle Evans			arguments = {
738*b53d7aa8SKyle Evans				"__buf",
739*b53d7aa8SKyle Evans				"src",
740*b53d7aa8SKyle Evans				"__len",
741*b53d7aa8SKyle Evans			},
742*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
743*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
744*b53d7aa8SKyle Evans			init = wstring_init,
745*b53d7aa8SKyle Evans		},
746*b53d7aa8SKyle Evans		{
747*b53d7aa8SKyle Evans			func = "wcsncpy",
748*b53d7aa8SKyle Evans			buftype = "wchar_t[]",
749*b53d7aa8SKyle Evans			arguments = {
750*b53d7aa8SKyle Evans				"__buf",
751*b53d7aa8SKyle Evans				"src",
752*b53d7aa8SKyle Evans				"__len",
753*b53d7aa8SKyle Evans			},
754*b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
755*b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
756*b53d7aa8SKyle Evans			init = wstring_init,
757*b53d7aa8SKyle Evans		},
758*b53d7aa8SKyle Evans	},
759020d003cSKyle Evans}
760020d003cSKyle Evans
761020d003cSKyle Evanslocal function write_test_boilerplate(fh, name, body)
762020d003cSKyle Evans	fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
763020d003cSKyle Evans	fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
764020d003cSKyle Evans	fh:write("{\n" .. body .. "\n}\n\n")
765020d003cSKyle Evans	return name
766020d003cSKyle Evansend
767020d003cSKyle Evans
768020d003cSKyle Evanslocal function generate_test_name(func, variant, disposition, heap)
769020d003cSKyle Evans	local basename = func
770020d003cSKyle Evans	if variant then
771020d003cSKyle Evans		basename = basename .. "_" .. variant
772020d003cSKyle Evans	end
773020d003cSKyle Evans	if heap then
774020d003cSKyle Evans		basename = basename .. "_heap"
775020d003cSKyle Evans	end
776020d003cSKyle Evans	if disposition < 0 then
777020d003cSKyle Evans		return basename .. "_before_end"
778020d003cSKyle Evans	elseif disposition == 0 then
779020d003cSKyle Evans		return basename .. "_end"
780020d003cSKyle Evans	else
781020d003cSKyle Evans		return basename .. "_after_end"
782020d003cSKyle Evans	end
783020d003cSKyle Evansend
784020d003cSKyle Evans
785020d003cSKyle Evanslocal function array_type(buftype)
786020d003cSKyle Evans	if not buftype:match("%[%]") then
787020d003cSKyle Evans		return nil
788020d003cSKyle Evans	end
789020d003cSKyle Evans
790020d003cSKyle Evans	return buftype:gsub("%[%]", "")
791020d003cSKyle Evansend
792020d003cSKyle Evans
793020d003cSKyle Evanslocal function configurable(def, idx)
794020d003cSKyle Evans	local cfgitem = def[idx]
795020d003cSKyle Evans
796020d003cSKyle Evans	if not cfgitem then
797020d003cSKyle Evans		return nil
798020d003cSKyle Evans	end
799020d003cSKyle Evans
800020d003cSKyle Evans	if type(cfgitem) == "function" then
801020d003cSKyle Evans		return cfgitem()
802020d003cSKyle Evans	end
803020d003cSKyle Evans
804020d003cSKyle Evans	return cfgitem
805020d003cSKyle Evansend
806020d003cSKyle Evans
807020d003cSKyle Evanslocal function generate_stackframe(buftype, bufsize, disposition, heap, def)
808020d003cSKyle Evans	local function len_offset(inverted, disposition)
809020d003cSKyle Evans		-- Tests that don't use __len in their arguments may use an
810020d003cSKyle Evans		-- inverted sense because we can't just specify a length that
811020d003cSKyle Evans		-- would induce an access just after the end.  Instead, we have
812020d003cSKyle Evans		-- to manipulate the buffer size to be too short so that the
813020d003cSKyle Evans		-- function under test would write one too many.
814020d003cSKyle Evans		if disposition < 0 then
815020d003cSKyle Evans			return ((inverted and " + ") or " - ") .. "1"
816020d003cSKyle Evans		elseif disposition == 0 then
817020d003cSKyle Evans			return ""
818020d003cSKyle Evans		else
819020d003cSKyle Evans			return ((inverted and " - ") or " + ") .. "1"
820020d003cSKyle Evans		end
821020d003cSKyle Evans	end
822020d003cSKyle Evans
823020d003cSKyle Evans	local function test_uses_len(def)
824020d003cSKyle Evans		if def.uses_len then
825020d003cSKyle Evans			return true
826020d003cSKyle Evans		end
827020d003cSKyle Evans
828020d003cSKyle Evans		for _, arg in ipairs(def.arguments) do
829020d003cSKyle Evans			if arg:match("__len") or arg:match("__idx") then
830020d003cSKyle Evans				return true
831020d003cSKyle Evans			end
832020d003cSKyle Evans		end
833020d003cSKyle Evans
834020d003cSKyle Evans		return false
835020d003cSKyle Evans	end
836020d003cSKyle Evans
837020d003cSKyle Evans
838020d003cSKyle Evans	-- This is perhaps a little convoluted, but we toss the buffer into a
839020d003cSKyle Evans	-- struct on the stack to guarantee that we have at least one valid
840020d003cSKyle Evans	-- byte on either side of the buffer -- a measure to make sure that
841020d003cSKyle Evans	-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
842020d003cSKyle Evans	-- rather than some other stack or memory protection.
843020d003cSKyle Evans	local vars = "\tstruct {\n"
844020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_l;\n"
845020d003cSKyle Evans
846020d003cSKyle Evans	local uses_len = test_uses_len(def)
847020d003cSKyle Evans	local bufsize_offset = len_offset(not uses_len, disposition)
848020d003cSKyle Evans	local buftype_elem = array_type(buftype)
849020d003cSKyle Evans	local size_expr = bufsize
850020d003cSKyle Evans
851020d003cSKyle Evans	if not uses_len then
852020d003cSKyle Evans		-- If the length isn't in use, we have to vary the buffer size
853020d003cSKyle Evans		-- since the fortified function likely has some internal size
854020d003cSKyle Evans		-- constraint that it's supposed to be checking.
855020d003cSKyle Evans		size_expr = size_expr .. bufsize_offset
856020d003cSKyle Evans	end
857020d003cSKyle Evans
858020d003cSKyle Evans	if not heap and buftype_elem then
859020d003cSKyle Evans		-- Array type: size goes after identifier
860020d003cSKyle Evans		vars = vars .. "\t\t" .. buftype_elem ..
861020d003cSKyle Evans		    " __buf[" .. size_expr .. "];\n"
862020d003cSKyle Evans	else
863020d003cSKyle Evans		local basic_type = buftype_elem or buftype
864020d003cSKyle Evans
865020d003cSKyle Evans		-- Heap tests obviously just put a pointer on the stack that
866020d003cSKyle Evans		-- points to our new allocation, but we leave it in the padded
867020d003cSKyle Evans		-- struct just to simplify our generator.
868020d003cSKyle Evans		if heap then
869020d003cSKyle Evans			basic_type = basic_type .. " *"
870020d003cSKyle Evans		end
871020d003cSKyle Evans		vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
872020d003cSKyle Evans	end
873020d003cSKyle Evans
874020d003cSKyle Evans	-- padding_r is our just-past-the-end padding that we use to make sure
875020d003cSKyle Evans	-- that there's a valid portion after the buffer that isn't being
876020d003cSKyle Evans	-- included in our function calls.  If we didn't have it, then we'd have
877020d003cSKyle Evans	-- a hard time feeling confident that an abort on the just-after tests
878020d003cSKyle Evans	-- isn't maybe from some other memory or stack protection.
879020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_r;\n"
880020d003cSKyle Evans	vars = vars .. "\t} __stack;\n"
881020d003cSKyle Evans
882020d003cSKyle Evans	-- Not all tests will use __bufsz, but some do for, e.g., clearing
883020d003cSKyle Evans	-- memory..
884020d003cSKyle Evans	vars = vars .. "\tconst size_t __bufsz __unused = "
885020d003cSKyle Evans	if heap then
886020d003cSKyle Evans		local scalar = 1
887020d003cSKyle Evans		if buftype_elem then
888020d003cSKyle Evans			scalar = size_expr
889020d003cSKyle Evans		end
890020d003cSKyle Evans
891020d003cSKyle Evans		vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
892020d003cSKyle Evans	else
893020d003cSKyle Evans		vars = vars .. "sizeof(__stack.__buf);\n"
894020d003cSKyle Evans	end
895020d003cSKyle Evans
896020d003cSKyle Evans	vars = vars .. "\tconst size_t __len = " .. bufsize ..
897020d003cSKyle Evans	    bufsize_offset .. ";\n"
898020d003cSKyle Evans	vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
899020d003cSKyle Evans
900020d003cSKyle Evans	-- For overflow testing, we need to fork() because we're expecting the
901020d003cSKyle Evans	-- test to ultimately abort()/_exit().  Then we can collect the exit
902020d003cSKyle Evans	-- status and report appropriately.
903020d003cSKyle Evans	if disposition > 0 then
904020d003cSKyle Evans		vars = vars .. "\tpid_t __child;\n"
905020d003cSKyle Evans		vars = vars .. "\tint __status;\n"
906020d003cSKyle Evans	end
907020d003cSKyle Evans
908020d003cSKyle Evans	-- Any other stackvars defined by the test get placed after everything
909020d003cSKyle Evans	-- else.
910020d003cSKyle Evans	vars = vars .. (configurable(def, "stackvars") or "")
911020d003cSKyle Evans
912020d003cSKyle Evans	return vars
913020d003cSKyle Evansend
914020d003cSKyle Evans
915020d003cSKyle Evanslocal function write_test(fh, func, disposition, heap, def)
916020d003cSKyle Evans	local testname = generate_test_name(func, def.variant, disposition, heap)
917020d003cSKyle Evans	local buftype = def.buftype or "unsigned char[]"
918020d003cSKyle Evans	local bufsize = def.bufsize or 42
919020d003cSKyle Evans	local body = ""
920020d003cSKyle Evans
921020d003cSKyle Evans	if def.exclude and def.exclude(disposition, heap) then
922020d003cSKyle Evans		return
923020d003cSKyle Evans	end
924020d003cSKyle Evans
925020d003cSKyle Evans	local function need_addr(buftype)
926020d003cSKyle Evans		return not (buftype:match("%[%]") or buftype:match("%*"))
927020d003cSKyle Evans	end
928020d003cSKyle Evans
929020d003cSKyle Evans	if heap then
930020d003cSKyle Evans		body = body .. "#define BUF __stack.__buf\n"
931020d003cSKyle Evans	else
932020d003cSKyle Evans		body = body .. "#define BUF &__stack.__buf\n"
933020d003cSKyle Evans	end
934020d003cSKyle Evans
935020d003cSKyle Evans	-- Setup the buffer
936020d003cSKyle Evans	body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
937020d003cSKyle Evans	    "\n"
938020d003cSKyle Evans
939020d003cSKyle Evans	-- Any early initialization goes before we would fork for the just-after
940020d003cSKyle Evans	-- tests, because they may want to skip the test based on some criteria
941020d003cSKyle Evans	-- and we can't propagate that up very easily once we're forked.
942020d003cSKyle Evans	local early_init = configurable(def, "early_init")
943020d003cSKyle Evans	body = body .. (early_init or "")
944020d003cSKyle Evans	if early_init then
945020d003cSKyle Evans		body = body .. "\n"
946020d003cSKyle Evans	end
947020d003cSKyle Evans
948020d003cSKyle Evans	-- Fork off, iff we're testing some access past the end of the buffer.
949020d003cSKyle Evans	if disposition > 0 then
950020d003cSKyle Evans		body = body .. [[
951020d003cSKyle Evans	__child = fork();
952020d003cSKyle Evans	ATF_REQUIRE(__child >= 0);
953020d003cSKyle Evans	if (__child > 0)
954020d003cSKyle Evans		goto monitor;
955020d003cSKyle Evans
956020d003cSKyle Evans	/* Child */
957020d003cSKyle Evans	disable_coredumps();
958020d003cSKyle Evans]]
959020d003cSKyle Evans	end
960020d003cSKyle Evans
961020d003cSKyle Evans	local bufvar = "__stack.__buf"
962020d003cSKyle Evans	if heap then
963020d003cSKyle Evans		-- Buffer needs to be initialized because it's a heap allocation.
964020d003cSKyle Evans		body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
965020d003cSKyle Evans	end
966020d003cSKyle Evans
967020d003cSKyle Evans	-- Non-early init happens just after the fork in the child, not the
968020d003cSKyle Evans	-- monitor.  This is used to setup any other buffers we may need, for
969020d003cSKyle Evans	-- instance.
970020d003cSKyle Evans	local extra_init = configurable(def, "init")
971020d003cSKyle Evans	body = body .. (extra_init or "")
972020d003cSKyle Evans
973020d003cSKyle Evans	if heap or extra_init then
974020d003cSKyle Evans		body = body .. "\n"
975020d003cSKyle Evans	end
976020d003cSKyle Evans
977020d003cSKyle Evans	-- Setup the function call with arguments as described in the test
978020d003cSKyle Evans	-- definition.
979020d003cSKyle Evans	body = body .. "\t" .. func .. "("
980020d003cSKyle Evans
981020d003cSKyle Evans	for idx, arg in ipairs(def.arguments) do
982020d003cSKyle Evans		if idx > 1 then
983020d003cSKyle Evans			body = body .. ", "
984020d003cSKyle Evans		end
985020d003cSKyle Evans
986020d003cSKyle Evans		if arg == "__buf" then
987020d003cSKyle Evans			if not heap and need_addr(buftype) then
988020d003cSKyle Evans				body = body .. "&"
989020d003cSKyle Evans			end
990020d003cSKyle Evans
991020d003cSKyle Evans			body = body .. bufvar
992020d003cSKyle Evans		else
993020d003cSKyle Evans			local argname = arg
994020d003cSKyle Evans
995020d003cSKyle Evans			if def.value_of then
996020d003cSKyle Evans				argname = argname or def.value_of(arg)
997020d003cSKyle Evans			end
998020d003cSKyle Evans
999020d003cSKyle Evans			body = body .. argname
1000020d003cSKyle Evans		end
1001020d003cSKyle Evans	end
1002020d003cSKyle Evans
1003020d003cSKyle Evans	body = body .. ");\n"
1004020d003cSKyle Evans
1005020d003cSKyle Evans	-- Monitor stuff follows, for OOB access.
1006020d003cSKyle Evans	if disposition <= 0 then
1007020d003cSKyle Evans		goto skip
1008020d003cSKyle Evans	end
1009020d003cSKyle Evans
1010020d003cSKyle Evans	body = body .. [[
1011020d003cSKyle Evans	_exit(EX_SOFTWARE);	/* Should have aborted. */
1012020d003cSKyle Evans
1013020d003cSKyle Evansmonitor:
1014020d003cSKyle Evans	while (waitpid(__child, &__status, 0) != __child) {
1015020d003cSKyle Evans		ATF_REQUIRE_EQ(EINTR, errno);
1016020d003cSKyle Evans	}
1017020d003cSKyle Evans
1018020d003cSKyle Evans	if (!WIFSIGNALED(__status)) {
1019020d003cSKyle Evans		switch (WEXITSTATUS(__status)) {
1020020d003cSKyle Evans		case EX_SOFTWARE:
1021020d003cSKyle Evans			atf_tc_fail("FORTIFY_SOURCE failed to abort");
1022020d003cSKyle Evans			break;
1023020d003cSKyle Evans		case EX_OSERR:
1024020d003cSKyle Evans			atf_tc_fail("setrlimit(2) failed");
1025020d003cSKyle Evans			break;
1026020d003cSKyle Evans		default:
1027020d003cSKyle Evans			atf_tc_fail("child exited with status %d",
1028020d003cSKyle Evans			    WEXITSTATUS(__status));
1029020d003cSKyle Evans		}
1030020d003cSKyle Evans	} else {
1031020d003cSKyle Evans		ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
1032020d003cSKyle Evans	}
1033020d003cSKyle Evans]]
1034020d003cSKyle Evans
1035020d003cSKyle Evans::skip::
1036020d003cSKyle Evans	body = body .. "#undef BUF\n"
1037020d003cSKyle Evans	return write_test_boilerplate(fh, testname, body)
1038020d003cSKyle Evansend
1039020d003cSKyle Evans
1040020d003cSKyle Evans-- main()
1041020d003cSKyle Evanslocal tests
1042020d003cSKyle Evanslocal tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
1043020d003cSKyle Evansfor k, defs in pairs(all_tests) do
1044020d003cSKyle Evans	if k == tcat then
1045020d003cSKyle Evans		tests = defs
1046020d003cSKyle Evans		break
1047020d003cSKyle Evans	end
1048020d003cSKyle Evansend
1049020d003cSKyle Evans
1050020d003cSKyle Evansassert(tests, "category " .. tcat .. " not found")
1051020d003cSKyle Evans
1052020d003cSKyle Evanslocal fh = io.stdout
1053020d003cSKyle Evansfh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
1054020d003cSKyle Evans    tcat .. "\"` */\n\n")
1055020d003cSKyle Evansfh:write("#define	_FORTIFY_SOURCE	2\n")
1056020d003cSKyle Evansfh:write("#define	TMPFILE_SIZE	(1024 * 32)\n")
1057020d003cSKyle Evans
1058020d003cSKyle Evansfh:write("\n")
1059020d003cSKyle Evansfor _, inc in ipairs(includes) do
1060020d003cSKyle Evans	fh:write("#include <" .. inc .. ">\n")
1061020d003cSKyle Evansend
1062020d003cSKyle Evans
1063020d003cSKyle Evansfh:write([[
1064020d003cSKyle Evans
1065cf8e5289SKyle Evansstatic FILE * __unused
1066cf8e5289SKyle Evansnew_fp(size_t __len)
1067cf8e5289SKyle Evans{
1068cf8e5289SKyle Evans	static char fpbuf[LINE_MAX];
1069cf8e5289SKyle Evans	FILE *fp;
1070cf8e5289SKyle Evans
1071cf8e5289SKyle Evans	ATF_REQUIRE(__len <= sizeof(fpbuf));
1072cf8e5289SKyle Evans
1073cf8e5289SKyle Evans	memset(fpbuf, 'A', sizeof(fpbuf) - 1);
1074cf8e5289SKyle Evans	fpbuf[sizeof(fpbuf) - 1] = '\0';
1075cf8e5289SKyle Evans
1076cf8e5289SKyle Evans	fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
1077cf8e5289SKyle Evans	ATF_REQUIRE(fp != NULL);
1078cf8e5289SKyle Evans
1079cf8e5289SKyle Evans	return (fp);
1080cf8e5289SKyle Evans}
1081cf8e5289SKyle Evans
1082020d003cSKyle Evans/*
1083020d003cSKyle Evans * Create a new symlink to use for readlink(2) style tests, we'll just use a
1084020d003cSKyle Evans * random target name to have something interesting to look at.
1085020d003cSKyle Evans */
1086020d003cSKyle Evansstatic const char * __unused
1087020d003cSKyle Evansnew_symlink(size_t __len)
1088020d003cSKyle Evans{
1089020d003cSKyle Evans	static const char linkname[] = "link";
1090020d003cSKyle Evans	char target[MAXNAMLEN];
1091020d003cSKyle Evans	int error;
1092020d003cSKyle Evans
1093020d003cSKyle Evans	ATF_REQUIRE(__len <= sizeof(target));
1094020d003cSKyle Evans
1095020d003cSKyle Evans	arc4random_buf(target, sizeof(target));
1096020d003cSKyle Evans
1097020d003cSKyle Evans	error = unlink(linkname);
1098020d003cSKyle Evans	ATF_REQUIRE(error == 0 || errno == ENOENT);
1099020d003cSKyle Evans
1100020d003cSKyle Evans	error = symlink(target, linkname);
1101020d003cSKyle Evans	ATF_REQUIRE(error == 0);
1102020d003cSKyle Evans
1103020d003cSKyle Evans	return (linkname);
1104020d003cSKyle Evans}
1105020d003cSKyle Evans
1106020d003cSKyle Evans/*
1107020d003cSKyle Evans * Constructs a tmpfile that we can use for testing read(2) and friends.
1108020d003cSKyle Evans */
1109020d003cSKyle Evansstatic int __unused
1110020d003cSKyle Evansnew_tmpfile(void)
1111020d003cSKyle Evans{
1112020d003cSKyle Evans	char buf[1024];
1113020d003cSKyle Evans	ssize_t rv;
1114020d003cSKyle Evans	size_t written;
1115020d003cSKyle Evans	int fd;
1116020d003cSKyle Evans
1117020d003cSKyle Evans	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
1118020d003cSKyle Evans	ATF_REQUIRE(fd >= 0);
1119020d003cSKyle Evans
1120020d003cSKyle Evans	written = 0;
1121020d003cSKyle Evans	while (written < TMPFILE_SIZE) {
1122020d003cSKyle Evans		rv = write(fd, buf, sizeof(buf));
1123020d003cSKyle Evans		ATF_REQUIRE(rv > 0);
1124020d003cSKyle Evans
1125020d003cSKyle Evans		written += rv;
1126020d003cSKyle Evans	}
1127020d003cSKyle Evans
1128020d003cSKyle Evans	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
1129020d003cSKyle Evans	return (fd);
1130020d003cSKyle Evans}
1131020d003cSKyle Evans
1132020d003cSKyle Evansstatic void
1133020d003cSKyle Evansdisable_coredumps(void)
1134020d003cSKyle Evans{
1135020d003cSKyle Evans	struct rlimit rl = { 0 };
1136020d003cSKyle Evans
1137020d003cSKyle Evans	if (setrlimit(RLIMIT_CORE, &rl) == -1)
1138020d003cSKyle Evans		_exit(EX_OSERR);
1139020d003cSKyle Evans}
1140020d003cSKyle Evans
1141cf8e5289SKyle Evans/*
1142cf8e5289SKyle Evans * Replaces stdin with a file that we can actually read from, for tests where
1143cf8e5289SKyle Evans * we want a FILE * or fd that we can get data from.
1144cf8e5289SKyle Evans */
1145cf8e5289SKyle Evansstatic void __unused
1146cf8e5289SKyle Evansreplace_stdin(void)
1147cf8e5289SKyle Evans{
1148cf8e5289SKyle Evans	int fd;
1149cf8e5289SKyle Evans
1150cf8e5289SKyle Evans	fd = new_tmpfile();
1151cf8e5289SKyle Evans
1152cf8e5289SKyle Evans	(void)dup2(fd, STDIN_FILENO);
1153cf8e5289SKyle Evans	if (fd != STDIN_FILENO)
1154cf8e5289SKyle Evans		close(fd);
1155cf8e5289SKyle Evans}
1156cf8e5289SKyle Evans
1157020d003cSKyle Evans]])
1158020d003cSKyle Evans
1159020d003cSKyle Evansfor _, def in pairs(tests) do
1160020d003cSKyle Evans	local func = def.func
1161020d003cSKyle Evans	local function write_tests(heap)
1162020d003cSKyle Evans		-- Dispositions here are relative to the buffer size prescribed
1163020d003cSKyle Evans		-- by the test definition.
1164020d003cSKyle Evans		local dispositions = def.dispositions or { -1, 0, 1 }
1165020d003cSKyle Evans
1166020d003cSKyle Evans		for _, disposition in ipairs(dispositions) do
1167020d003cSKyle Evans			tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
1168020d003cSKyle Evans		end
1169020d003cSKyle Evans	end
1170020d003cSKyle Evans
1171020d003cSKyle Evans	write_tests(false)
1172020d003cSKyle Evans	write_tests(true)
1173020d003cSKyle Evansend
1174020d003cSKyle Evans
1175020d003cSKyle Evansfh:write("ATF_TP_ADD_TCS(tp)\n")
1176020d003cSKyle Evansfh:write("{\n")
1177020d003cSKyle Evansfor idx = 1, #tests_added do
1178020d003cSKyle Evans	fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
1179020d003cSKyle Evansend
1180020d003cSKyle Evansfh:write("\treturn (atf_no_error());\n")
1181020d003cSKyle Evansfh:write("}\n")
1182