xref: /freebsd/lib/libc/tests/secure/generate-fortify-tests.lua (revision 88276dfbf19e3c0dbd1abee5c2e18c8a4a8c5559)
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",
72*88276dfbSKyle 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",
80020d003cSKyle Evans	"atf-c.h",
81020d003cSKyle Evans}
82020d003cSKyle Evans
83020d003cSKyle Evanslocal tests_added = {}
84020d003cSKyle Evans
85020d003cSKyle Evans-- Some of these will need to be excluded because clang sees the wrong size when
86020d003cSKyle Evans-- an array is embedded inside a struct, we'll get something that looks more
87020d003cSKyle Evans-- like __builtin_object_size(ptr, 0) than it does the correct
88020d003cSKyle Evans-- __builtin_object_size(ptr, 1) (i.e., includes the padding after).  This is
89020d003cSKyle Evans-- almost certainly a bug in llvm.
90020d003cSKyle Evanslocal function excludes_stack_overflow(disposition, is_heap)
91020d003cSKyle Evans	return (not is_heap) and disposition > 0
92020d003cSKyle Evansend
93020d003cSKyle Evans
94*88276dfbSKyle Evanslocal poll_init = [[
95*88276dfbSKyle Evans	for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
96*88276dfbSKyle Evans		__stack.__buf[i].fd = -1;
97*88276dfbSKyle Evans	}
98*88276dfbSKyle Evans]]
99*88276dfbSKyle Evans
100020d003cSKyle Evanslocal printf_stackvars = "\tchar srcvar[__len + 10];\n"
101020d003cSKyle Evanslocal printf_init = [[
102020d003cSKyle Evans	memset(srcvar, 'A', sizeof(srcvar) - 1);
103020d003cSKyle Evans	srcvar[sizeof(srcvar) - 1] = '\0';
104020d003cSKyle Evans]]
105020d003cSKyle Evans
106cf8e5289SKyle Evanslocal stdio_init = [[
107cf8e5289SKyle Evans	replace_stdin();
108cf8e5289SKyle Evans]]
109cf8e5289SKyle Evans
110020d003cSKyle Evanslocal string_stackvars = "\tchar src[__len];\n"
111020d003cSKyle Evanslocal string_init = [[
112020d003cSKyle Evans	memset(__stack.__buf, 0, __len);
113020d003cSKyle Evans	memset(src, 'A', __len - 1);
114020d003cSKyle Evans	src[__len - 1] = '\0';
115020d003cSKyle Evans]]
116020d003cSKyle Evans
117020d003cSKyle Evans-- Each test entry describes how to test a given function.  We need to know how
118020d003cSKyle Evans-- to construct the buffer, we need to know the argument set we're dealing with,
119020d003cSKyle Evans-- and we need to know what we're passing to each argument.  We could be passing
120020d003cSKyle Evans-- fixed values, or we could be passing the __buf under test.
121020d003cSKyle Evans--
122020d003cSKyle Evans-- definition:
123020d003cSKyle Evans--   func: name of the function under test to call
124020d003cSKyle Evans--   bufsize: size of buffer to generate, defaults to 42
125020d003cSKyle Evans--   buftype: type of buffer to generate, defaults to unsigned char[]
126020d003cSKyle Evans--   arguments: __buf, __len, or the name of a variable placed on the stack
127020d003cSKyle Evans--   exclude: a function(disposition, is_heap) that returns true if this combo
128020d003cSKyle Evans--     should be excluded.
129020d003cSKyle Evans--   stackvars: extra variables to be placed on the stack, should be a string
130020d003cSKyle Evans--     optionally formatted with tabs and newlines
131020d003cSKyle Evans--   init: extra code to inject just before the function call for initialization
132020d003cSKyle Evans--     of the buffer or any of the above-added stackvars; also a string
133020d003cSKyle Evans--   uses_len: bool-ish, necessary if arguments doesn't include either __idx or
134020d003cSKyle Evans--     or __len so that the test generator doesn't try to vary the size of the
135020d003cSKyle Evans--     buffer instead of just manipulating __idx/__len to try and induce an
136020d003cSKyle Evans--     overflow.
137020d003cSKyle Evans--
138020d003cSKyle Evans-- Most tests will just use the default bufsize/buftype, but under some
139020d003cSKyle Evans-- circumstances it's useful to use a different type (e.g., for alignment
140020d003cSKyle Evans-- requirements).
141020d003cSKyle Evanslocal all_tests = {
142*88276dfbSKyle Evans	poll = {
143*88276dfbSKyle Evans		-- <poll.h>
144*88276dfbSKyle Evans		{
145*88276dfbSKyle Evans			func = "poll",
146*88276dfbSKyle Evans			bufsize = "4",
147*88276dfbSKyle Evans			buftype = "struct pollfd[]",
148*88276dfbSKyle Evans			arguments = {
149*88276dfbSKyle Evans				"__buf",
150*88276dfbSKyle Evans				"__len",
151*88276dfbSKyle Evans				"0",
152*88276dfbSKyle Evans			},
153*88276dfbSKyle Evans			init = poll_init,
154*88276dfbSKyle Evans		},
155*88276dfbSKyle Evans		{
156*88276dfbSKyle Evans			func = "ppoll",
157*88276dfbSKyle Evans			bufsize = "4",
158*88276dfbSKyle Evans			buftype = "struct pollfd[]",
159*88276dfbSKyle Evans			arguments = {
160*88276dfbSKyle Evans				"__buf",
161*88276dfbSKyle Evans				"__len",
162*88276dfbSKyle Evans				"&tv",
163*88276dfbSKyle Evans				"NULL",
164*88276dfbSKyle Evans			},
165*88276dfbSKyle Evans			stackvars = "\tstruct timespec tv = { 0 };\n",
166*88276dfbSKyle Evans			init = poll_init,
167*88276dfbSKyle Evans		},
168*88276dfbSKyle Evans	},
169020d003cSKyle Evans	stdio = {
170020d003cSKyle Evans		-- <stdio.h>
171020d003cSKyle Evans		{
172cf8e5289SKyle Evans			func = "ctermid",
173cf8e5289SKyle Evans			bufsize = "L_ctermid",
174cf8e5289SKyle Evans			arguments = {
175cf8e5289SKyle Evans				"__buf",
176cf8e5289SKyle Evans			},
177cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
178cf8e5289SKyle Evans		},
179cf8e5289SKyle Evans		{
180cf8e5289SKyle Evans			func = "ctermid_r",
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 = "fread",
189cf8e5289SKyle Evans			arguments = {
190cf8e5289SKyle Evans				"__buf",
191cf8e5289SKyle Evans				"__len",
192cf8e5289SKyle Evans				"1",
193cf8e5289SKyle Evans				"stdin",
194cf8e5289SKyle Evans			},
195cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
196cf8e5289SKyle Evans			init = stdio_init,
197cf8e5289SKyle Evans		},
198cf8e5289SKyle Evans		{
199cf8e5289SKyle Evans			func = "fread_unlocked",
200cf8e5289SKyle Evans			arguments = {
201cf8e5289SKyle Evans				"__buf",
202cf8e5289SKyle Evans				"__len",
203cf8e5289SKyle Evans				"1",
204cf8e5289SKyle Evans				"stdin",
205cf8e5289SKyle Evans			},
206cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
207cf8e5289SKyle Evans			init = stdio_init,
208cf8e5289SKyle Evans		},
209cf8e5289SKyle Evans		{
210cf8e5289SKyle Evans			func = "gets_s",
211cf8e5289SKyle Evans			arguments = {
212cf8e5289SKyle Evans				"__buf",
213cf8e5289SKyle Evans				"__len",
214cf8e5289SKyle Evans			},
215cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
216cf8e5289SKyle Evans			init = stdio_init,
217cf8e5289SKyle Evans		},
218cf8e5289SKyle Evans		{
219020d003cSKyle Evans			func = "sprintf",
220020d003cSKyle Evans			arguments = {
221020d003cSKyle Evans				"__buf",
222020d003cSKyle Evans				"\"%.*s\"",
223020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
224020d003cSKyle Evans				"srcvar",
225020d003cSKyle Evans			},
226020d003cSKyle Evans			exclude = excludes_stack_overflow,
227020d003cSKyle Evans			stackvars = printf_stackvars,
228020d003cSKyle Evans			init = printf_init,
229020d003cSKyle Evans		},
230020d003cSKyle Evans		{
231020d003cSKyle Evans			func = "snprintf",
232020d003cSKyle Evans			arguments = {
233020d003cSKyle Evans				"__buf",
234020d003cSKyle Evans				"__len",
235020d003cSKyle Evans				"\"%.*s\"",
236020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
237020d003cSKyle Evans				"srcvar",
238020d003cSKyle Evans			},
239020d003cSKyle Evans			exclude = excludes_stack_overflow,
240020d003cSKyle Evans			stackvars = printf_stackvars,
241020d003cSKyle Evans			init = printf_init,
242020d003cSKyle Evans		},
243cf8e5289SKyle Evans		{
244cf8e5289SKyle Evans			func = "tmpnam",
245cf8e5289SKyle Evans			bufsize = "L_tmpnam",
246cf8e5289SKyle Evans			arguments = {
247cf8e5289SKyle Evans				"__buf",
248cf8e5289SKyle Evans			},
249cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
250cf8e5289SKyle Evans		},
251cf8e5289SKyle Evans		{
252cf8e5289SKyle Evans			func = "fgets",
253cf8e5289SKyle Evans			arguments = {
254cf8e5289SKyle Evans				"__buf",
255cf8e5289SKyle Evans				"__len",
256cf8e5289SKyle Evans				"fp",
257cf8e5289SKyle Evans			},
258cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
259cf8e5289SKyle Evans			stackvars = "\tFILE *fp;\n",
260cf8e5289SKyle Evans			init = [[
261cf8e5289SKyle Evans	fp = new_fp(__len);
262cf8e5289SKyle Evans]],
263cf8e5289SKyle Evans		},
264020d003cSKyle Evans	},
265020d003cSKyle Evans	string = {
266020d003cSKyle Evans		-- <string.h>
267020d003cSKyle Evans		{
268020d003cSKyle Evans			func = "memcpy",
269020d003cSKyle Evans			arguments = {
270020d003cSKyle Evans				"__buf",
271020d003cSKyle Evans				"src",
272020d003cSKyle Evans				"__len",
273020d003cSKyle Evans			},
274020d003cSKyle Evans			exclude = excludes_stack_overflow,
275020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
276020d003cSKyle Evans		},
277020d003cSKyle Evans		{
278cf8e5289SKyle Evans			func = "mempcpy",
279cf8e5289SKyle Evans			arguments = {
280cf8e5289SKyle Evans				"__buf",
281cf8e5289SKyle Evans				"src",
282cf8e5289SKyle Evans				"__len",
283cf8e5289SKyle Evans			},
284cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
285cf8e5289SKyle Evans			stackvars = "\tchar src[__len + 10];\n",
286cf8e5289SKyle Evans		},
287cf8e5289SKyle Evans		{
288020d003cSKyle Evans			func = "memmove",
289020d003cSKyle Evans			arguments = {
290020d003cSKyle Evans				"__buf",
291020d003cSKyle Evans				"src",
292020d003cSKyle Evans				"__len",
293020d003cSKyle Evans			},
294020d003cSKyle Evans			exclude = excludes_stack_overflow,
295020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
296020d003cSKyle Evans		},
297020d003cSKyle Evans		{
298020d003cSKyle Evans			func = "memset",
299020d003cSKyle Evans			arguments = {
300020d003cSKyle Evans				"__buf",
301020d003cSKyle Evans				"0",
302020d003cSKyle Evans				"__len",
303020d003cSKyle Evans			},
304020d003cSKyle Evans			exclude = excludes_stack_overflow,
305020d003cSKyle Evans		},
306020d003cSKyle Evans		{
307020d003cSKyle Evans			func = "stpcpy",
308020d003cSKyle Evans			arguments = {
309020d003cSKyle Evans				"__buf",
310020d003cSKyle Evans				"src",
311020d003cSKyle Evans			},
312020d003cSKyle Evans			exclude = excludes_stack_overflow,
313020d003cSKyle Evans			stackvars = string_stackvars,
314020d003cSKyle Evans			init = string_init,
315020d003cSKyle Evans			uses_len = true,
316020d003cSKyle Evans		},
317020d003cSKyle Evans		{
318020d003cSKyle Evans			func = "stpncpy",
319020d003cSKyle Evans			arguments = {
320020d003cSKyle Evans				"__buf",
321020d003cSKyle Evans				"src",
322020d003cSKyle Evans				"__len",
323020d003cSKyle Evans			},
324020d003cSKyle Evans			exclude = excludes_stack_overflow,
325020d003cSKyle Evans			stackvars = string_stackvars,
326020d003cSKyle Evans			init = string_init,
327020d003cSKyle Evans		},
328020d003cSKyle Evans		{
329020d003cSKyle Evans			func = "strcat",
330020d003cSKyle Evans			arguments = {
331020d003cSKyle Evans				"__buf",
332020d003cSKyle Evans				"src",
333020d003cSKyle Evans			},
334020d003cSKyle Evans			exclude = excludes_stack_overflow,
335020d003cSKyle Evans			stackvars = string_stackvars,
336020d003cSKyle Evans			init = string_init,
337020d003cSKyle Evans			uses_len = true,
338020d003cSKyle Evans		},
339020d003cSKyle Evans		{
340cf8e5289SKyle Evans			func = "strlcat",
341cf8e5289SKyle Evans			arguments = {
342cf8e5289SKyle Evans				"__buf",
343cf8e5289SKyle Evans				"src",
344cf8e5289SKyle Evans				"__len",
345cf8e5289SKyle Evans			},
346cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
347cf8e5289SKyle Evans			stackvars = string_stackvars,
348cf8e5289SKyle Evans			init = string_init,
349cf8e5289SKyle Evans		},
350cf8e5289SKyle Evans		{
351020d003cSKyle Evans			func = "strncat",
352020d003cSKyle Evans			arguments = {
353020d003cSKyle Evans				"__buf",
354020d003cSKyle Evans				"src",
355020d003cSKyle Evans				"__len",
356020d003cSKyle Evans			},
357020d003cSKyle Evans			exclude = excludes_stack_overflow,
358020d003cSKyle Evans			stackvars = string_stackvars,
359020d003cSKyle Evans			init = string_init,
360020d003cSKyle Evans		},
361020d003cSKyle Evans		{
362020d003cSKyle Evans			func = "strcpy",
363020d003cSKyle Evans			arguments = {
364020d003cSKyle Evans				"__buf",
365020d003cSKyle Evans				"src",
366020d003cSKyle Evans			},
367020d003cSKyle Evans			exclude = excludes_stack_overflow,
368020d003cSKyle Evans			stackvars = string_stackvars,
369020d003cSKyle Evans			init = string_init,
370020d003cSKyle Evans			uses_len = true,
371020d003cSKyle Evans		},
372020d003cSKyle Evans		{
373cf8e5289SKyle Evans			func = "strlcpy",
374cf8e5289SKyle Evans			arguments = {
375cf8e5289SKyle Evans				"__buf",
376cf8e5289SKyle Evans				"src",
377cf8e5289SKyle Evans				"__len",
378cf8e5289SKyle Evans			},
379cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
380cf8e5289SKyle Evans			stackvars = string_stackvars,
381cf8e5289SKyle Evans			init = string_init,
382cf8e5289SKyle Evans		},
383cf8e5289SKyle Evans		{
384020d003cSKyle Evans			func = "strncpy",
385020d003cSKyle Evans			arguments = {
386020d003cSKyle Evans				"__buf",
387020d003cSKyle Evans				"src",
388020d003cSKyle Evans				"__len",
389020d003cSKyle Evans			},
390020d003cSKyle Evans			exclude = excludes_stack_overflow,
391020d003cSKyle Evans			stackvars = string_stackvars,
392020d003cSKyle Evans			init = string_init,
393020d003cSKyle Evans		},
394020d003cSKyle Evans	},
395020d003cSKyle Evans	strings = {
396020d003cSKyle Evans		-- <strings.h>
397020d003cSKyle Evans		{
398020d003cSKyle Evans			func = "bcopy",
399020d003cSKyle Evans			arguments = {
400020d003cSKyle Evans				"src",
401020d003cSKyle Evans				"__buf",
402020d003cSKyle Evans				"__len",
403020d003cSKyle Evans			},
404020d003cSKyle Evans			exclude = excludes_stack_overflow,
405020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
406020d003cSKyle Evans		},
407020d003cSKyle Evans		{
408020d003cSKyle Evans			func = "bzero",
409020d003cSKyle Evans			arguments = {
410020d003cSKyle Evans				"__buf",
411020d003cSKyle Evans				"__len",
412020d003cSKyle Evans			},
413020d003cSKyle Evans			exclude = excludes_stack_overflow,
414020d003cSKyle Evans		},
415cf8e5289SKyle Evans		{
416cf8e5289SKyle Evans			func = "explicit_bzero",
417cf8e5289SKyle Evans			arguments = {
418cf8e5289SKyle Evans				"__buf",
419cf8e5289SKyle Evans				"__len",
420cf8e5289SKyle Evans			},
421cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
422cf8e5289SKyle Evans		},
423020d003cSKyle Evans	},
424020d003cSKyle Evans	unistd = {
425020d003cSKyle Evans		-- <unistd.h>
426020d003cSKyle Evans		{
427020d003cSKyle Evans			func = "getcwd",
428020d003cSKyle Evans			bufsize = "8",
429020d003cSKyle Evans			arguments = {
430020d003cSKyle Evans				"__buf",
431020d003cSKyle Evans				"__len",
432020d003cSKyle Evans			},
433020d003cSKyle Evans			exclude = excludes_stack_overflow,
434020d003cSKyle Evans		},
435020d003cSKyle Evans		{
436cf8e5289SKyle Evans			func = "getgrouplist",
437cf8e5289SKyle Evans			bufsize = "4",
438cf8e5289SKyle Evans			buftype = "gid_t[]",
439cf8e5289SKyle Evans			arguments = {
440cf8e5289SKyle Evans				"\"root\"",
441cf8e5289SKyle Evans				"0",
442cf8e5289SKyle Evans				"__buf",
443cf8e5289SKyle Evans				"&intlen",
444cf8e5289SKyle Evans			},
445cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
446cf8e5289SKyle Evans			stackvars = "\tint intlen = (int)__len;\n",
447cf8e5289SKyle Evans			uses_len = true,
448cf8e5289SKyle Evans		},
449cf8e5289SKyle Evans		{
450cf8e5289SKyle Evans			func = "getgroups",
451cf8e5289SKyle Evans			bufsize = "4",
452cf8e5289SKyle Evans			buftype = "gid_t[]",
453cf8e5289SKyle Evans			arguments = {
454cf8e5289SKyle Evans				"__len",
455cf8e5289SKyle Evans				"__buf",
456cf8e5289SKyle Evans			},
457cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
458cf8e5289SKyle Evans		},
459cf8e5289SKyle Evans		{
460cf8e5289SKyle Evans			func = "getloginclass",
461cf8e5289SKyle Evans			arguments = {
462cf8e5289SKyle Evans				"__buf",
463cf8e5289SKyle Evans				"__len",
464cf8e5289SKyle Evans			},
465cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
466cf8e5289SKyle Evans		},
467cf8e5289SKyle Evans		{
468cf8e5289SKyle Evans			func = "pread",
469cf8e5289SKyle Evans			bufsize = "41",
470cf8e5289SKyle Evans			arguments = {
471cf8e5289SKyle Evans				"fd",
472cf8e5289SKyle Evans				"__buf",
473cf8e5289SKyle Evans				"__len",
474cf8e5289SKyle Evans				"0",
475cf8e5289SKyle Evans			},
476cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
477cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
478cf8e5289SKyle Evans			init = [[
479cf8e5289SKyle Evans	fd = new_tmpfile();	/* Cannot fail */
480cf8e5289SKyle Evans]],
481cf8e5289SKyle Evans		},
482cf8e5289SKyle Evans		{
483020d003cSKyle Evans			func = "read",
484020d003cSKyle Evans			bufsize = "41",
485020d003cSKyle Evans			arguments = {
486020d003cSKyle Evans				"fd",
487020d003cSKyle Evans				"__buf",
488020d003cSKyle Evans				"__len",
489020d003cSKyle Evans			},
490020d003cSKyle Evans			exclude = excludes_stack_overflow,
491020d003cSKyle Evans			stackvars = "\tint fd;\n",
492020d003cSKyle Evans			init = [[
493020d003cSKyle Evans	fd = new_tmpfile();	/* Cannot fail */
494020d003cSKyle Evans]],
495020d003cSKyle Evans		},
496020d003cSKyle Evans		{
497020d003cSKyle Evans			func = "readlink",
498020d003cSKyle Evans			arguments = {
499020d003cSKyle Evans				"path",
500020d003cSKyle Evans				"__buf",
501020d003cSKyle Evans				"__len",
502020d003cSKyle Evans			},
503020d003cSKyle Evans			exclude = excludes_stack_overflow,
504020d003cSKyle Evans			stackvars = "\tconst char *path;\n",
505020d003cSKyle Evans			init = [[
506020d003cSKyle Evans	path = new_symlink(__len);		/* Cannot fail */
507020d003cSKyle Evans]],
508020d003cSKyle Evans		},
509cf8e5289SKyle Evans		{
510cf8e5289SKyle Evans			func = "readlinkat",
511cf8e5289SKyle Evans			arguments = {
512cf8e5289SKyle Evans				"AT_FDCWD",
513cf8e5289SKyle Evans				"path",
514cf8e5289SKyle Evans				"__buf",
515cf8e5289SKyle Evans				"__len",
516cf8e5289SKyle Evans			},
517cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
518cf8e5289SKyle Evans			stackvars = "\tconst char *path;\n",
519cf8e5289SKyle Evans			init = [[
520cf8e5289SKyle Evans	path = new_symlink(__len);		/* Cannot fail */
521cf8e5289SKyle Evans]],
522cf8e5289SKyle Evans		},
523cf8e5289SKyle Evans		{
524cf8e5289SKyle Evans			func = "getdomainname",
525cf8e5289SKyle Evans			bufsize = "4",
526cf8e5289SKyle Evans			arguments = {
527cf8e5289SKyle Evans				"__buf",
528cf8e5289SKyle Evans				"__len",
529cf8e5289SKyle Evans			},
530cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
531cf8e5289SKyle Evans			stackvars = "\tchar sysdomain[256];\n",
532cf8e5289SKyle Evans			early_init = [[
533cf8e5289SKyle Evans	(void)getdomainname(sysdomain, __len);
534cf8e5289SKyle Evans	if (strlen(sysdomain) <= __len)
535cf8e5289SKyle Evans		atf_tc_skip("domain name too short for testing");
536cf8e5289SKyle Evans]]
537cf8e5289SKyle Evans		},
538cf8e5289SKyle Evans		{
539cf8e5289SKyle Evans			func = "getentropy",
540cf8e5289SKyle Evans			arguments = {
541cf8e5289SKyle Evans				"__buf",
542cf8e5289SKyle Evans				"__len",
543cf8e5289SKyle Evans			},
544cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
545cf8e5289SKyle Evans		},
546cf8e5289SKyle Evans		{
547cf8e5289SKyle Evans			func = "gethostname",
548cf8e5289SKyle Evans			bufsize = "4",
549cf8e5289SKyle Evans			arguments = {
550cf8e5289SKyle Evans				"__buf",
551cf8e5289SKyle Evans				"__len",
552cf8e5289SKyle Evans			},
553cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
554cf8e5289SKyle Evans			stackvars = [[
555cf8e5289SKyle Evans	char syshost[256];
556cf8e5289SKyle Evans	int error;
557cf8e5289SKyle Evans]],
558cf8e5289SKyle Evans			early_init = [[
559cf8e5289SKyle Evans	error = gethostname(syshost, __len);
560cf8e5289SKyle Evans	if (error != 0 || strlen(syshost) <= __len)
561cf8e5289SKyle Evans		atf_tc_skip("hostname too short for testing");
562cf8e5289SKyle Evans]]
563cf8e5289SKyle Evans		},
564cf8e5289SKyle Evans		{
565cf8e5289SKyle Evans			func = "getlogin_r",
566cf8e5289SKyle Evans			bufsize = "MAXLOGNAME + 1",
567cf8e5289SKyle Evans			arguments = {
568cf8e5289SKyle Evans				"__buf",
569cf8e5289SKyle Evans				"__len",
570cf8e5289SKyle Evans			},
571cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
572cf8e5289SKyle Evans		},
573cf8e5289SKyle Evans		{
574cf8e5289SKyle Evans			func = "ttyname_r",
575cf8e5289SKyle Evans			arguments = {
576cf8e5289SKyle Evans				"fd",
577cf8e5289SKyle Evans				"__buf",
578cf8e5289SKyle Evans				"__len",
579cf8e5289SKyle Evans			},
580cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
581cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
582cf8e5289SKyle Evans			early_init = [[
583cf8e5289SKyle Evans	fd = STDIN_FILENO;
584cf8e5289SKyle Evans	if (!isatty(fd))
585cf8e5289SKyle Evans		atf_tc_skip("stdin is not an fd");
586cf8e5289SKyle Evans]]
587cf8e5289SKyle Evans		},
588020d003cSKyle Evans	},
589020d003cSKyle Evans}
590020d003cSKyle Evans
591020d003cSKyle Evanslocal function write_test_boilerplate(fh, name, body)
592020d003cSKyle Evans	fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
593020d003cSKyle Evans	fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
594020d003cSKyle Evans	fh:write("{\n" .. body .. "\n}\n\n")
595020d003cSKyle Evans	return name
596020d003cSKyle Evansend
597020d003cSKyle Evans
598020d003cSKyle Evanslocal function generate_test_name(func, variant, disposition, heap)
599020d003cSKyle Evans	local basename = func
600020d003cSKyle Evans	if variant then
601020d003cSKyle Evans		basename = basename .. "_" .. variant
602020d003cSKyle Evans	end
603020d003cSKyle Evans	if heap then
604020d003cSKyle Evans		basename = basename .. "_heap"
605020d003cSKyle Evans	end
606020d003cSKyle Evans	if disposition < 0 then
607020d003cSKyle Evans		return basename .. "_before_end"
608020d003cSKyle Evans	elseif disposition == 0 then
609020d003cSKyle Evans		return basename .. "_end"
610020d003cSKyle Evans	else
611020d003cSKyle Evans		return basename .. "_after_end"
612020d003cSKyle Evans	end
613020d003cSKyle Evansend
614020d003cSKyle Evans
615020d003cSKyle Evanslocal function array_type(buftype)
616020d003cSKyle Evans	if not buftype:match("%[%]") then
617020d003cSKyle Evans		return nil
618020d003cSKyle Evans	end
619020d003cSKyle Evans
620020d003cSKyle Evans	return buftype:gsub("%[%]", "")
621020d003cSKyle Evansend
622020d003cSKyle Evans
623020d003cSKyle Evanslocal function configurable(def, idx)
624020d003cSKyle Evans	local cfgitem = def[idx]
625020d003cSKyle Evans
626020d003cSKyle Evans	if not cfgitem then
627020d003cSKyle Evans		return nil
628020d003cSKyle Evans	end
629020d003cSKyle Evans
630020d003cSKyle Evans	if type(cfgitem) == "function" then
631020d003cSKyle Evans		return cfgitem()
632020d003cSKyle Evans	end
633020d003cSKyle Evans
634020d003cSKyle Evans	return cfgitem
635020d003cSKyle Evansend
636020d003cSKyle Evans
637020d003cSKyle Evanslocal function generate_stackframe(buftype, bufsize, disposition, heap, def)
638020d003cSKyle Evans	local function len_offset(inverted, disposition)
639020d003cSKyle Evans		-- Tests that don't use __len in their arguments may use an
640020d003cSKyle Evans		-- inverted sense because we can't just specify a length that
641020d003cSKyle Evans		-- would induce an access just after the end.  Instead, we have
642020d003cSKyle Evans		-- to manipulate the buffer size to be too short so that the
643020d003cSKyle Evans		-- function under test would write one too many.
644020d003cSKyle Evans		if disposition < 0 then
645020d003cSKyle Evans			return ((inverted and " + ") or " - ") .. "1"
646020d003cSKyle Evans		elseif disposition == 0 then
647020d003cSKyle Evans			return ""
648020d003cSKyle Evans		else
649020d003cSKyle Evans			return ((inverted and " - ") or " + ") .. "1"
650020d003cSKyle Evans		end
651020d003cSKyle Evans	end
652020d003cSKyle Evans
653020d003cSKyle Evans	local function test_uses_len(def)
654020d003cSKyle Evans		if def.uses_len then
655020d003cSKyle Evans			return true
656020d003cSKyle Evans		end
657020d003cSKyle Evans
658020d003cSKyle Evans		for _, arg in ipairs(def.arguments) do
659020d003cSKyle Evans			if arg:match("__len") or arg:match("__idx") then
660020d003cSKyle Evans				return true
661020d003cSKyle Evans			end
662020d003cSKyle Evans		end
663020d003cSKyle Evans
664020d003cSKyle Evans		return false
665020d003cSKyle Evans	end
666020d003cSKyle Evans
667020d003cSKyle Evans
668020d003cSKyle Evans	-- This is perhaps a little convoluted, but we toss the buffer into a
669020d003cSKyle Evans	-- struct on the stack to guarantee that we have at least one valid
670020d003cSKyle Evans	-- byte on either side of the buffer -- a measure to make sure that
671020d003cSKyle Evans	-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
672020d003cSKyle Evans	-- rather than some other stack or memory protection.
673020d003cSKyle Evans	local vars = "\tstruct {\n"
674020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_l;\n"
675020d003cSKyle Evans
676020d003cSKyle Evans	local uses_len = test_uses_len(def)
677020d003cSKyle Evans	local bufsize_offset = len_offset(not uses_len, disposition)
678020d003cSKyle Evans	local buftype_elem = array_type(buftype)
679020d003cSKyle Evans	local size_expr = bufsize
680020d003cSKyle Evans
681020d003cSKyle Evans	if not uses_len then
682020d003cSKyle Evans		-- If the length isn't in use, we have to vary the buffer size
683020d003cSKyle Evans		-- since the fortified function likely has some internal size
684020d003cSKyle Evans		-- constraint that it's supposed to be checking.
685020d003cSKyle Evans		size_expr = size_expr .. bufsize_offset
686020d003cSKyle Evans	end
687020d003cSKyle Evans
688020d003cSKyle Evans	if not heap and buftype_elem then
689020d003cSKyle Evans		-- Array type: size goes after identifier
690020d003cSKyle Evans		vars = vars .. "\t\t" .. buftype_elem ..
691020d003cSKyle Evans		    " __buf[" .. size_expr .. "];\n"
692020d003cSKyle Evans	else
693020d003cSKyle Evans		local basic_type = buftype_elem or buftype
694020d003cSKyle Evans
695020d003cSKyle Evans		-- Heap tests obviously just put a pointer on the stack that
696020d003cSKyle Evans		-- points to our new allocation, but we leave it in the padded
697020d003cSKyle Evans		-- struct just to simplify our generator.
698020d003cSKyle Evans		if heap then
699020d003cSKyle Evans			basic_type = basic_type .. " *"
700020d003cSKyle Evans		end
701020d003cSKyle Evans		vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
702020d003cSKyle Evans	end
703020d003cSKyle Evans
704020d003cSKyle Evans	-- padding_r is our just-past-the-end padding that we use to make sure
705020d003cSKyle Evans	-- that there's a valid portion after the buffer that isn't being
706020d003cSKyle Evans	-- included in our function calls.  If we didn't have it, then we'd have
707020d003cSKyle Evans	-- a hard time feeling confident that an abort on the just-after tests
708020d003cSKyle Evans	-- isn't maybe from some other memory or stack protection.
709020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_r;\n"
710020d003cSKyle Evans	vars = vars .. "\t} __stack;\n"
711020d003cSKyle Evans
712020d003cSKyle Evans	-- Not all tests will use __bufsz, but some do for, e.g., clearing
713020d003cSKyle Evans	-- memory..
714020d003cSKyle Evans	vars = vars .. "\tconst size_t __bufsz __unused = "
715020d003cSKyle Evans	if heap then
716020d003cSKyle Evans		local scalar = 1
717020d003cSKyle Evans		if buftype_elem then
718020d003cSKyle Evans			scalar = size_expr
719020d003cSKyle Evans		end
720020d003cSKyle Evans
721020d003cSKyle Evans		vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
722020d003cSKyle Evans	else
723020d003cSKyle Evans		vars = vars .. "sizeof(__stack.__buf);\n"
724020d003cSKyle Evans	end
725020d003cSKyle Evans
726020d003cSKyle Evans	vars = vars .. "\tconst size_t __len = " .. bufsize ..
727020d003cSKyle Evans	    bufsize_offset .. ";\n"
728020d003cSKyle Evans	vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
729020d003cSKyle Evans
730020d003cSKyle Evans	-- For overflow testing, we need to fork() because we're expecting the
731020d003cSKyle Evans	-- test to ultimately abort()/_exit().  Then we can collect the exit
732020d003cSKyle Evans	-- status and report appropriately.
733020d003cSKyle Evans	if disposition > 0 then
734020d003cSKyle Evans		vars = vars .. "\tpid_t __child;\n"
735020d003cSKyle Evans		vars = vars .. "\tint __status;\n"
736020d003cSKyle Evans	end
737020d003cSKyle Evans
738020d003cSKyle Evans	-- Any other stackvars defined by the test get placed after everything
739020d003cSKyle Evans	-- else.
740020d003cSKyle Evans	vars = vars .. (configurable(def, "stackvars") or "")
741020d003cSKyle Evans
742020d003cSKyle Evans	return vars
743020d003cSKyle Evansend
744020d003cSKyle Evans
745020d003cSKyle Evanslocal function write_test(fh, func, disposition, heap, def)
746020d003cSKyle Evans	local testname = generate_test_name(func, def.variant, disposition, heap)
747020d003cSKyle Evans	local buftype = def.buftype or "unsigned char[]"
748020d003cSKyle Evans	local bufsize = def.bufsize or 42
749020d003cSKyle Evans	local body = ""
750020d003cSKyle Evans
751020d003cSKyle Evans	if def.exclude and def.exclude(disposition, heap) then
752020d003cSKyle Evans		return
753020d003cSKyle Evans	end
754020d003cSKyle Evans
755020d003cSKyle Evans	local function need_addr(buftype)
756020d003cSKyle Evans		return not (buftype:match("%[%]") or buftype:match("%*"))
757020d003cSKyle Evans	end
758020d003cSKyle Evans
759020d003cSKyle Evans	if heap then
760020d003cSKyle Evans		body = body .. "#define BUF __stack.__buf\n"
761020d003cSKyle Evans	else
762020d003cSKyle Evans		body = body .. "#define BUF &__stack.__buf\n"
763020d003cSKyle Evans	end
764020d003cSKyle Evans
765020d003cSKyle Evans	-- Setup the buffer
766020d003cSKyle Evans	body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
767020d003cSKyle Evans	    "\n"
768020d003cSKyle Evans
769020d003cSKyle Evans	-- Any early initialization goes before we would fork for the just-after
770020d003cSKyle Evans	-- tests, because they may want to skip the test based on some criteria
771020d003cSKyle Evans	-- and we can't propagate that up very easily once we're forked.
772020d003cSKyle Evans	local early_init = configurable(def, "early_init")
773020d003cSKyle Evans	body = body .. (early_init or "")
774020d003cSKyle Evans	if early_init then
775020d003cSKyle Evans		body = body .. "\n"
776020d003cSKyle Evans	end
777020d003cSKyle Evans
778020d003cSKyle Evans	-- Fork off, iff we're testing some access past the end of the buffer.
779020d003cSKyle Evans	if disposition > 0 then
780020d003cSKyle Evans		body = body .. [[
781020d003cSKyle Evans	__child = fork();
782020d003cSKyle Evans	ATF_REQUIRE(__child >= 0);
783020d003cSKyle Evans	if (__child > 0)
784020d003cSKyle Evans		goto monitor;
785020d003cSKyle Evans
786020d003cSKyle Evans	/* Child */
787020d003cSKyle Evans	disable_coredumps();
788020d003cSKyle Evans]]
789020d003cSKyle Evans	end
790020d003cSKyle Evans
791020d003cSKyle Evans	local bufvar = "__stack.__buf"
792020d003cSKyle Evans	if heap then
793020d003cSKyle Evans		-- Buffer needs to be initialized because it's a heap allocation.
794020d003cSKyle Evans		body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
795020d003cSKyle Evans	end
796020d003cSKyle Evans
797020d003cSKyle Evans	-- Non-early init happens just after the fork in the child, not the
798020d003cSKyle Evans	-- monitor.  This is used to setup any other buffers we may need, for
799020d003cSKyle Evans	-- instance.
800020d003cSKyle Evans	local extra_init = configurable(def, "init")
801020d003cSKyle Evans	body = body .. (extra_init or "")
802020d003cSKyle Evans
803020d003cSKyle Evans	if heap or extra_init then
804020d003cSKyle Evans		body = body .. "\n"
805020d003cSKyle Evans	end
806020d003cSKyle Evans
807020d003cSKyle Evans	-- Setup the function call with arguments as described in the test
808020d003cSKyle Evans	-- definition.
809020d003cSKyle Evans	body = body .. "\t" .. func .. "("
810020d003cSKyle Evans
811020d003cSKyle Evans	for idx, arg in ipairs(def.arguments) do
812020d003cSKyle Evans		if idx > 1 then
813020d003cSKyle Evans			body = body .. ", "
814020d003cSKyle Evans		end
815020d003cSKyle Evans
816020d003cSKyle Evans		if arg == "__buf" then
817020d003cSKyle Evans			if not heap and need_addr(buftype) then
818020d003cSKyle Evans				body = body .. "&"
819020d003cSKyle Evans			end
820020d003cSKyle Evans
821020d003cSKyle Evans			body = body .. bufvar
822020d003cSKyle Evans		else
823020d003cSKyle Evans			local argname = arg
824020d003cSKyle Evans
825020d003cSKyle Evans			if def.value_of then
826020d003cSKyle Evans				argname = argname or def.value_of(arg)
827020d003cSKyle Evans			end
828020d003cSKyle Evans
829020d003cSKyle Evans			body = body .. argname
830020d003cSKyle Evans		end
831020d003cSKyle Evans	end
832020d003cSKyle Evans
833020d003cSKyle Evans	body = body .. ");\n"
834020d003cSKyle Evans
835020d003cSKyle Evans	-- Monitor stuff follows, for OOB access.
836020d003cSKyle Evans	if disposition <= 0 then
837020d003cSKyle Evans		goto skip
838020d003cSKyle Evans	end
839020d003cSKyle Evans
840020d003cSKyle Evans	body = body .. [[
841020d003cSKyle Evans	_exit(EX_SOFTWARE);	/* Should have aborted. */
842020d003cSKyle Evans
843020d003cSKyle Evansmonitor:
844020d003cSKyle Evans	while (waitpid(__child, &__status, 0) != __child) {
845020d003cSKyle Evans		ATF_REQUIRE_EQ(EINTR, errno);
846020d003cSKyle Evans	}
847020d003cSKyle Evans
848020d003cSKyle Evans	if (!WIFSIGNALED(__status)) {
849020d003cSKyle Evans		switch (WEXITSTATUS(__status)) {
850020d003cSKyle Evans		case EX_SOFTWARE:
851020d003cSKyle Evans			atf_tc_fail("FORTIFY_SOURCE failed to abort");
852020d003cSKyle Evans			break;
853020d003cSKyle Evans		case EX_OSERR:
854020d003cSKyle Evans			atf_tc_fail("setrlimit(2) failed");
855020d003cSKyle Evans			break;
856020d003cSKyle Evans		default:
857020d003cSKyle Evans			atf_tc_fail("child exited with status %d",
858020d003cSKyle Evans			    WEXITSTATUS(__status));
859020d003cSKyle Evans		}
860020d003cSKyle Evans	} else {
861020d003cSKyle Evans		ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
862020d003cSKyle Evans	}
863020d003cSKyle Evans]]
864020d003cSKyle Evans
865020d003cSKyle Evans::skip::
866020d003cSKyle Evans	body = body .. "#undef BUF\n"
867020d003cSKyle Evans	return write_test_boilerplate(fh, testname, body)
868020d003cSKyle Evansend
869020d003cSKyle Evans
870020d003cSKyle Evans-- main()
871020d003cSKyle Evanslocal tests
872020d003cSKyle Evanslocal tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
873020d003cSKyle Evansfor k, defs in pairs(all_tests) do
874020d003cSKyle Evans	if k == tcat then
875020d003cSKyle Evans		tests = defs
876020d003cSKyle Evans		break
877020d003cSKyle Evans	end
878020d003cSKyle Evansend
879020d003cSKyle Evans
880020d003cSKyle Evansassert(tests, "category " .. tcat .. " not found")
881020d003cSKyle Evans
882020d003cSKyle Evanslocal fh = io.stdout
883020d003cSKyle Evansfh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
884020d003cSKyle Evans    tcat .. "\"` */\n\n")
885020d003cSKyle Evansfh:write("#define	_FORTIFY_SOURCE	2\n")
886020d003cSKyle Evansfh:write("#define	TMPFILE_SIZE	(1024 * 32)\n")
887020d003cSKyle Evans
888020d003cSKyle Evansfh:write("\n")
889020d003cSKyle Evansfor _, inc in ipairs(includes) do
890020d003cSKyle Evans	fh:write("#include <" .. inc .. ">\n")
891020d003cSKyle Evansend
892020d003cSKyle Evans
893020d003cSKyle Evansfh:write([[
894020d003cSKyle Evans
895cf8e5289SKyle Evansstatic FILE * __unused
896cf8e5289SKyle Evansnew_fp(size_t __len)
897cf8e5289SKyle Evans{
898cf8e5289SKyle Evans	static char fpbuf[LINE_MAX];
899cf8e5289SKyle Evans	FILE *fp;
900cf8e5289SKyle Evans
901cf8e5289SKyle Evans	ATF_REQUIRE(__len <= sizeof(fpbuf));
902cf8e5289SKyle Evans
903cf8e5289SKyle Evans	memset(fpbuf, 'A', sizeof(fpbuf) - 1);
904cf8e5289SKyle Evans	fpbuf[sizeof(fpbuf) - 1] = '\0';
905cf8e5289SKyle Evans
906cf8e5289SKyle Evans	fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
907cf8e5289SKyle Evans	ATF_REQUIRE(fp != NULL);
908cf8e5289SKyle Evans
909cf8e5289SKyle Evans	return (fp);
910cf8e5289SKyle Evans}
911cf8e5289SKyle Evans
912020d003cSKyle Evans/*
913020d003cSKyle Evans * Create a new symlink to use for readlink(2) style tests, we'll just use a
914020d003cSKyle Evans * random target name to have something interesting to look at.
915020d003cSKyle Evans */
916020d003cSKyle Evansstatic const char * __unused
917020d003cSKyle Evansnew_symlink(size_t __len)
918020d003cSKyle Evans{
919020d003cSKyle Evans	static const char linkname[] = "link";
920020d003cSKyle Evans	char target[MAXNAMLEN];
921020d003cSKyle Evans	int error;
922020d003cSKyle Evans
923020d003cSKyle Evans	ATF_REQUIRE(__len <= sizeof(target));
924020d003cSKyle Evans
925020d003cSKyle Evans	arc4random_buf(target, sizeof(target));
926020d003cSKyle Evans
927020d003cSKyle Evans	error = unlink(linkname);
928020d003cSKyle Evans	ATF_REQUIRE(error == 0 || errno == ENOENT);
929020d003cSKyle Evans
930020d003cSKyle Evans	error = symlink(target, linkname);
931020d003cSKyle Evans	ATF_REQUIRE(error == 0);
932020d003cSKyle Evans
933020d003cSKyle Evans	return (linkname);
934020d003cSKyle Evans}
935020d003cSKyle Evans
936020d003cSKyle Evans/*
937020d003cSKyle Evans * Constructs a tmpfile that we can use for testing read(2) and friends.
938020d003cSKyle Evans */
939020d003cSKyle Evansstatic int __unused
940020d003cSKyle Evansnew_tmpfile(void)
941020d003cSKyle Evans{
942020d003cSKyle Evans	char buf[1024];
943020d003cSKyle Evans	ssize_t rv;
944020d003cSKyle Evans	size_t written;
945020d003cSKyle Evans	int fd;
946020d003cSKyle Evans
947020d003cSKyle Evans	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
948020d003cSKyle Evans	ATF_REQUIRE(fd >= 0);
949020d003cSKyle Evans
950020d003cSKyle Evans	written = 0;
951020d003cSKyle Evans	while (written < TMPFILE_SIZE) {
952020d003cSKyle Evans		rv = write(fd, buf, sizeof(buf));
953020d003cSKyle Evans		ATF_REQUIRE(rv > 0);
954020d003cSKyle Evans
955020d003cSKyle Evans		written += rv;
956020d003cSKyle Evans	}
957020d003cSKyle Evans
958020d003cSKyle Evans	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
959020d003cSKyle Evans	return (fd);
960020d003cSKyle Evans}
961020d003cSKyle Evans
962020d003cSKyle Evansstatic void
963020d003cSKyle Evansdisable_coredumps(void)
964020d003cSKyle Evans{
965020d003cSKyle Evans	struct rlimit rl = { 0 };
966020d003cSKyle Evans
967020d003cSKyle Evans	if (setrlimit(RLIMIT_CORE, &rl) == -1)
968020d003cSKyle Evans		_exit(EX_OSERR);
969020d003cSKyle Evans}
970020d003cSKyle Evans
971cf8e5289SKyle Evans/*
972cf8e5289SKyle Evans * Replaces stdin with a file that we can actually read from, for tests where
973cf8e5289SKyle Evans * we want a FILE * or fd that we can get data from.
974cf8e5289SKyle Evans */
975cf8e5289SKyle Evansstatic void __unused
976cf8e5289SKyle Evansreplace_stdin(void)
977cf8e5289SKyle Evans{
978cf8e5289SKyle Evans	int fd;
979cf8e5289SKyle Evans
980cf8e5289SKyle Evans	fd = new_tmpfile();
981cf8e5289SKyle Evans
982cf8e5289SKyle Evans	(void)dup2(fd, STDIN_FILENO);
983cf8e5289SKyle Evans	if (fd != STDIN_FILENO)
984cf8e5289SKyle Evans		close(fd);
985cf8e5289SKyle Evans}
986cf8e5289SKyle Evans
987020d003cSKyle Evans]])
988020d003cSKyle Evans
989020d003cSKyle Evansfor _, def in pairs(tests) do
990020d003cSKyle Evans	local func = def.func
991020d003cSKyle Evans	local function write_tests(heap)
992020d003cSKyle Evans		-- Dispositions here are relative to the buffer size prescribed
993020d003cSKyle Evans		-- by the test definition.
994020d003cSKyle Evans		local dispositions = def.dispositions or { -1, 0, 1 }
995020d003cSKyle Evans
996020d003cSKyle Evans		for _, disposition in ipairs(dispositions) do
997020d003cSKyle Evans			tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
998020d003cSKyle Evans		end
999020d003cSKyle Evans	end
1000020d003cSKyle Evans
1001020d003cSKyle Evans	write_tests(false)
1002020d003cSKyle Evans	write_tests(true)
1003020d003cSKyle Evansend
1004020d003cSKyle Evans
1005020d003cSKyle Evansfh:write("ATF_TP_ADD_TCS(tp)\n")
1006020d003cSKyle Evansfh:write("{\n")
1007020d003cSKyle Evansfor idx = 1, #tests_added do
1008020d003cSKyle Evans	fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
1009020d003cSKyle Evansend
1010020d003cSKyle Evansfh:write("\treturn (atf_no_error());\n")
1011020d003cSKyle Evansfh:write("}\n")
1012