xref: /freebsd/lib/libc/tests/secure/generate-fortify-tests.lua (revision 062d9380b98663eb2a4a3d7ce9e919e743984060)
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",
65*062d9380SKyle Evans	"sys/random.h",
66020d003cSKyle Evans	"sys/resource.h",
67020d003cSKyle Evans	"sys/time.h",
68020d003cSKyle Evans	"sys/wait.h",
69020d003cSKyle Evans	"dirent.h",
70020d003cSKyle Evans	"errno.h",
71020d003cSKyle Evans	"fcntl.h",
72020d003cSKyle Evans	"limits.h",
7388276dfbSKyle Evans	"poll.h",
74020d003cSKyle Evans	"signal.h",
75020d003cSKyle Evans	"stdio.h",
76020d003cSKyle Evans	"stdlib.h",
77020d003cSKyle Evans	"string.h",
78020d003cSKyle Evans	"strings.h",
79020d003cSKyle Evans	"sysexits.h",
80020d003cSKyle Evans	"unistd.h",
81b53d7aa8SKyle Evans	"wchar.h",
82020d003cSKyle Evans	"atf-c.h",
83020d003cSKyle Evans}
84020d003cSKyle Evans
85020d003cSKyle Evanslocal tests_added = {}
86020d003cSKyle Evans
87020d003cSKyle Evans-- Some of these will need to be excluded because clang sees the wrong size when
88020d003cSKyle Evans-- an array is embedded inside a struct, we'll get something that looks more
89020d003cSKyle Evans-- like __builtin_object_size(ptr, 0) than it does the correct
90020d003cSKyle Evans-- __builtin_object_size(ptr, 1) (i.e., includes the padding after).  This is
91020d003cSKyle Evans-- almost certainly a bug in llvm.
92020d003cSKyle Evanslocal function excludes_stack_overflow(disposition, is_heap)
93020d003cSKyle Evans	return (not is_heap) and disposition > 0
94020d003cSKyle Evansend
95020d003cSKyle Evans
9688276dfbSKyle Evanslocal poll_init = [[
9788276dfbSKyle Evans	for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
9888276dfbSKyle Evans		__stack.__buf[i].fd = -1;
9988276dfbSKyle Evans	}
10088276dfbSKyle Evans]]
10188276dfbSKyle Evans
102020d003cSKyle Evanslocal printf_stackvars = "\tchar srcvar[__len + 10];\n"
103020d003cSKyle Evanslocal printf_init = [[
104020d003cSKyle Evans	memset(srcvar, 'A', sizeof(srcvar) - 1);
105020d003cSKyle Evans	srcvar[sizeof(srcvar) - 1] = '\0';
106020d003cSKyle Evans]]
107020d003cSKyle Evans
108cf8e5289SKyle Evanslocal stdio_init = [[
109cf8e5289SKyle Evans	replace_stdin();
110cf8e5289SKyle Evans]]
111cf8e5289SKyle Evans
112020d003cSKyle Evanslocal string_stackvars = "\tchar src[__len];\n"
113020d003cSKyle Evanslocal string_init = [[
114020d003cSKyle Evans	memset(__stack.__buf, 0, __len);
115020d003cSKyle Evans	memset(src, 'A', __len - 1);
116020d003cSKyle Evans	src[__len - 1] = '\0';
117020d003cSKyle Evans]]
118020d003cSKyle Evans
119b53d7aa8SKyle Evanslocal wstring_stackvars = "\twchar_t src[__len];\n"
120b53d7aa8SKyle Evanslocal wstring_init = [[
121b53d7aa8SKyle Evans	wmemset(__stack.__buf, 0, __len);
122b53d7aa8SKyle Evans	wmemset(src, 'A', __len - 1);
123b53d7aa8SKyle Evans	src[__len - 1] = '\0';
124b53d7aa8SKyle Evans]]
125b53d7aa8SKyle Evans
126020d003cSKyle Evans-- Each test entry describes how to test a given function.  We need to know how
127020d003cSKyle Evans-- to construct the buffer, we need to know the argument set we're dealing with,
128020d003cSKyle Evans-- and we need to know what we're passing to each argument.  We could be passing
129020d003cSKyle Evans-- fixed values, or we could be passing the __buf under test.
130020d003cSKyle Evans--
131020d003cSKyle Evans-- definition:
132020d003cSKyle Evans--   func: name of the function under test to call
133020d003cSKyle Evans--   bufsize: size of buffer to generate, defaults to 42
134020d003cSKyle Evans--   buftype: type of buffer to generate, defaults to unsigned char[]
135020d003cSKyle Evans--   arguments: __buf, __len, or the name of a variable placed on the stack
136020d003cSKyle Evans--   exclude: a function(disposition, is_heap) that returns true if this combo
137020d003cSKyle Evans--     should be excluded.
138020d003cSKyle Evans--   stackvars: extra variables to be placed on the stack, should be a string
139020d003cSKyle Evans--     optionally formatted with tabs and newlines
140020d003cSKyle Evans--   init: extra code to inject just before the function call for initialization
141020d003cSKyle Evans--     of the buffer or any of the above-added stackvars; also a string
142020d003cSKyle Evans--   uses_len: bool-ish, necessary if arguments doesn't include either __idx or
143020d003cSKyle Evans--     or __len so that the test generator doesn't try to vary the size of the
144020d003cSKyle Evans--     buffer instead of just manipulating __idx/__len to try and induce an
145020d003cSKyle Evans--     overflow.
146020d003cSKyle Evans--
147020d003cSKyle Evans-- Most tests will just use the default bufsize/buftype, but under some
148020d003cSKyle Evans-- circumstances it's useful to use a different type (e.g., for alignment
149020d003cSKyle Evans-- requirements).
150020d003cSKyle Evanslocal all_tests = {
151*062d9380SKyle Evans	random = {
152*062d9380SKyle Evans		-- <sys/random.h>
153*062d9380SKyle Evans		{
154*062d9380SKyle Evans			func = "getrandom",
155*062d9380SKyle Evans			arguments = {
156*062d9380SKyle Evans				"__buf",
157*062d9380SKyle Evans				"__len",
158*062d9380SKyle Evans				"0",
159*062d9380SKyle Evans			},
160*062d9380SKyle Evans			exclude = excludes_stack_overflow,
161*062d9380SKyle Evans		},
162*062d9380SKyle Evans	},
16388276dfbSKyle Evans	poll = {
16488276dfbSKyle Evans		-- <poll.h>
16588276dfbSKyle Evans		{
16688276dfbSKyle Evans			func = "poll",
16788276dfbSKyle Evans			bufsize = "4",
16888276dfbSKyle Evans			buftype = "struct pollfd[]",
16988276dfbSKyle Evans			arguments = {
17088276dfbSKyle Evans				"__buf",
17188276dfbSKyle Evans				"__len",
17288276dfbSKyle Evans				"0",
17388276dfbSKyle Evans			},
17488276dfbSKyle Evans			init = poll_init,
17588276dfbSKyle Evans		},
17688276dfbSKyle Evans		{
17788276dfbSKyle Evans			func = "ppoll",
17888276dfbSKyle Evans			bufsize = "4",
17988276dfbSKyle Evans			buftype = "struct pollfd[]",
18088276dfbSKyle Evans			arguments = {
18188276dfbSKyle Evans				"__buf",
18288276dfbSKyle Evans				"__len",
18388276dfbSKyle Evans				"&tv",
18488276dfbSKyle Evans				"NULL",
18588276dfbSKyle Evans			},
18688276dfbSKyle Evans			stackvars = "\tstruct timespec tv = { 0 };\n",
18788276dfbSKyle Evans			init = poll_init,
18888276dfbSKyle Evans		},
18988276dfbSKyle Evans	},
190020d003cSKyle Evans	stdio = {
191020d003cSKyle Evans		-- <stdio.h>
192020d003cSKyle Evans		{
193cf8e5289SKyle Evans			func = "ctermid",
194cf8e5289SKyle Evans			bufsize = "L_ctermid",
195cf8e5289SKyle Evans			arguments = {
196cf8e5289SKyle Evans				"__buf",
197cf8e5289SKyle Evans			},
198cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
199cf8e5289SKyle Evans		},
200cf8e5289SKyle Evans		{
201cf8e5289SKyle Evans			func = "ctermid_r",
202cf8e5289SKyle Evans			bufsize = "L_ctermid",
203cf8e5289SKyle Evans			arguments = {
204cf8e5289SKyle Evans				"__buf",
205cf8e5289SKyle Evans			},
206cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
207cf8e5289SKyle Evans		},
208cf8e5289SKyle Evans		{
209cf8e5289SKyle Evans			func = "fread",
210cf8e5289SKyle Evans			arguments = {
211cf8e5289SKyle Evans				"__buf",
212cf8e5289SKyle Evans				"__len",
213cf8e5289SKyle Evans				"1",
214cf8e5289SKyle Evans				"stdin",
215cf8e5289SKyle Evans			},
216cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
217cf8e5289SKyle Evans			init = stdio_init,
218cf8e5289SKyle Evans		},
219cf8e5289SKyle Evans		{
220cf8e5289SKyle Evans			func = "fread_unlocked",
221cf8e5289SKyle Evans			arguments = {
222cf8e5289SKyle Evans				"__buf",
223cf8e5289SKyle Evans				"__len",
224cf8e5289SKyle Evans				"1",
225cf8e5289SKyle Evans				"stdin",
226cf8e5289SKyle Evans			},
227cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
228cf8e5289SKyle Evans			init = stdio_init,
229cf8e5289SKyle Evans		},
230cf8e5289SKyle Evans		{
231cf8e5289SKyle Evans			func = "gets_s",
232cf8e5289SKyle Evans			arguments = {
233cf8e5289SKyle Evans				"__buf",
234cf8e5289SKyle Evans				"__len",
235cf8e5289SKyle Evans			},
236cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
237cf8e5289SKyle Evans			init = stdio_init,
238cf8e5289SKyle Evans		},
239cf8e5289SKyle Evans		{
240020d003cSKyle Evans			func = "sprintf",
241020d003cSKyle Evans			arguments = {
242020d003cSKyle Evans				"__buf",
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		},
251020d003cSKyle Evans		{
252020d003cSKyle Evans			func = "snprintf",
253020d003cSKyle Evans			arguments = {
254020d003cSKyle Evans				"__buf",
255020d003cSKyle Evans				"__len",
256020d003cSKyle Evans				"\"%.*s\"",
257020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
258020d003cSKyle Evans				"srcvar",
259020d003cSKyle Evans			},
260020d003cSKyle Evans			exclude = excludes_stack_overflow,
261020d003cSKyle Evans			stackvars = printf_stackvars,
262020d003cSKyle Evans			init = printf_init,
263020d003cSKyle Evans		},
264cf8e5289SKyle Evans		{
265cf8e5289SKyle Evans			func = "tmpnam",
266cf8e5289SKyle Evans			bufsize = "L_tmpnam",
267cf8e5289SKyle Evans			arguments = {
268cf8e5289SKyle Evans				"__buf",
269cf8e5289SKyle Evans			},
270cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
271cf8e5289SKyle Evans		},
272cf8e5289SKyle Evans		{
273cf8e5289SKyle Evans			func = "fgets",
274cf8e5289SKyle Evans			arguments = {
275cf8e5289SKyle Evans				"__buf",
276cf8e5289SKyle Evans				"__len",
277cf8e5289SKyle Evans				"fp",
278cf8e5289SKyle Evans			},
279cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
280cf8e5289SKyle Evans			stackvars = "\tFILE *fp;\n",
281cf8e5289SKyle Evans			init = [[
282cf8e5289SKyle Evans	fp = new_fp(__len);
283cf8e5289SKyle Evans]],
284cf8e5289SKyle Evans		},
285020d003cSKyle Evans	},
286d0b74459SKyle Evans	stdlib = {
287d0b74459SKyle Evans		-- <stdlib.h>
288d0b74459SKyle Evans		{
289d0b74459SKyle Evans			func = "arc4random_buf",
290d0b74459SKyle Evans			arguments = {
291d0b74459SKyle Evans				"__buf",
292d0b74459SKyle Evans				"__len",
293d0b74459SKyle Evans			},
294d0b74459SKyle Evans			exclude = excludes_stack_overflow,
295d0b74459SKyle Evans		},
296d0b74459SKyle Evans		{
297d0b74459SKyle Evans			func = "realpath",
298d0b74459SKyle Evans			bufsize = "PATH_MAX",
299d0b74459SKyle Evans			arguments = {
300d0b74459SKyle Evans				"\".\"",
301d0b74459SKyle Evans				"__buf",
302d0b74459SKyle Evans			},
303d0b74459SKyle Evans			exclude = excludes_stack_overflow,
304d0b74459SKyle Evans		},
305d0b74459SKyle Evans	},
306020d003cSKyle Evans	string = {
307020d003cSKyle Evans		-- <string.h>
308020d003cSKyle Evans		{
309020d003cSKyle Evans			func = "memcpy",
310020d003cSKyle Evans			arguments = {
311020d003cSKyle Evans				"__buf",
312020d003cSKyle Evans				"src",
313020d003cSKyle Evans				"__len",
314020d003cSKyle Evans			},
315020d003cSKyle Evans			exclude = excludes_stack_overflow,
316020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
317020d003cSKyle Evans		},
318020d003cSKyle Evans		{
319cf8e5289SKyle Evans			func = "mempcpy",
320cf8e5289SKyle Evans			arguments = {
321cf8e5289SKyle Evans				"__buf",
322cf8e5289SKyle Evans				"src",
323cf8e5289SKyle Evans				"__len",
324cf8e5289SKyle Evans			},
325cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
326cf8e5289SKyle Evans			stackvars = "\tchar src[__len + 10];\n",
327cf8e5289SKyle Evans		},
328cf8e5289SKyle Evans		{
329020d003cSKyle Evans			func = "memmove",
330020d003cSKyle Evans			arguments = {
331020d003cSKyle Evans				"__buf",
332020d003cSKyle Evans				"src",
333020d003cSKyle Evans				"__len",
334020d003cSKyle Evans			},
335020d003cSKyle Evans			exclude = excludes_stack_overflow,
336020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
337020d003cSKyle Evans		},
338020d003cSKyle Evans		{
339020d003cSKyle Evans			func = "memset",
340020d003cSKyle Evans			arguments = {
341020d003cSKyle Evans				"__buf",
342020d003cSKyle Evans				"0",
343020d003cSKyle Evans				"__len",
344020d003cSKyle Evans			},
345020d003cSKyle Evans			exclude = excludes_stack_overflow,
346020d003cSKyle Evans		},
347020d003cSKyle Evans		{
348020d003cSKyle Evans			func = "stpcpy",
349020d003cSKyle Evans			arguments = {
350020d003cSKyle Evans				"__buf",
351020d003cSKyle Evans				"src",
352020d003cSKyle Evans			},
353020d003cSKyle Evans			exclude = excludes_stack_overflow,
354020d003cSKyle Evans			stackvars = string_stackvars,
355020d003cSKyle Evans			init = string_init,
356020d003cSKyle Evans			uses_len = true,
357020d003cSKyle Evans		},
358020d003cSKyle Evans		{
359020d003cSKyle Evans			func = "stpncpy",
360020d003cSKyle Evans			arguments = {
361020d003cSKyle Evans				"__buf",
362020d003cSKyle Evans				"src",
363020d003cSKyle Evans				"__len",
364020d003cSKyle Evans			},
365020d003cSKyle Evans			exclude = excludes_stack_overflow,
366020d003cSKyle Evans			stackvars = string_stackvars,
367020d003cSKyle Evans			init = string_init,
368020d003cSKyle Evans		},
369020d003cSKyle Evans		{
370020d003cSKyle Evans			func = "strcat",
371020d003cSKyle Evans			arguments = {
372020d003cSKyle Evans				"__buf",
373020d003cSKyle Evans				"src",
374020d003cSKyle Evans			},
375020d003cSKyle Evans			exclude = excludes_stack_overflow,
376020d003cSKyle Evans			stackvars = string_stackvars,
377020d003cSKyle Evans			init = string_init,
378020d003cSKyle Evans			uses_len = true,
379020d003cSKyle Evans		},
380020d003cSKyle Evans		{
381cf8e5289SKyle Evans			func = "strlcat",
382cf8e5289SKyle Evans			arguments = {
383cf8e5289SKyle Evans				"__buf",
384cf8e5289SKyle Evans				"src",
385cf8e5289SKyle Evans				"__len",
386cf8e5289SKyle Evans			},
387cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
388cf8e5289SKyle Evans			stackvars = string_stackvars,
389cf8e5289SKyle Evans			init = string_init,
390cf8e5289SKyle Evans		},
391cf8e5289SKyle Evans		{
392020d003cSKyle Evans			func = "strncat",
393020d003cSKyle Evans			arguments = {
394020d003cSKyle Evans				"__buf",
395020d003cSKyle Evans				"src",
396020d003cSKyle Evans				"__len",
397020d003cSKyle Evans			},
398020d003cSKyle Evans			exclude = excludes_stack_overflow,
399020d003cSKyle Evans			stackvars = string_stackvars,
400020d003cSKyle Evans			init = string_init,
401020d003cSKyle Evans		},
402020d003cSKyle Evans		{
403020d003cSKyle Evans			func = "strcpy",
404020d003cSKyle Evans			arguments = {
405020d003cSKyle Evans				"__buf",
406020d003cSKyle Evans				"src",
407020d003cSKyle Evans			},
408020d003cSKyle Evans			exclude = excludes_stack_overflow,
409020d003cSKyle Evans			stackvars = string_stackvars,
410020d003cSKyle Evans			init = string_init,
411020d003cSKyle Evans			uses_len = true,
412020d003cSKyle Evans		},
413020d003cSKyle Evans		{
414cf8e5289SKyle Evans			func = "strlcpy",
415cf8e5289SKyle Evans			arguments = {
416cf8e5289SKyle Evans				"__buf",
417cf8e5289SKyle Evans				"src",
418cf8e5289SKyle Evans				"__len",
419cf8e5289SKyle Evans			},
420cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
421cf8e5289SKyle Evans			stackvars = string_stackvars,
422cf8e5289SKyle Evans			init = string_init,
423cf8e5289SKyle Evans		},
424cf8e5289SKyle Evans		{
425020d003cSKyle Evans			func = "strncpy",
426020d003cSKyle Evans			arguments = {
427020d003cSKyle Evans				"__buf",
428020d003cSKyle Evans				"src",
429020d003cSKyle Evans				"__len",
430020d003cSKyle Evans			},
431020d003cSKyle Evans			exclude = excludes_stack_overflow,
432020d003cSKyle Evans			stackvars = string_stackvars,
433020d003cSKyle Evans			init = string_init,
434020d003cSKyle Evans		},
435020d003cSKyle Evans	},
436020d003cSKyle Evans	strings = {
437020d003cSKyle Evans		-- <strings.h>
438020d003cSKyle Evans		{
439020d003cSKyle Evans			func = "bcopy",
440020d003cSKyle Evans			arguments = {
441020d003cSKyle Evans				"src",
442020d003cSKyle Evans				"__buf",
443020d003cSKyle Evans				"__len",
444020d003cSKyle Evans			},
445020d003cSKyle Evans			exclude = excludes_stack_overflow,
446020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
447020d003cSKyle Evans		},
448020d003cSKyle Evans		{
449020d003cSKyle Evans			func = "bzero",
450020d003cSKyle Evans			arguments = {
451020d003cSKyle Evans				"__buf",
452020d003cSKyle Evans				"__len",
453020d003cSKyle Evans			},
454020d003cSKyle Evans			exclude = excludes_stack_overflow,
455020d003cSKyle Evans		},
456cf8e5289SKyle Evans		{
457cf8e5289SKyle Evans			func = "explicit_bzero",
458cf8e5289SKyle Evans			arguments = {
459cf8e5289SKyle Evans				"__buf",
460cf8e5289SKyle Evans				"__len",
461cf8e5289SKyle Evans			},
462cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
463cf8e5289SKyle Evans		},
464020d003cSKyle Evans	},
465020d003cSKyle Evans	unistd = {
466020d003cSKyle Evans		-- <unistd.h>
467020d003cSKyle Evans		{
468020d003cSKyle Evans			func = "getcwd",
469020d003cSKyle Evans			bufsize = "8",
470020d003cSKyle Evans			arguments = {
471020d003cSKyle Evans				"__buf",
472020d003cSKyle Evans				"__len",
473020d003cSKyle Evans			},
474020d003cSKyle Evans			exclude = excludes_stack_overflow,
475020d003cSKyle Evans		},
476020d003cSKyle Evans		{
477cf8e5289SKyle Evans			func = "getgrouplist",
478cf8e5289SKyle Evans			bufsize = "4",
479cf8e5289SKyle Evans			buftype = "gid_t[]",
480cf8e5289SKyle Evans			arguments = {
481cf8e5289SKyle Evans				"\"root\"",
482cf8e5289SKyle Evans				"0",
483cf8e5289SKyle Evans				"__buf",
484cf8e5289SKyle Evans				"&intlen",
485cf8e5289SKyle Evans			},
486cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
487cf8e5289SKyle Evans			stackvars = "\tint intlen = (int)__len;\n",
488cf8e5289SKyle Evans			uses_len = true,
489cf8e5289SKyle Evans		},
490cf8e5289SKyle Evans		{
491cf8e5289SKyle Evans			func = "getgroups",
492cf8e5289SKyle Evans			bufsize = "4",
493cf8e5289SKyle Evans			buftype = "gid_t[]",
494cf8e5289SKyle Evans			arguments = {
495cf8e5289SKyle Evans				"__len",
496cf8e5289SKyle Evans				"__buf",
497cf8e5289SKyle Evans			},
498cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
499cf8e5289SKyle Evans		},
500cf8e5289SKyle Evans		{
501cf8e5289SKyle Evans			func = "getloginclass",
502cf8e5289SKyle Evans			arguments = {
503cf8e5289SKyle Evans				"__buf",
504cf8e5289SKyle Evans				"__len",
505cf8e5289SKyle Evans			},
506cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
507cf8e5289SKyle Evans		},
508cf8e5289SKyle Evans		{
509cf8e5289SKyle Evans			func = "pread",
510cf8e5289SKyle Evans			bufsize = "41",
511cf8e5289SKyle Evans			arguments = {
512cf8e5289SKyle Evans				"fd",
513cf8e5289SKyle Evans				"__buf",
514cf8e5289SKyle Evans				"__len",
515cf8e5289SKyle Evans				"0",
516cf8e5289SKyle Evans			},
517cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
518cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
519cf8e5289SKyle Evans			init = [[
520cf8e5289SKyle Evans	fd = new_tmpfile();	/* Cannot fail */
521cf8e5289SKyle Evans]],
522cf8e5289SKyle Evans		},
523cf8e5289SKyle Evans		{
524020d003cSKyle Evans			func = "read",
525020d003cSKyle Evans			bufsize = "41",
526020d003cSKyle Evans			arguments = {
527020d003cSKyle Evans				"fd",
528020d003cSKyle Evans				"__buf",
529020d003cSKyle Evans				"__len",
530020d003cSKyle Evans			},
531020d003cSKyle Evans			exclude = excludes_stack_overflow,
532020d003cSKyle Evans			stackvars = "\tint fd;\n",
533020d003cSKyle Evans			init = [[
534020d003cSKyle Evans	fd = new_tmpfile();	/* Cannot fail */
535020d003cSKyle Evans]],
536020d003cSKyle Evans		},
537020d003cSKyle Evans		{
538020d003cSKyle Evans			func = "readlink",
539020d003cSKyle Evans			arguments = {
540020d003cSKyle Evans				"path",
541020d003cSKyle Evans				"__buf",
542020d003cSKyle Evans				"__len",
543020d003cSKyle Evans			},
544020d003cSKyle Evans			exclude = excludes_stack_overflow,
545020d003cSKyle Evans			stackvars = "\tconst char *path;\n",
546020d003cSKyle Evans			init = [[
547020d003cSKyle Evans	path = new_symlink(__len);		/* Cannot fail */
548020d003cSKyle Evans]],
549020d003cSKyle Evans		},
550cf8e5289SKyle Evans		{
551cf8e5289SKyle Evans			func = "readlinkat",
552cf8e5289SKyle Evans			arguments = {
553cf8e5289SKyle Evans				"AT_FDCWD",
554cf8e5289SKyle Evans				"path",
555cf8e5289SKyle Evans				"__buf",
556cf8e5289SKyle Evans				"__len",
557cf8e5289SKyle Evans			},
558cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
559cf8e5289SKyle Evans			stackvars = "\tconst char *path;\n",
560cf8e5289SKyle Evans			init = [[
561cf8e5289SKyle Evans	path = new_symlink(__len);		/* Cannot fail */
562cf8e5289SKyle Evans]],
563cf8e5289SKyle Evans		},
564cf8e5289SKyle Evans		{
565cf8e5289SKyle Evans			func = "getdomainname",
566cf8e5289SKyle Evans			bufsize = "4",
567cf8e5289SKyle Evans			arguments = {
568cf8e5289SKyle Evans				"__buf",
569cf8e5289SKyle Evans				"__len",
570cf8e5289SKyle Evans			},
571cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
572cf8e5289SKyle Evans			stackvars = "\tchar sysdomain[256];\n",
573cf8e5289SKyle Evans			early_init = [[
574cf8e5289SKyle Evans	(void)getdomainname(sysdomain, __len);
575cf8e5289SKyle Evans	if (strlen(sysdomain) <= __len)
576cf8e5289SKyle Evans		atf_tc_skip("domain name too short for testing");
577cf8e5289SKyle Evans]]
578cf8e5289SKyle Evans		},
579cf8e5289SKyle Evans		{
580cf8e5289SKyle Evans			func = "getentropy",
581cf8e5289SKyle Evans			arguments = {
582cf8e5289SKyle Evans				"__buf",
583cf8e5289SKyle Evans				"__len",
584cf8e5289SKyle Evans			},
585cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
586cf8e5289SKyle Evans		},
587cf8e5289SKyle Evans		{
588cf8e5289SKyle Evans			func = "gethostname",
589cf8e5289SKyle Evans			bufsize = "4",
590cf8e5289SKyle Evans			arguments = {
591cf8e5289SKyle Evans				"__buf",
592cf8e5289SKyle Evans				"__len",
593cf8e5289SKyle Evans			},
594cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
595cf8e5289SKyle Evans			stackvars = [[
596cf8e5289SKyle Evans	char syshost[256];
597cf8e5289SKyle Evans	int error;
598cf8e5289SKyle Evans]],
599cf8e5289SKyle Evans			early_init = [[
600cf8e5289SKyle Evans	error = gethostname(syshost, __len);
601cf8e5289SKyle Evans	if (error != 0 || strlen(syshost) <= __len)
602cf8e5289SKyle Evans		atf_tc_skip("hostname too short for testing");
603cf8e5289SKyle Evans]]
604cf8e5289SKyle Evans		},
605cf8e5289SKyle Evans		{
606cf8e5289SKyle Evans			func = "getlogin_r",
607cf8e5289SKyle Evans			bufsize = "MAXLOGNAME + 1",
608cf8e5289SKyle Evans			arguments = {
609cf8e5289SKyle Evans				"__buf",
610cf8e5289SKyle Evans				"__len",
611cf8e5289SKyle Evans			},
612cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
613cf8e5289SKyle Evans		},
614cf8e5289SKyle Evans		{
615cf8e5289SKyle Evans			func = "ttyname_r",
616cf8e5289SKyle Evans			arguments = {
617cf8e5289SKyle Evans				"fd",
618cf8e5289SKyle Evans				"__buf",
619cf8e5289SKyle Evans				"__len",
620cf8e5289SKyle Evans			},
621cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
622cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
623cf8e5289SKyle Evans			early_init = [[
624cf8e5289SKyle Evans	fd = STDIN_FILENO;
625cf8e5289SKyle Evans	if (!isatty(fd))
626cf8e5289SKyle Evans		atf_tc_skip("stdin is not an fd");
627cf8e5289SKyle Evans]]
628cf8e5289SKyle Evans		},
629020d003cSKyle Evans	},
630b53d7aa8SKyle Evans	wchar = {
631b53d7aa8SKyle Evans		-- <wchar.h>
632b53d7aa8SKyle Evans		{
633b53d7aa8SKyle Evans			func = "wmemcpy",
634b53d7aa8SKyle Evans			buftype = "wchar_t[]",
635b53d7aa8SKyle Evans			arguments = {
636b53d7aa8SKyle Evans				"__buf",
637b53d7aa8SKyle Evans				"src",
638b53d7aa8SKyle Evans				"__len",
639b53d7aa8SKyle Evans			},
640b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
641b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
642b53d7aa8SKyle Evans		},
643b53d7aa8SKyle Evans		{
644b53d7aa8SKyle Evans			func = "wmempcpy",
645b53d7aa8SKyle Evans			buftype = "wchar_t[]",
646b53d7aa8SKyle Evans			arguments = {
647b53d7aa8SKyle Evans				"__buf",
648b53d7aa8SKyle Evans				"src",
649b53d7aa8SKyle Evans				"__len",
650b53d7aa8SKyle Evans			},
651b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
652b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
653b53d7aa8SKyle Evans		},
654b53d7aa8SKyle Evans		{
655b53d7aa8SKyle Evans			func = "wmemmove",
656b53d7aa8SKyle Evans			buftype = "wchar_t[]",
657b53d7aa8SKyle Evans			arguments = {
658b53d7aa8SKyle Evans				"__buf",
659b53d7aa8SKyle Evans				"src",
660b53d7aa8SKyle Evans				"__len",
661b53d7aa8SKyle Evans			},
662b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
663b53d7aa8SKyle Evans			stackvars = "\twchar_t src[__len + 10];\n",
664b53d7aa8SKyle Evans		},
665b53d7aa8SKyle Evans		{
666b53d7aa8SKyle Evans			func = "wmemset",
667b53d7aa8SKyle Evans			buftype = "wchar_t[]",
668b53d7aa8SKyle Evans			arguments = {
669b53d7aa8SKyle Evans				"__buf",
670b53d7aa8SKyle Evans				"L'0'",
671b53d7aa8SKyle Evans				"__len",
672b53d7aa8SKyle Evans			},
673b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
674b53d7aa8SKyle Evans		},
675b53d7aa8SKyle Evans		{
676b53d7aa8SKyle Evans			func = "wcpcpy",
677b53d7aa8SKyle Evans			buftype = "wchar_t[]",
678b53d7aa8SKyle Evans			arguments = {
679b53d7aa8SKyle Evans				"__buf",
680b53d7aa8SKyle Evans				"src",
681b53d7aa8SKyle Evans			},
682b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
683b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
684b53d7aa8SKyle Evans			init = wstring_init,
685b53d7aa8SKyle Evans			uses_len = true,
686b53d7aa8SKyle Evans		},
687b53d7aa8SKyle Evans		{
688b53d7aa8SKyle Evans			func = "wcpncpy",
689b53d7aa8SKyle Evans			buftype = "wchar_t[]",
690b53d7aa8SKyle Evans			arguments = {
691b53d7aa8SKyle Evans				"__buf",
692b53d7aa8SKyle Evans				"src",
693b53d7aa8SKyle Evans				"__len",
694b53d7aa8SKyle Evans			},
695b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
696b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
697b53d7aa8SKyle Evans			init = wstring_init,
698b53d7aa8SKyle Evans		},
699b53d7aa8SKyle Evans		{
700b53d7aa8SKyle Evans			func = "wcscat",
701b53d7aa8SKyle Evans			buftype = "wchar_t[]",
702b53d7aa8SKyle Evans			arguments = {
703b53d7aa8SKyle Evans				"__buf",
704b53d7aa8SKyle Evans				"src",
705b53d7aa8SKyle Evans			},
706b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
707b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
708b53d7aa8SKyle Evans			init = wstring_init,
709b53d7aa8SKyle Evans			uses_len = true,
710b53d7aa8SKyle Evans		},
711b53d7aa8SKyle Evans		{
712b53d7aa8SKyle Evans			func = "wcslcat",
713b53d7aa8SKyle Evans			buftype = "wchar_t[]",
714b53d7aa8SKyle Evans			arguments = {
715b53d7aa8SKyle Evans				"__buf",
716b53d7aa8SKyle Evans				"src",
717b53d7aa8SKyle Evans				"__len",
718b53d7aa8SKyle Evans			},
719b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
720b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
721b53d7aa8SKyle Evans			init = wstring_init,
722b53d7aa8SKyle Evans		},
723b53d7aa8SKyle Evans		{
724b53d7aa8SKyle Evans			func = "wcsncat",
725b53d7aa8SKyle Evans			buftype = "wchar_t[]",
726b53d7aa8SKyle Evans			arguments = {
727b53d7aa8SKyle Evans				"__buf",
728b53d7aa8SKyle Evans				"src",
729b53d7aa8SKyle Evans				"__len",
730b53d7aa8SKyle Evans			},
731b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
732b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
733b53d7aa8SKyle Evans			init = wstring_init,
734b53d7aa8SKyle Evans		},
735b53d7aa8SKyle Evans		{
736b53d7aa8SKyle Evans			func = "wcscpy",
737b53d7aa8SKyle Evans			buftype = "wchar_t[]",
738b53d7aa8SKyle Evans			arguments = {
739b53d7aa8SKyle Evans				"__buf",
740b53d7aa8SKyle Evans				"src",
741b53d7aa8SKyle Evans			},
742b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
743b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
744b53d7aa8SKyle Evans			init = wstring_init,
745b53d7aa8SKyle Evans			uses_len = true,
746b53d7aa8SKyle Evans		},
747b53d7aa8SKyle Evans		{
748b53d7aa8SKyle Evans			func = "wcslcpy",
749b53d7aa8SKyle Evans			buftype = "wchar_t[]",
750b53d7aa8SKyle Evans			arguments = {
751b53d7aa8SKyle Evans				"__buf",
752b53d7aa8SKyle Evans				"src",
753b53d7aa8SKyle Evans				"__len",
754b53d7aa8SKyle Evans			},
755b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
756b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
757b53d7aa8SKyle Evans			init = wstring_init,
758b53d7aa8SKyle Evans		},
759b53d7aa8SKyle Evans		{
760b53d7aa8SKyle Evans			func = "wcsncpy",
761b53d7aa8SKyle Evans			buftype = "wchar_t[]",
762b53d7aa8SKyle Evans			arguments = {
763b53d7aa8SKyle Evans				"__buf",
764b53d7aa8SKyle Evans				"src",
765b53d7aa8SKyle Evans				"__len",
766b53d7aa8SKyle Evans			},
767b53d7aa8SKyle Evans			exclude = excludes_stack_overflow,
768b53d7aa8SKyle Evans			stackvars = wstring_stackvars,
769b53d7aa8SKyle Evans			init = wstring_init,
770b53d7aa8SKyle Evans		},
771b53d7aa8SKyle Evans	},
772020d003cSKyle Evans}
773020d003cSKyle Evans
774020d003cSKyle Evanslocal function write_test_boilerplate(fh, name, body)
775020d003cSKyle Evans	fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
776020d003cSKyle Evans	fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
777020d003cSKyle Evans	fh:write("{\n" .. body .. "\n}\n\n")
778020d003cSKyle Evans	return name
779020d003cSKyle Evansend
780020d003cSKyle Evans
781020d003cSKyle Evanslocal function generate_test_name(func, variant, disposition, heap)
782020d003cSKyle Evans	local basename = func
783020d003cSKyle Evans	if variant then
784020d003cSKyle Evans		basename = basename .. "_" .. variant
785020d003cSKyle Evans	end
786020d003cSKyle Evans	if heap then
787020d003cSKyle Evans		basename = basename .. "_heap"
788020d003cSKyle Evans	end
789020d003cSKyle Evans	if disposition < 0 then
790020d003cSKyle Evans		return basename .. "_before_end"
791020d003cSKyle Evans	elseif disposition == 0 then
792020d003cSKyle Evans		return basename .. "_end"
793020d003cSKyle Evans	else
794020d003cSKyle Evans		return basename .. "_after_end"
795020d003cSKyle Evans	end
796020d003cSKyle Evansend
797020d003cSKyle Evans
798020d003cSKyle Evanslocal function array_type(buftype)
799020d003cSKyle Evans	if not buftype:match("%[%]") then
800020d003cSKyle Evans		return nil
801020d003cSKyle Evans	end
802020d003cSKyle Evans
803020d003cSKyle Evans	return buftype:gsub("%[%]", "")
804020d003cSKyle Evansend
805020d003cSKyle Evans
806020d003cSKyle Evanslocal function configurable(def, idx)
807020d003cSKyle Evans	local cfgitem = def[idx]
808020d003cSKyle Evans
809020d003cSKyle Evans	if not cfgitem then
810020d003cSKyle Evans		return nil
811020d003cSKyle Evans	end
812020d003cSKyle Evans
813020d003cSKyle Evans	if type(cfgitem) == "function" then
814020d003cSKyle Evans		return cfgitem()
815020d003cSKyle Evans	end
816020d003cSKyle Evans
817020d003cSKyle Evans	return cfgitem
818020d003cSKyle Evansend
819020d003cSKyle Evans
820020d003cSKyle Evanslocal function generate_stackframe(buftype, bufsize, disposition, heap, def)
821020d003cSKyle Evans	local function len_offset(inverted, disposition)
822020d003cSKyle Evans		-- Tests that don't use __len in their arguments may use an
823020d003cSKyle Evans		-- inverted sense because we can't just specify a length that
824020d003cSKyle Evans		-- would induce an access just after the end.  Instead, we have
825020d003cSKyle Evans		-- to manipulate the buffer size to be too short so that the
826020d003cSKyle Evans		-- function under test would write one too many.
827020d003cSKyle Evans		if disposition < 0 then
828020d003cSKyle Evans			return ((inverted and " + ") or " - ") .. "1"
829020d003cSKyle Evans		elseif disposition == 0 then
830020d003cSKyle Evans			return ""
831020d003cSKyle Evans		else
832020d003cSKyle Evans			return ((inverted and " - ") or " + ") .. "1"
833020d003cSKyle Evans		end
834020d003cSKyle Evans	end
835020d003cSKyle Evans
836020d003cSKyle Evans	local function test_uses_len(def)
837020d003cSKyle Evans		if def.uses_len then
838020d003cSKyle Evans			return true
839020d003cSKyle Evans		end
840020d003cSKyle Evans
841020d003cSKyle Evans		for _, arg in ipairs(def.arguments) do
842020d003cSKyle Evans			if arg:match("__len") or arg:match("__idx") then
843020d003cSKyle Evans				return true
844020d003cSKyle Evans			end
845020d003cSKyle Evans		end
846020d003cSKyle Evans
847020d003cSKyle Evans		return false
848020d003cSKyle Evans	end
849020d003cSKyle Evans
850020d003cSKyle Evans
851020d003cSKyle Evans	-- This is perhaps a little convoluted, but we toss the buffer into a
852020d003cSKyle Evans	-- struct on the stack to guarantee that we have at least one valid
853020d003cSKyle Evans	-- byte on either side of the buffer -- a measure to make sure that
854020d003cSKyle Evans	-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
855020d003cSKyle Evans	-- rather than some other stack or memory protection.
856020d003cSKyle Evans	local vars = "\tstruct {\n"
857020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_l;\n"
858020d003cSKyle Evans
859020d003cSKyle Evans	local uses_len = test_uses_len(def)
860020d003cSKyle Evans	local bufsize_offset = len_offset(not uses_len, disposition)
861020d003cSKyle Evans	local buftype_elem = array_type(buftype)
862020d003cSKyle Evans	local size_expr = bufsize
863020d003cSKyle Evans
864020d003cSKyle Evans	if not uses_len then
865020d003cSKyle Evans		-- If the length isn't in use, we have to vary the buffer size
866020d003cSKyle Evans		-- since the fortified function likely has some internal size
867020d003cSKyle Evans		-- constraint that it's supposed to be checking.
868020d003cSKyle Evans		size_expr = size_expr .. bufsize_offset
869020d003cSKyle Evans	end
870020d003cSKyle Evans
871020d003cSKyle Evans	if not heap and buftype_elem then
872020d003cSKyle Evans		-- Array type: size goes after identifier
873020d003cSKyle Evans		vars = vars .. "\t\t" .. buftype_elem ..
874020d003cSKyle Evans		    " __buf[" .. size_expr .. "];\n"
875020d003cSKyle Evans	else
876020d003cSKyle Evans		local basic_type = buftype_elem or buftype
877020d003cSKyle Evans
878020d003cSKyle Evans		-- Heap tests obviously just put a pointer on the stack that
879020d003cSKyle Evans		-- points to our new allocation, but we leave it in the padded
880020d003cSKyle Evans		-- struct just to simplify our generator.
881020d003cSKyle Evans		if heap then
882020d003cSKyle Evans			basic_type = basic_type .. " *"
883020d003cSKyle Evans		end
884020d003cSKyle Evans		vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
885020d003cSKyle Evans	end
886020d003cSKyle Evans
887020d003cSKyle Evans	-- padding_r is our just-past-the-end padding that we use to make sure
888020d003cSKyle Evans	-- that there's a valid portion after the buffer that isn't being
889020d003cSKyle Evans	-- included in our function calls.  If we didn't have it, then we'd have
890020d003cSKyle Evans	-- a hard time feeling confident that an abort on the just-after tests
891020d003cSKyle Evans	-- isn't maybe from some other memory or stack protection.
892020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_r;\n"
893020d003cSKyle Evans	vars = vars .. "\t} __stack;\n"
894020d003cSKyle Evans
895020d003cSKyle Evans	-- Not all tests will use __bufsz, but some do for, e.g., clearing
896020d003cSKyle Evans	-- memory..
897020d003cSKyle Evans	vars = vars .. "\tconst size_t __bufsz __unused = "
898020d003cSKyle Evans	if heap then
899020d003cSKyle Evans		local scalar = 1
900020d003cSKyle Evans		if buftype_elem then
901020d003cSKyle Evans			scalar = size_expr
902020d003cSKyle Evans		end
903020d003cSKyle Evans
904020d003cSKyle Evans		vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
905020d003cSKyle Evans	else
906020d003cSKyle Evans		vars = vars .. "sizeof(__stack.__buf);\n"
907020d003cSKyle Evans	end
908020d003cSKyle Evans
909020d003cSKyle Evans	vars = vars .. "\tconst size_t __len = " .. bufsize ..
910020d003cSKyle Evans	    bufsize_offset .. ";\n"
911020d003cSKyle Evans	vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
912020d003cSKyle Evans
913020d003cSKyle Evans	-- For overflow testing, we need to fork() because we're expecting the
914020d003cSKyle Evans	-- test to ultimately abort()/_exit().  Then we can collect the exit
915020d003cSKyle Evans	-- status and report appropriately.
916020d003cSKyle Evans	if disposition > 0 then
917020d003cSKyle Evans		vars = vars .. "\tpid_t __child;\n"
918020d003cSKyle Evans		vars = vars .. "\tint __status;\n"
919020d003cSKyle Evans	end
920020d003cSKyle Evans
921020d003cSKyle Evans	-- Any other stackvars defined by the test get placed after everything
922020d003cSKyle Evans	-- else.
923020d003cSKyle Evans	vars = vars .. (configurable(def, "stackvars") or "")
924020d003cSKyle Evans
925020d003cSKyle Evans	return vars
926020d003cSKyle Evansend
927020d003cSKyle Evans
928020d003cSKyle Evanslocal function write_test(fh, func, disposition, heap, def)
929020d003cSKyle Evans	local testname = generate_test_name(func, def.variant, disposition, heap)
930020d003cSKyle Evans	local buftype = def.buftype or "unsigned char[]"
931020d003cSKyle Evans	local bufsize = def.bufsize or 42
932020d003cSKyle Evans	local body = ""
933020d003cSKyle Evans
934020d003cSKyle Evans	if def.exclude and def.exclude(disposition, heap) then
935020d003cSKyle Evans		return
936020d003cSKyle Evans	end
937020d003cSKyle Evans
938020d003cSKyle Evans	local function need_addr(buftype)
939020d003cSKyle Evans		return not (buftype:match("%[%]") or buftype:match("%*"))
940020d003cSKyle Evans	end
941020d003cSKyle Evans
942020d003cSKyle Evans	if heap then
943020d003cSKyle Evans		body = body .. "#define BUF __stack.__buf\n"
944020d003cSKyle Evans	else
945020d003cSKyle Evans		body = body .. "#define BUF &__stack.__buf\n"
946020d003cSKyle Evans	end
947020d003cSKyle Evans
948020d003cSKyle Evans	-- Setup the buffer
949020d003cSKyle Evans	body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
950020d003cSKyle Evans	    "\n"
951020d003cSKyle Evans
952020d003cSKyle Evans	-- Any early initialization goes before we would fork for the just-after
953020d003cSKyle Evans	-- tests, because they may want to skip the test based on some criteria
954020d003cSKyle Evans	-- and we can't propagate that up very easily once we're forked.
955020d003cSKyle Evans	local early_init = configurable(def, "early_init")
956020d003cSKyle Evans	body = body .. (early_init or "")
957020d003cSKyle Evans	if early_init then
958020d003cSKyle Evans		body = body .. "\n"
959020d003cSKyle Evans	end
960020d003cSKyle Evans
961020d003cSKyle Evans	-- Fork off, iff we're testing some access past the end of the buffer.
962020d003cSKyle Evans	if disposition > 0 then
963020d003cSKyle Evans		body = body .. [[
964020d003cSKyle Evans	__child = fork();
965020d003cSKyle Evans	ATF_REQUIRE(__child >= 0);
966020d003cSKyle Evans	if (__child > 0)
967020d003cSKyle Evans		goto monitor;
968020d003cSKyle Evans
969020d003cSKyle Evans	/* Child */
970020d003cSKyle Evans	disable_coredumps();
971020d003cSKyle Evans]]
972020d003cSKyle Evans	end
973020d003cSKyle Evans
974020d003cSKyle Evans	local bufvar = "__stack.__buf"
975020d003cSKyle Evans	if heap then
976020d003cSKyle Evans		-- Buffer needs to be initialized because it's a heap allocation.
977020d003cSKyle Evans		body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
978020d003cSKyle Evans	end
979020d003cSKyle Evans
980020d003cSKyle Evans	-- Non-early init happens just after the fork in the child, not the
981020d003cSKyle Evans	-- monitor.  This is used to setup any other buffers we may need, for
982020d003cSKyle Evans	-- instance.
983020d003cSKyle Evans	local extra_init = configurable(def, "init")
984020d003cSKyle Evans	body = body .. (extra_init or "")
985020d003cSKyle Evans
986020d003cSKyle Evans	if heap or extra_init then
987020d003cSKyle Evans		body = body .. "\n"
988020d003cSKyle Evans	end
989020d003cSKyle Evans
990020d003cSKyle Evans	-- Setup the function call with arguments as described in the test
991020d003cSKyle Evans	-- definition.
992020d003cSKyle Evans	body = body .. "\t" .. func .. "("
993020d003cSKyle Evans
994020d003cSKyle Evans	for idx, arg in ipairs(def.arguments) do
995020d003cSKyle Evans		if idx > 1 then
996020d003cSKyle Evans			body = body .. ", "
997020d003cSKyle Evans		end
998020d003cSKyle Evans
999020d003cSKyle Evans		if arg == "__buf" then
1000020d003cSKyle Evans			if not heap and need_addr(buftype) then
1001020d003cSKyle Evans				body = body .. "&"
1002020d003cSKyle Evans			end
1003020d003cSKyle Evans
1004020d003cSKyle Evans			body = body .. bufvar
1005020d003cSKyle Evans		else
1006020d003cSKyle Evans			local argname = arg
1007020d003cSKyle Evans
1008020d003cSKyle Evans			if def.value_of then
1009020d003cSKyle Evans				argname = argname or def.value_of(arg)
1010020d003cSKyle Evans			end
1011020d003cSKyle Evans
1012020d003cSKyle Evans			body = body .. argname
1013020d003cSKyle Evans		end
1014020d003cSKyle Evans	end
1015020d003cSKyle Evans
1016020d003cSKyle Evans	body = body .. ");\n"
1017020d003cSKyle Evans
1018020d003cSKyle Evans	-- Monitor stuff follows, for OOB access.
1019020d003cSKyle Evans	if disposition <= 0 then
1020020d003cSKyle Evans		goto skip
1021020d003cSKyle Evans	end
1022020d003cSKyle Evans
1023020d003cSKyle Evans	body = body .. [[
1024020d003cSKyle Evans	_exit(EX_SOFTWARE);	/* Should have aborted. */
1025020d003cSKyle Evans
1026020d003cSKyle Evansmonitor:
1027020d003cSKyle Evans	while (waitpid(__child, &__status, 0) != __child) {
1028020d003cSKyle Evans		ATF_REQUIRE_EQ(EINTR, errno);
1029020d003cSKyle Evans	}
1030020d003cSKyle Evans
1031020d003cSKyle Evans	if (!WIFSIGNALED(__status)) {
1032020d003cSKyle Evans		switch (WEXITSTATUS(__status)) {
1033020d003cSKyle Evans		case EX_SOFTWARE:
1034020d003cSKyle Evans			atf_tc_fail("FORTIFY_SOURCE failed to abort");
1035020d003cSKyle Evans			break;
1036020d003cSKyle Evans		case EX_OSERR:
1037020d003cSKyle Evans			atf_tc_fail("setrlimit(2) failed");
1038020d003cSKyle Evans			break;
1039020d003cSKyle Evans		default:
1040020d003cSKyle Evans			atf_tc_fail("child exited with status %d",
1041020d003cSKyle Evans			    WEXITSTATUS(__status));
1042020d003cSKyle Evans		}
1043020d003cSKyle Evans	} else {
1044020d003cSKyle Evans		ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
1045020d003cSKyle Evans	}
1046020d003cSKyle Evans]]
1047020d003cSKyle Evans
1048020d003cSKyle Evans::skip::
1049020d003cSKyle Evans	body = body .. "#undef BUF\n"
1050020d003cSKyle Evans	return write_test_boilerplate(fh, testname, body)
1051020d003cSKyle Evansend
1052020d003cSKyle Evans
1053020d003cSKyle Evans-- main()
1054020d003cSKyle Evanslocal tests
1055020d003cSKyle Evanslocal tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
1056020d003cSKyle Evansfor k, defs in pairs(all_tests) do
1057020d003cSKyle Evans	if k == tcat then
1058020d003cSKyle Evans		tests = defs
1059020d003cSKyle Evans		break
1060020d003cSKyle Evans	end
1061020d003cSKyle Evansend
1062020d003cSKyle Evans
1063020d003cSKyle Evansassert(tests, "category " .. tcat .. " not found")
1064020d003cSKyle Evans
1065020d003cSKyle Evanslocal fh = io.stdout
1066020d003cSKyle Evansfh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
1067020d003cSKyle Evans    tcat .. "\"` */\n\n")
1068020d003cSKyle Evansfh:write("#define	_FORTIFY_SOURCE	2\n")
1069020d003cSKyle Evansfh:write("#define	TMPFILE_SIZE	(1024 * 32)\n")
1070020d003cSKyle Evans
1071020d003cSKyle Evansfh:write("\n")
1072020d003cSKyle Evansfor _, inc in ipairs(includes) do
1073020d003cSKyle Evans	fh:write("#include <" .. inc .. ">\n")
1074020d003cSKyle Evansend
1075020d003cSKyle Evans
1076020d003cSKyle Evansfh:write([[
1077020d003cSKyle Evans
1078cf8e5289SKyle Evansstatic FILE * __unused
1079cf8e5289SKyle Evansnew_fp(size_t __len)
1080cf8e5289SKyle Evans{
1081cf8e5289SKyle Evans	static char fpbuf[LINE_MAX];
1082cf8e5289SKyle Evans	FILE *fp;
1083cf8e5289SKyle Evans
1084cf8e5289SKyle Evans	ATF_REQUIRE(__len <= sizeof(fpbuf));
1085cf8e5289SKyle Evans
1086cf8e5289SKyle Evans	memset(fpbuf, 'A', sizeof(fpbuf) - 1);
1087cf8e5289SKyle Evans	fpbuf[sizeof(fpbuf) - 1] = '\0';
1088cf8e5289SKyle Evans
1089cf8e5289SKyle Evans	fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
1090cf8e5289SKyle Evans	ATF_REQUIRE(fp != NULL);
1091cf8e5289SKyle Evans
1092cf8e5289SKyle Evans	return (fp);
1093cf8e5289SKyle Evans}
1094cf8e5289SKyle Evans
1095020d003cSKyle Evans/*
1096020d003cSKyle Evans * Create a new symlink to use for readlink(2) style tests, we'll just use a
1097020d003cSKyle Evans * random target name to have something interesting to look at.
1098020d003cSKyle Evans */
1099020d003cSKyle Evansstatic const char * __unused
1100020d003cSKyle Evansnew_symlink(size_t __len)
1101020d003cSKyle Evans{
1102020d003cSKyle Evans	static const char linkname[] = "link";
1103020d003cSKyle Evans	char target[MAXNAMLEN];
1104020d003cSKyle Evans	int error;
1105020d003cSKyle Evans
1106020d003cSKyle Evans	ATF_REQUIRE(__len <= sizeof(target));
1107020d003cSKyle Evans
1108020d003cSKyle Evans	arc4random_buf(target, sizeof(target));
1109020d003cSKyle Evans
1110020d003cSKyle Evans	error = unlink(linkname);
1111020d003cSKyle Evans	ATF_REQUIRE(error == 0 || errno == ENOENT);
1112020d003cSKyle Evans
1113020d003cSKyle Evans	error = symlink(target, linkname);
1114020d003cSKyle Evans	ATF_REQUIRE(error == 0);
1115020d003cSKyle Evans
1116020d003cSKyle Evans	return (linkname);
1117020d003cSKyle Evans}
1118020d003cSKyle Evans
1119020d003cSKyle Evans/*
1120020d003cSKyle Evans * Constructs a tmpfile that we can use for testing read(2) and friends.
1121020d003cSKyle Evans */
1122020d003cSKyle Evansstatic int __unused
1123020d003cSKyle Evansnew_tmpfile(void)
1124020d003cSKyle Evans{
1125020d003cSKyle Evans	char buf[1024];
1126020d003cSKyle Evans	ssize_t rv;
1127020d003cSKyle Evans	size_t written;
1128020d003cSKyle Evans	int fd;
1129020d003cSKyle Evans
1130020d003cSKyle Evans	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
1131020d003cSKyle Evans	ATF_REQUIRE(fd >= 0);
1132020d003cSKyle Evans
1133020d003cSKyle Evans	written = 0;
1134020d003cSKyle Evans	while (written < TMPFILE_SIZE) {
1135020d003cSKyle Evans		rv = write(fd, buf, sizeof(buf));
1136020d003cSKyle Evans		ATF_REQUIRE(rv > 0);
1137020d003cSKyle Evans
1138020d003cSKyle Evans		written += rv;
1139020d003cSKyle Evans	}
1140020d003cSKyle Evans
1141020d003cSKyle Evans	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
1142020d003cSKyle Evans	return (fd);
1143020d003cSKyle Evans}
1144020d003cSKyle Evans
1145020d003cSKyle Evansstatic void
1146020d003cSKyle Evansdisable_coredumps(void)
1147020d003cSKyle Evans{
1148020d003cSKyle Evans	struct rlimit rl = { 0 };
1149020d003cSKyle Evans
1150020d003cSKyle Evans	if (setrlimit(RLIMIT_CORE, &rl) == -1)
1151020d003cSKyle Evans		_exit(EX_OSERR);
1152020d003cSKyle Evans}
1153020d003cSKyle Evans
1154cf8e5289SKyle Evans/*
1155cf8e5289SKyle Evans * Replaces stdin with a file that we can actually read from, for tests where
1156cf8e5289SKyle Evans * we want a FILE * or fd that we can get data from.
1157cf8e5289SKyle Evans */
1158cf8e5289SKyle Evansstatic void __unused
1159cf8e5289SKyle Evansreplace_stdin(void)
1160cf8e5289SKyle Evans{
1161cf8e5289SKyle Evans	int fd;
1162cf8e5289SKyle Evans
1163cf8e5289SKyle Evans	fd = new_tmpfile();
1164cf8e5289SKyle Evans
1165cf8e5289SKyle Evans	(void)dup2(fd, STDIN_FILENO);
1166cf8e5289SKyle Evans	if (fd != STDIN_FILENO)
1167cf8e5289SKyle Evans		close(fd);
1168cf8e5289SKyle Evans}
1169cf8e5289SKyle Evans
1170020d003cSKyle Evans]])
1171020d003cSKyle Evans
1172020d003cSKyle Evansfor _, def in pairs(tests) do
1173020d003cSKyle Evans	local func = def.func
1174020d003cSKyle Evans	local function write_tests(heap)
1175020d003cSKyle Evans		-- Dispositions here are relative to the buffer size prescribed
1176020d003cSKyle Evans		-- by the test definition.
1177020d003cSKyle Evans		local dispositions = def.dispositions or { -1, 0, 1 }
1178020d003cSKyle Evans
1179020d003cSKyle Evans		for _, disposition in ipairs(dispositions) do
1180020d003cSKyle Evans			tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
1181020d003cSKyle Evans		end
1182020d003cSKyle Evans	end
1183020d003cSKyle Evans
1184020d003cSKyle Evans	write_tests(false)
1185020d003cSKyle Evans	write_tests(true)
1186020d003cSKyle Evansend
1187020d003cSKyle Evans
1188020d003cSKyle Evansfh:write("ATF_TP_ADD_TCS(tp)\n")
1189020d003cSKyle Evansfh:write("{\n")
1190020d003cSKyle Evansfor idx = 1, #tests_added do
1191020d003cSKyle Evans	fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
1192020d003cSKyle Evansend
1193020d003cSKyle Evansfh:write("\treturn (atf_no_error());\n")
1194020d003cSKyle Evansfh:write("}\n")
1195