xref: /freebsd/lib/libc/tests/secure/generate-fortify-tests.lua (revision cf8e5289a110954600f135024d1515a77d0ae34d)
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",
72020d003cSKyle Evans	"signal.h",
73020d003cSKyle Evans	"stdio.h",
74020d003cSKyle Evans	"stdlib.h",
75020d003cSKyle Evans	"string.h",
76020d003cSKyle Evans	"strings.h",
77020d003cSKyle Evans	"sysexits.h",
78020d003cSKyle Evans	"unistd.h",
79020d003cSKyle Evans	"atf-c.h",
80020d003cSKyle Evans}
81020d003cSKyle Evans
82020d003cSKyle Evanslocal tests_added = {}
83020d003cSKyle Evans
84020d003cSKyle Evans-- Some of these will need to be excluded because clang sees the wrong size when
85020d003cSKyle Evans-- an array is embedded inside a struct, we'll get something that looks more
86020d003cSKyle Evans-- like __builtin_object_size(ptr, 0) than it does the correct
87020d003cSKyle Evans-- __builtin_object_size(ptr, 1) (i.e., includes the padding after).  This is
88020d003cSKyle Evans-- almost certainly a bug in llvm.
89020d003cSKyle Evanslocal function excludes_stack_overflow(disposition, is_heap)
90020d003cSKyle Evans	return (not is_heap) and disposition > 0
91020d003cSKyle Evansend
92020d003cSKyle Evans
93020d003cSKyle Evanslocal printf_stackvars = "\tchar srcvar[__len + 10];\n"
94020d003cSKyle Evanslocal printf_init = [[
95020d003cSKyle Evans	memset(srcvar, 'A', sizeof(srcvar) - 1);
96020d003cSKyle Evans	srcvar[sizeof(srcvar) - 1] = '\0';
97020d003cSKyle Evans]]
98020d003cSKyle Evans
99*cf8e5289SKyle Evanslocal stdio_init = [[
100*cf8e5289SKyle Evans	replace_stdin();
101*cf8e5289SKyle Evans]]
102*cf8e5289SKyle Evans
103020d003cSKyle Evanslocal string_stackvars = "\tchar src[__len];\n"
104020d003cSKyle Evanslocal string_init = [[
105020d003cSKyle Evans	memset(__stack.__buf, 0, __len);
106020d003cSKyle Evans	memset(src, 'A', __len - 1);
107020d003cSKyle Evans	src[__len - 1] = '\0';
108020d003cSKyle Evans]]
109020d003cSKyle Evans
110020d003cSKyle Evans-- Each test entry describes how to test a given function.  We need to know how
111020d003cSKyle Evans-- to construct the buffer, we need to know the argument set we're dealing with,
112020d003cSKyle Evans-- and we need to know what we're passing to each argument.  We could be passing
113020d003cSKyle Evans-- fixed values, or we could be passing the __buf under test.
114020d003cSKyle Evans--
115020d003cSKyle Evans-- definition:
116020d003cSKyle Evans--   func: name of the function under test to call
117020d003cSKyle Evans--   bufsize: size of buffer to generate, defaults to 42
118020d003cSKyle Evans--   buftype: type of buffer to generate, defaults to unsigned char[]
119020d003cSKyle Evans--   arguments: __buf, __len, or the name of a variable placed on the stack
120020d003cSKyle Evans--   exclude: a function(disposition, is_heap) that returns true if this combo
121020d003cSKyle Evans--     should be excluded.
122020d003cSKyle Evans--   stackvars: extra variables to be placed on the stack, should be a string
123020d003cSKyle Evans--     optionally formatted with tabs and newlines
124020d003cSKyle Evans--   init: extra code to inject just before the function call for initialization
125020d003cSKyle Evans--     of the buffer or any of the above-added stackvars; also a string
126020d003cSKyle Evans--   uses_len: bool-ish, necessary if arguments doesn't include either __idx or
127020d003cSKyle Evans--     or __len so that the test generator doesn't try to vary the size of the
128020d003cSKyle Evans--     buffer instead of just manipulating __idx/__len to try and induce an
129020d003cSKyle Evans--     overflow.
130020d003cSKyle Evans--
131020d003cSKyle Evans-- Most tests will just use the default bufsize/buftype, but under some
132020d003cSKyle Evans-- circumstances it's useful to use a different type (e.g., for alignment
133020d003cSKyle Evans-- requirements).
134020d003cSKyle Evanslocal all_tests = {
135020d003cSKyle Evans	stdio = {
136020d003cSKyle Evans		-- <stdio.h>
137020d003cSKyle Evans		{
138*cf8e5289SKyle Evans			func = "ctermid",
139*cf8e5289SKyle Evans			bufsize = "L_ctermid",
140*cf8e5289SKyle Evans			arguments = {
141*cf8e5289SKyle Evans				"__buf",
142*cf8e5289SKyle Evans			},
143*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
144*cf8e5289SKyle Evans		},
145*cf8e5289SKyle Evans		{
146*cf8e5289SKyle Evans			func = "ctermid_r",
147*cf8e5289SKyle Evans			bufsize = "L_ctermid",
148*cf8e5289SKyle Evans			arguments = {
149*cf8e5289SKyle Evans				"__buf",
150*cf8e5289SKyle Evans			},
151*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
152*cf8e5289SKyle Evans		},
153*cf8e5289SKyle Evans		{
154*cf8e5289SKyle Evans			func = "fread",
155*cf8e5289SKyle Evans			arguments = {
156*cf8e5289SKyle Evans				"__buf",
157*cf8e5289SKyle Evans				"__len",
158*cf8e5289SKyle Evans				"1",
159*cf8e5289SKyle Evans				"stdin",
160*cf8e5289SKyle Evans			},
161*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
162*cf8e5289SKyle Evans			init = stdio_init,
163*cf8e5289SKyle Evans		},
164*cf8e5289SKyle Evans		{
165*cf8e5289SKyle Evans			func = "fread_unlocked",
166*cf8e5289SKyle Evans			arguments = {
167*cf8e5289SKyle Evans				"__buf",
168*cf8e5289SKyle Evans				"__len",
169*cf8e5289SKyle Evans				"1",
170*cf8e5289SKyle Evans				"stdin",
171*cf8e5289SKyle Evans			},
172*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
173*cf8e5289SKyle Evans			init = stdio_init,
174*cf8e5289SKyle Evans		},
175*cf8e5289SKyle Evans		{
176*cf8e5289SKyle Evans			func = "gets_s",
177*cf8e5289SKyle Evans			arguments = {
178*cf8e5289SKyle Evans				"__buf",
179*cf8e5289SKyle Evans				"__len",
180*cf8e5289SKyle Evans			},
181*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
182*cf8e5289SKyle Evans			init = stdio_init,
183*cf8e5289SKyle Evans		},
184*cf8e5289SKyle Evans		{
185020d003cSKyle Evans			func = "sprintf",
186020d003cSKyle Evans			arguments = {
187020d003cSKyle Evans				"__buf",
188020d003cSKyle Evans				"\"%.*s\"",
189020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
190020d003cSKyle Evans				"srcvar",
191020d003cSKyle Evans			},
192020d003cSKyle Evans			exclude = excludes_stack_overflow,
193020d003cSKyle Evans			stackvars = printf_stackvars,
194020d003cSKyle Evans			init = printf_init,
195020d003cSKyle Evans		},
196020d003cSKyle Evans		{
197020d003cSKyle Evans			func = "snprintf",
198020d003cSKyle Evans			arguments = {
199020d003cSKyle Evans				"__buf",
200020d003cSKyle Evans				"__len",
201020d003cSKyle Evans				"\"%.*s\"",
202020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
203020d003cSKyle Evans				"srcvar",
204020d003cSKyle Evans			},
205020d003cSKyle Evans			exclude = excludes_stack_overflow,
206020d003cSKyle Evans			stackvars = printf_stackvars,
207020d003cSKyle Evans			init = printf_init,
208020d003cSKyle Evans		},
209*cf8e5289SKyle Evans		{
210*cf8e5289SKyle Evans			func = "tmpnam",
211*cf8e5289SKyle Evans			bufsize = "L_tmpnam",
212*cf8e5289SKyle Evans			arguments = {
213*cf8e5289SKyle Evans				"__buf",
214*cf8e5289SKyle Evans			},
215*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
216*cf8e5289SKyle Evans		},
217*cf8e5289SKyle Evans		{
218*cf8e5289SKyle Evans			func = "fgets",
219*cf8e5289SKyle Evans			arguments = {
220*cf8e5289SKyle Evans				"__buf",
221*cf8e5289SKyle Evans				"__len",
222*cf8e5289SKyle Evans				"fp",
223*cf8e5289SKyle Evans			},
224*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
225*cf8e5289SKyle Evans			stackvars = "\tFILE *fp;\n",
226*cf8e5289SKyle Evans			init = [[
227*cf8e5289SKyle Evans	fp = new_fp(__len);
228*cf8e5289SKyle Evans]],
229*cf8e5289SKyle Evans		},
230020d003cSKyle Evans	},
231020d003cSKyle Evans	string = {
232020d003cSKyle Evans		-- <string.h>
233020d003cSKyle Evans		{
234020d003cSKyle Evans			func = "memcpy",
235020d003cSKyle Evans			arguments = {
236020d003cSKyle Evans				"__buf",
237020d003cSKyle Evans				"src",
238020d003cSKyle Evans				"__len",
239020d003cSKyle Evans			},
240020d003cSKyle Evans			exclude = excludes_stack_overflow,
241020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
242020d003cSKyle Evans		},
243020d003cSKyle Evans		{
244*cf8e5289SKyle Evans			func = "mempcpy",
245*cf8e5289SKyle Evans			arguments = {
246*cf8e5289SKyle Evans				"__buf",
247*cf8e5289SKyle Evans				"src",
248*cf8e5289SKyle Evans				"__len",
249*cf8e5289SKyle Evans			},
250*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
251*cf8e5289SKyle Evans			stackvars = "\tchar src[__len + 10];\n",
252*cf8e5289SKyle Evans		},
253*cf8e5289SKyle Evans		{
254020d003cSKyle Evans			func = "memmove",
255020d003cSKyle Evans			arguments = {
256020d003cSKyle Evans				"__buf",
257020d003cSKyle Evans				"src",
258020d003cSKyle Evans				"__len",
259020d003cSKyle Evans			},
260020d003cSKyle Evans			exclude = excludes_stack_overflow,
261020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
262020d003cSKyle Evans		},
263020d003cSKyle Evans		{
264020d003cSKyle Evans			func = "memset",
265020d003cSKyle Evans			arguments = {
266020d003cSKyle Evans				"__buf",
267020d003cSKyle Evans				"0",
268020d003cSKyle Evans				"__len",
269020d003cSKyle Evans			},
270020d003cSKyle Evans			exclude = excludes_stack_overflow,
271020d003cSKyle Evans		},
272020d003cSKyle Evans		{
273020d003cSKyle Evans			func = "stpcpy",
274020d003cSKyle Evans			arguments = {
275020d003cSKyle Evans				"__buf",
276020d003cSKyle Evans				"src",
277020d003cSKyle Evans			},
278020d003cSKyle Evans			exclude = excludes_stack_overflow,
279020d003cSKyle Evans			stackvars = string_stackvars,
280020d003cSKyle Evans			init = string_init,
281020d003cSKyle Evans			uses_len = true,
282020d003cSKyle Evans		},
283020d003cSKyle Evans		{
284020d003cSKyle Evans			func = "stpncpy",
285020d003cSKyle Evans			arguments = {
286020d003cSKyle Evans				"__buf",
287020d003cSKyle Evans				"src",
288020d003cSKyle Evans				"__len",
289020d003cSKyle Evans			},
290020d003cSKyle Evans			exclude = excludes_stack_overflow,
291020d003cSKyle Evans			stackvars = string_stackvars,
292020d003cSKyle Evans			init = string_init,
293020d003cSKyle Evans		},
294020d003cSKyle Evans		{
295020d003cSKyle Evans			func = "strcat",
296020d003cSKyle Evans			arguments = {
297020d003cSKyle Evans				"__buf",
298020d003cSKyle Evans				"src",
299020d003cSKyle Evans			},
300020d003cSKyle Evans			exclude = excludes_stack_overflow,
301020d003cSKyle Evans			stackvars = string_stackvars,
302020d003cSKyle Evans			init = string_init,
303020d003cSKyle Evans			uses_len = true,
304020d003cSKyle Evans		},
305020d003cSKyle Evans		{
306*cf8e5289SKyle Evans			func = "strlcat",
307*cf8e5289SKyle Evans			arguments = {
308*cf8e5289SKyle Evans				"__buf",
309*cf8e5289SKyle Evans				"src",
310*cf8e5289SKyle Evans				"__len",
311*cf8e5289SKyle Evans			},
312*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
313*cf8e5289SKyle Evans			stackvars = string_stackvars,
314*cf8e5289SKyle Evans			init = string_init,
315*cf8e5289SKyle Evans		},
316*cf8e5289SKyle Evans		{
317020d003cSKyle Evans			func = "strncat",
318020d003cSKyle Evans			arguments = {
319020d003cSKyle Evans				"__buf",
320020d003cSKyle Evans				"src",
321020d003cSKyle Evans				"__len",
322020d003cSKyle Evans			},
323020d003cSKyle Evans			exclude = excludes_stack_overflow,
324020d003cSKyle Evans			stackvars = string_stackvars,
325020d003cSKyle Evans			init = string_init,
326020d003cSKyle Evans		},
327020d003cSKyle Evans		{
328020d003cSKyle Evans			func = "strcpy",
329020d003cSKyle Evans			arguments = {
330020d003cSKyle Evans				"__buf",
331020d003cSKyle Evans				"src",
332020d003cSKyle Evans			},
333020d003cSKyle Evans			exclude = excludes_stack_overflow,
334020d003cSKyle Evans			stackvars = string_stackvars,
335020d003cSKyle Evans			init = string_init,
336020d003cSKyle Evans			uses_len = true,
337020d003cSKyle Evans		},
338020d003cSKyle Evans		{
339*cf8e5289SKyle Evans			func = "strlcpy",
340*cf8e5289SKyle Evans			arguments = {
341*cf8e5289SKyle Evans				"__buf",
342*cf8e5289SKyle Evans				"src",
343*cf8e5289SKyle Evans				"__len",
344*cf8e5289SKyle Evans			},
345*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
346*cf8e5289SKyle Evans			stackvars = string_stackvars,
347*cf8e5289SKyle Evans			init = string_init,
348*cf8e5289SKyle Evans		},
349*cf8e5289SKyle Evans		{
350020d003cSKyle Evans			func = "strncpy",
351020d003cSKyle Evans			arguments = {
352020d003cSKyle Evans				"__buf",
353020d003cSKyle Evans				"src",
354020d003cSKyle Evans				"__len",
355020d003cSKyle Evans			},
356020d003cSKyle Evans			exclude = excludes_stack_overflow,
357020d003cSKyle Evans			stackvars = string_stackvars,
358020d003cSKyle Evans			init = string_init,
359020d003cSKyle Evans		},
360020d003cSKyle Evans	},
361020d003cSKyle Evans	strings = {
362020d003cSKyle Evans		-- <strings.h>
363020d003cSKyle Evans		{
364020d003cSKyle Evans			func = "bcopy",
365020d003cSKyle Evans			arguments = {
366020d003cSKyle Evans				"src",
367020d003cSKyle Evans				"__buf",
368020d003cSKyle Evans				"__len",
369020d003cSKyle Evans			},
370020d003cSKyle Evans			exclude = excludes_stack_overflow,
371020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
372020d003cSKyle Evans		},
373020d003cSKyle Evans		{
374020d003cSKyle Evans			func = "bzero",
375020d003cSKyle Evans			arguments = {
376020d003cSKyle Evans				"__buf",
377020d003cSKyle Evans				"__len",
378020d003cSKyle Evans			},
379020d003cSKyle Evans			exclude = excludes_stack_overflow,
380020d003cSKyle Evans		},
381*cf8e5289SKyle Evans		{
382*cf8e5289SKyle Evans			func = "explicit_bzero",
383*cf8e5289SKyle Evans			arguments = {
384*cf8e5289SKyle Evans				"__buf",
385*cf8e5289SKyle Evans				"__len",
386*cf8e5289SKyle Evans			},
387*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
388*cf8e5289SKyle Evans		},
389020d003cSKyle Evans	},
390020d003cSKyle Evans	unistd = {
391020d003cSKyle Evans		-- <unistd.h>
392020d003cSKyle Evans		{
393020d003cSKyle Evans			func = "getcwd",
394020d003cSKyle Evans			bufsize = "8",
395020d003cSKyle Evans			arguments = {
396020d003cSKyle Evans				"__buf",
397020d003cSKyle Evans				"__len",
398020d003cSKyle Evans			},
399020d003cSKyle Evans			exclude = excludes_stack_overflow,
400020d003cSKyle Evans		},
401020d003cSKyle Evans		{
402*cf8e5289SKyle Evans			func = "getgrouplist",
403*cf8e5289SKyle Evans			bufsize = "4",
404*cf8e5289SKyle Evans			buftype = "gid_t[]",
405*cf8e5289SKyle Evans			arguments = {
406*cf8e5289SKyle Evans				"\"root\"",
407*cf8e5289SKyle Evans				"0",
408*cf8e5289SKyle Evans				"__buf",
409*cf8e5289SKyle Evans				"&intlen",
410*cf8e5289SKyle Evans			},
411*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
412*cf8e5289SKyle Evans			stackvars = "\tint intlen = (int)__len;\n",
413*cf8e5289SKyle Evans			uses_len = true,
414*cf8e5289SKyle Evans		},
415*cf8e5289SKyle Evans		{
416*cf8e5289SKyle Evans			func = "getgroups",
417*cf8e5289SKyle Evans			bufsize = "4",
418*cf8e5289SKyle Evans			buftype = "gid_t[]",
419*cf8e5289SKyle Evans			arguments = {
420*cf8e5289SKyle Evans				"__len",
421*cf8e5289SKyle Evans				"__buf",
422*cf8e5289SKyle Evans			},
423*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
424*cf8e5289SKyle Evans		},
425*cf8e5289SKyle Evans		{
426*cf8e5289SKyle Evans			func = "getloginclass",
427*cf8e5289SKyle Evans			arguments = {
428*cf8e5289SKyle Evans				"__buf",
429*cf8e5289SKyle Evans				"__len",
430*cf8e5289SKyle Evans			},
431*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
432*cf8e5289SKyle Evans		},
433*cf8e5289SKyle Evans		{
434*cf8e5289SKyle Evans			func = "pread",
435*cf8e5289SKyle Evans			bufsize = "41",
436*cf8e5289SKyle Evans			arguments = {
437*cf8e5289SKyle Evans				"fd",
438*cf8e5289SKyle Evans				"__buf",
439*cf8e5289SKyle Evans				"__len",
440*cf8e5289SKyle Evans				"0",
441*cf8e5289SKyle Evans			},
442*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
443*cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
444*cf8e5289SKyle Evans			init = [[
445*cf8e5289SKyle Evans	fd = new_tmpfile();	/* Cannot fail */
446*cf8e5289SKyle Evans]],
447*cf8e5289SKyle Evans		},
448*cf8e5289SKyle Evans		{
449020d003cSKyle Evans			func = "read",
450020d003cSKyle Evans			bufsize = "41",
451020d003cSKyle Evans			arguments = {
452020d003cSKyle Evans				"fd",
453020d003cSKyle Evans				"__buf",
454020d003cSKyle Evans				"__len",
455020d003cSKyle Evans			},
456020d003cSKyle Evans			exclude = excludes_stack_overflow,
457020d003cSKyle Evans			stackvars = "\tint fd;\n",
458020d003cSKyle Evans			init = [[
459020d003cSKyle Evans	fd = new_tmpfile();	/* Cannot fail */
460020d003cSKyle Evans]],
461020d003cSKyle Evans		},
462020d003cSKyle Evans		{
463020d003cSKyle Evans			func = "readlink",
464020d003cSKyle Evans			arguments = {
465020d003cSKyle Evans				"path",
466020d003cSKyle Evans				"__buf",
467020d003cSKyle Evans				"__len",
468020d003cSKyle Evans			},
469020d003cSKyle Evans			exclude = excludes_stack_overflow,
470020d003cSKyle Evans			stackvars = "\tconst char *path;\n",
471020d003cSKyle Evans			init = [[
472020d003cSKyle Evans	path = new_symlink(__len);		/* Cannot fail */
473020d003cSKyle Evans]],
474020d003cSKyle Evans		},
475*cf8e5289SKyle Evans		{
476*cf8e5289SKyle Evans			func = "readlinkat",
477*cf8e5289SKyle Evans			arguments = {
478*cf8e5289SKyle Evans				"AT_FDCWD",
479*cf8e5289SKyle Evans				"path",
480*cf8e5289SKyle Evans				"__buf",
481*cf8e5289SKyle Evans				"__len",
482*cf8e5289SKyle Evans			},
483*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
484*cf8e5289SKyle Evans			stackvars = "\tconst char *path;\n",
485*cf8e5289SKyle Evans			init = [[
486*cf8e5289SKyle Evans	path = new_symlink(__len);		/* Cannot fail */
487*cf8e5289SKyle Evans]],
488*cf8e5289SKyle Evans		},
489*cf8e5289SKyle Evans		{
490*cf8e5289SKyle Evans			func = "getdomainname",
491*cf8e5289SKyle Evans			bufsize = "4",
492*cf8e5289SKyle Evans			arguments = {
493*cf8e5289SKyle Evans				"__buf",
494*cf8e5289SKyle Evans				"__len",
495*cf8e5289SKyle Evans			},
496*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
497*cf8e5289SKyle Evans			stackvars = "\tchar sysdomain[256];\n",
498*cf8e5289SKyle Evans			early_init = [[
499*cf8e5289SKyle Evans	(void)getdomainname(sysdomain, __len);
500*cf8e5289SKyle Evans	if (strlen(sysdomain) <= __len)
501*cf8e5289SKyle Evans		atf_tc_skip("domain name too short for testing");
502*cf8e5289SKyle Evans]]
503*cf8e5289SKyle Evans		},
504*cf8e5289SKyle Evans		{
505*cf8e5289SKyle Evans			func = "getentropy",
506*cf8e5289SKyle Evans			arguments = {
507*cf8e5289SKyle Evans				"__buf",
508*cf8e5289SKyle Evans				"__len",
509*cf8e5289SKyle Evans			},
510*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
511*cf8e5289SKyle Evans		},
512*cf8e5289SKyle Evans		{
513*cf8e5289SKyle Evans			func = "gethostname",
514*cf8e5289SKyle Evans			bufsize = "4",
515*cf8e5289SKyle Evans			arguments = {
516*cf8e5289SKyle Evans				"__buf",
517*cf8e5289SKyle Evans				"__len",
518*cf8e5289SKyle Evans			},
519*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
520*cf8e5289SKyle Evans			stackvars = [[
521*cf8e5289SKyle Evans	char syshost[256];
522*cf8e5289SKyle Evans	int error;
523*cf8e5289SKyle Evans]],
524*cf8e5289SKyle Evans			early_init = [[
525*cf8e5289SKyle Evans	error = gethostname(syshost, __len);
526*cf8e5289SKyle Evans	if (error != 0 || strlen(syshost) <= __len)
527*cf8e5289SKyle Evans		atf_tc_skip("hostname too short for testing");
528*cf8e5289SKyle Evans]]
529*cf8e5289SKyle Evans		},
530*cf8e5289SKyle Evans		{
531*cf8e5289SKyle Evans			func = "getlogin_r",
532*cf8e5289SKyle Evans			bufsize = "MAXLOGNAME + 1",
533*cf8e5289SKyle Evans			arguments = {
534*cf8e5289SKyle Evans				"__buf",
535*cf8e5289SKyle Evans				"__len",
536*cf8e5289SKyle Evans			},
537*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
538*cf8e5289SKyle Evans		},
539*cf8e5289SKyle Evans		{
540*cf8e5289SKyle Evans			func = "ttyname_r",
541*cf8e5289SKyle Evans			arguments = {
542*cf8e5289SKyle Evans				"fd",
543*cf8e5289SKyle Evans				"__buf",
544*cf8e5289SKyle Evans				"__len",
545*cf8e5289SKyle Evans			},
546*cf8e5289SKyle Evans			exclude = excludes_stack_overflow,
547*cf8e5289SKyle Evans			stackvars = "\tint fd;\n",
548*cf8e5289SKyle Evans			early_init = [[
549*cf8e5289SKyle Evans	fd = STDIN_FILENO;
550*cf8e5289SKyle Evans	if (!isatty(fd))
551*cf8e5289SKyle Evans		atf_tc_skip("stdin is not an fd");
552*cf8e5289SKyle Evans]]
553*cf8e5289SKyle Evans		},
554020d003cSKyle Evans	},
555020d003cSKyle Evans}
556020d003cSKyle Evans
557020d003cSKyle Evanslocal function write_test_boilerplate(fh, name, body)
558020d003cSKyle Evans	fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
559020d003cSKyle Evans	fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
560020d003cSKyle Evans	fh:write("{\n" .. body .. "\n}\n\n")
561020d003cSKyle Evans	return name
562020d003cSKyle Evansend
563020d003cSKyle Evans
564020d003cSKyle Evanslocal function generate_test_name(func, variant, disposition, heap)
565020d003cSKyle Evans	local basename = func
566020d003cSKyle Evans	if variant then
567020d003cSKyle Evans		basename = basename .. "_" .. variant
568020d003cSKyle Evans	end
569020d003cSKyle Evans	if heap then
570020d003cSKyle Evans		basename = basename .. "_heap"
571020d003cSKyle Evans	end
572020d003cSKyle Evans	if disposition < 0 then
573020d003cSKyle Evans		return basename .. "_before_end"
574020d003cSKyle Evans	elseif disposition == 0 then
575020d003cSKyle Evans		return basename .. "_end"
576020d003cSKyle Evans	else
577020d003cSKyle Evans		return basename .. "_after_end"
578020d003cSKyle Evans	end
579020d003cSKyle Evansend
580020d003cSKyle Evans
581020d003cSKyle Evanslocal function array_type(buftype)
582020d003cSKyle Evans	if not buftype:match("%[%]") then
583020d003cSKyle Evans		return nil
584020d003cSKyle Evans	end
585020d003cSKyle Evans
586020d003cSKyle Evans	return buftype:gsub("%[%]", "")
587020d003cSKyle Evansend
588020d003cSKyle Evans
589020d003cSKyle Evanslocal function configurable(def, idx)
590020d003cSKyle Evans	local cfgitem = def[idx]
591020d003cSKyle Evans
592020d003cSKyle Evans	if not cfgitem then
593020d003cSKyle Evans		return nil
594020d003cSKyle Evans	end
595020d003cSKyle Evans
596020d003cSKyle Evans	if type(cfgitem) == "function" then
597020d003cSKyle Evans		return cfgitem()
598020d003cSKyle Evans	end
599020d003cSKyle Evans
600020d003cSKyle Evans	return cfgitem
601020d003cSKyle Evansend
602020d003cSKyle Evans
603020d003cSKyle Evanslocal function generate_stackframe(buftype, bufsize, disposition, heap, def)
604020d003cSKyle Evans	local function len_offset(inverted, disposition)
605020d003cSKyle Evans		-- Tests that don't use __len in their arguments may use an
606020d003cSKyle Evans		-- inverted sense because we can't just specify a length that
607020d003cSKyle Evans		-- would induce an access just after the end.  Instead, we have
608020d003cSKyle Evans		-- to manipulate the buffer size to be too short so that the
609020d003cSKyle Evans		-- function under test would write one too many.
610020d003cSKyle Evans		if disposition < 0 then
611020d003cSKyle Evans			return ((inverted and " + ") or " - ") .. "1"
612020d003cSKyle Evans		elseif disposition == 0 then
613020d003cSKyle Evans			return ""
614020d003cSKyle Evans		else
615020d003cSKyle Evans			return ((inverted and " - ") or " + ") .. "1"
616020d003cSKyle Evans		end
617020d003cSKyle Evans	end
618020d003cSKyle Evans
619020d003cSKyle Evans	local function test_uses_len(def)
620020d003cSKyle Evans		if def.uses_len then
621020d003cSKyle Evans			return true
622020d003cSKyle Evans		end
623020d003cSKyle Evans
624020d003cSKyle Evans		for _, arg in ipairs(def.arguments) do
625020d003cSKyle Evans			if arg:match("__len") or arg:match("__idx") then
626020d003cSKyle Evans				return true
627020d003cSKyle Evans			end
628020d003cSKyle Evans		end
629020d003cSKyle Evans
630020d003cSKyle Evans		return false
631020d003cSKyle Evans	end
632020d003cSKyle Evans
633020d003cSKyle Evans
634020d003cSKyle Evans	-- This is perhaps a little convoluted, but we toss the buffer into a
635020d003cSKyle Evans	-- struct on the stack to guarantee that we have at least one valid
636020d003cSKyle Evans	-- byte on either side of the buffer -- a measure to make sure that
637020d003cSKyle Evans	-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
638020d003cSKyle Evans	-- rather than some other stack or memory protection.
639020d003cSKyle Evans	local vars = "\tstruct {\n"
640020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_l;\n"
641020d003cSKyle Evans
642020d003cSKyle Evans	local uses_len = test_uses_len(def)
643020d003cSKyle Evans	local bufsize_offset = len_offset(not uses_len, disposition)
644020d003cSKyle Evans	local buftype_elem = array_type(buftype)
645020d003cSKyle Evans	local size_expr = bufsize
646020d003cSKyle Evans
647020d003cSKyle Evans	if not uses_len then
648020d003cSKyle Evans		-- If the length isn't in use, we have to vary the buffer size
649020d003cSKyle Evans		-- since the fortified function likely has some internal size
650020d003cSKyle Evans		-- constraint that it's supposed to be checking.
651020d003cSKyle Evans		size_expr = size_expr .. bufsize_offset
652020d003cSKyle Evans	end
653020d003cSKyle Evans
654020d003cSKyle Evans	if not heap and buftype_elem then
655020d003cSKyle Evans		-- Array type: size goes after identifier
656020d003cSKyle Evans		vars = vars .. "\t\t" .. buftype_elem ..
657020d003cSKyle Evans		    " __buf[" .. size_expr .. "];\n"
658020d003cSKyle Evans	else
659020d003cSKyle Evans		local basic_type = buftype_elem or buftype
660020d003cSKyle Evans
661020d003cSKyle Evans		-- Heap tests obviously just put a pointer on the stack that
662020d003cSKyle Evans		-- points to our new allocation, but we leave it in the padded
663020d003cSKyle Evans		-- struct just to simplify our generator.
664020d003cSKyle Evans		if heap then
665020d003cSKyle Evans			basic_type = basic_type .. " *"
666020d003cSKyle Evans		end
667020d003cSKyle Evans		vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
668020d003cSKyle Evans	end
669020d003cSKyle Evans
670020d003cSKyle Evans	-- padding_r is our just-past-the-end padding that we use to make sure
671020d003cSKyle Evans	-- that there's a valid portion after the buffer that isn't being
672020d003cSKyle Evans	-- included in our function calls.  If we didn't have it, then we'd have
673020d003cSKyle Evans	-- a hard time feeling confident that an abort on the just-after tests
674020d003cSKyle Evans	-- isn't maybe from some other memory or stack protection.
675020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_r;\n"
676020d003cSKyle Evans	vars = vars .. "\t} __stack;\n"
677020d003cSKyle Evans
678020d003cSKyle Evans	-- Not all tests will use __bufsz, but some do for, e.g., clearing
679020d003cSKyle Evans	-- memory..
680020d003cSKyle Evans	vars = vars .. "\tconst size_t __bufsz __unused = "
681020d003cSKyle Evans	if heap then
682020d003cSKyle Evans		local scalar = 1
683020d003cSKyle Evans		if buftype_elem then
684020d003cSKyle Evans			scalar = size_expr
685020d003cSKyle Evans		end
686020d003cSKyle Evans
687020d003cSKyle Evans		vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
688020d003cSKyle Evans	else
689020d003cSKyle Evans		vars = vars .. "sizeof(__stack.__buf);\n"
690020d003cSKyle Evans	end
691020d003cSKyle Evans
692020d003cSKyle Evans	vars = vars .. "\tconst size_t __len = " .. bufsize ..
693020d003cSKyle Evans	    bufsize_offset .. ";\n"
694020d003cSKyle Evans	vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
695020d003cSKyle Evans
696020d003cSKyle Evans	-- For overflow testing, we need to fork() because we're expecting the
697020d003cSKyle Evans	-- test to ultimately abort()/_exit().  Then we can collect the exit
698020d003cSKyle Evans	-- status and report appropriately.
699020d003cSKyle Evans	if disposition > 0 then
700020d003cSKyle Evans		vars = vars .. "\tpid_t __child;\n"
701020d003cSKyle Evans		vars = vars .. "\tint __status;\n"
702020d003cSKyle Evans	end
703020d003cSKyle Evans
704020d003cSKyle Evans	-- Any other stackvars defined by the test get placed after everything
705020d003cSKyle Evans	-- else.
706020d003cSKyle Evans	vars = vars .. (configurable(def, "stackvars") or "")
707020d003cSKyle Evans
708020d003cSKyle Evans	return vars
709020d003cSKyle Evansend
710020d003cSKyle Evans
711020d003cSKyle Evanslocal function write_test(fh, func, disposition, heap, def)
712020d003cSKyle Evans	local testname = generate_test_name(func, def.variant, disposition, heap)
713020d003cSKyle Evans	local buftype = def.buftype or "unsigned char[]"
714020d003cSKyle Evans	local bufsize = def.bufsize or 42
715020d003cSKyle Evans	local body = ""
716020d003cSKyle Evans
717020d003cSKyle Evans	if def.exclude and def.exclude(disposition, heap) then
718020d003cSKyle Evans		return
719020d003cSKyle Evans	end
720020d003cSKyle Evans
721020d003cSKyle Evans	local function need_addr(buftype)
722020d003cSKyle Evans		return not (buftype:match("%[%]") or buftype:match("%*"))
723020d003cSKyle Evans	end
724020d003cSKyle Evans
725020d003cSKyle Evans	if heap then
726020d003cSKyle Evans		body = body .. "#define BUF __stack.__buf\n"
727020d003cSKyle Evans	else
728020d003cSKyle Evans		body = body .. "#define BUF &__stack.__buf\n"
729020d003cSKyle Evans	end
730020d003cSKyle Evans
731020d003cSKyle Evans	-- Setup the buffer
732020d003cSKyle Evans	body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
733020d003cSKyle Evans	    "\n"
734020d003cSKyle Evans
735020d003cSKyle Evans	-- Any early initialization goes before we would fork for the just-after
736020d003cSKyle Evans	-- tests, because they may want to skip the test based on some criteria
737020d003cSKyle Evans	-- and we can't propagate that up very easily once we're forked.
738020d003cSKyle Evans	local early_init = configurable(def, "early_init")
739020d003cSKyle Evans	body = body .. (early_init or "")
740020d003cSKyle Evans	if early_init then
741020d003cSKyle Evans		body = body .. "\n"
742020d003cSKyle Evans	end
743020d003cSKyle Evans
744020d003cSKyle Evans	-- Fork off, iff we're testing some access past the end of the buffer.
745020d003cSKyle Evans	if disposition > 0 then
746020d003cSKyle Evans		body = body .. [[
747020d003cSKyle Evans	__child = fork();
748020d003cSKyle Evans	ATF_REQUIRE(__child >= 0);
749020d003cSKyle Evans	if (__child > 0)
750020d003cSKyle Evans		goto monitor;
751020d003cSKyle Evans
752020d003cSKyle Evans	/* Child */
753020d003cSKyle Evans	disable_coredumps();
754020d003cSKyle Evans]]
755020d003cSKyle Evans	end
756020d003cSKyle Evans
757020d003cSKyle Evans	local bufvar = "__stack.__buf"
758020d003cSKyle Evans	if heap then
759020d003cSKyle Evans		-- Buffer needs to be initialized because it's a heap allocation.
760020d003cSKyle Evans		body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
761020d003cSKyle Evans	end
762020d003cSKyle Evans
763020d003cSKyle Evans	-- Non-early init happens just after the fork in the child, not the
764020d003cSKyle Evans	-- monitor.  This is used to setup any other buffers we may need, for
765020d003cSKyle Evans	-- instance.
766020d003cSKyle Evans	local extra_init = configurable(def, "init")
767020d003cSKyle Evans	body = body .. (extra_init or "")
768020d003cSKyle Evans
769020d003cSKyle Evans	if heap or extra_init then
770020d003cSKyle Evans		body = body .. "\n"
771020d003cSKyle Evans	end
772020d003cSKyle Evans
773020d003cSKyle Evans	-- Setup the function call with arguments as described in the test
774020d003cSKyle Evans	-- definition.
775020d003cSKyle Evans	body = body .. "\t" .. func .. "("
776020d003cSKyle Evans
777020d003cSKyle Evans	for idx, arg in ipairs(def.arguments) do
778020d003cSKyle Evans		if idx > 1 then
779020d003cSKyle Evans			body = body .. ", "
780020d003cSKyle Evans		end
781020d003cSKyle Evans
782020d003cSKyle Evans		if arg == "__buf" then
783020d003cSKyle Evans			if not heap and need_addr(buftype) then
784020d003cSKyle Evans				body = body .. "&"
785020d003cSKyle Evans			end
786020d003cSKyle Evans
787020d003cSKyle Evans			body = body .. bufvar
788020d003cSKyle Evans		else
789020d003cSKyle Evans			local argname = arg
790020d003cSKyle Evans
791020d003cSKyle Evans			if def.value_of then
792020d003cSKyle Evans				argname = argname or def.value_of(arg)
793020d003cSKyle Evans			end
794020d003cSKyle Evans
795020d003cSKyle Evans			body = body .. argname
796020d003cSKyle Evans		end
797020d003cSKyle Evans	end
798020d003cSKyle Evans
799020d003cSKyle Evans	body = body .. ");\n"
800020d003cSKyle Evans
801020d003cSKyle Evans	-- Monitor stuff follows, for OOB access.
802020d003cSKyle Evans	if disposition <= 0 then
803020d003cSKyle Evans		goto skip
804020d003cSKyle Evans	end
805020d003cSKyle Evans
806020d003cSKyle Evans	body = body .. [[
807020d003cSKyle Evans	_exit(EX_SOFTWARE);	/* Should have aborted. */
808020d003cSKyle Evans
809020d003cSKyle Evansmonitor:
810020d003cSKyle Evans	while (waitpid(__child, &__status, 0) != __child) {
811020d003cSKyle Evans		ATF_REQUIRE_EQ(EINTR, errno);
812020d003cSKyle Evans	}
813020d003cSKyle Evans
814020d003cSKyle Evans	if (!WIFSIGNALED(__status)) {
815020d003cSKyle Evans		switch (WEXITSTATUS(__status)) {
816020d003cSKyle Evans		case EX_SOFTWARE:
817020d003cSKyle Evans			atf_tc_fail("FORTIFY_SOURCE failed to abort");
818020d003cSKyle Evans			break;
819020d003cSKyle Evans		case EX_OSERR:
820020d003cSKyle Evans			atf_tc_fail("setrlimit(2) failed");
821020d003cSKyle Evans			break;
822020d003cSKyle Evans		default:
823020d003cSKyle Evans			atf_tc_fail("child exited with status %d",
824020d003cSKyle Evans			    WEXITSTATUS(__status));
825020d003cSKyle Evans		}
826020d003cSKyle Evans	} else {
827020d003cSKyle Evans		ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
828020d003cSKyle Evans	}
829020d003cSKyle Evans]]
830020d003cSKyle Evans
831020d003cSKyle Evans::skip::
832020d003cSKyle Evans	body = body .. "#undef BUF\n"
833020d003cSKyle Evans	return write_test_boilerplate(fh, testname, body)
834020d003cSKyle Evansend
835020d003cSKyle Evans
836020d003cSKyle Evans-- main()
837020d003cSKyle Evanslocal tests
838020d003cSKyle Evanslocal tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
839020d003cSKyle Evansfor k, defs in pairs(all_tests) do
840020d003cSKyle Evans	if k == tcat then
841020d003cSKyle Evans		tests = defs
842020d003cSKyle Evans		break
843020d003cSKyle Evans	end
844020d003cSKyle Evansend
845020d003cSKyle Evans
846020d003cSKyle Evansassert(tests, "category " .. tcat .. " not found")
847020d003cSKyle Evans
848020d003cSKyle Evanslocal fh = io.stdout
849020d003cSKyle Evansfh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
850020d003cSKyle Evans    tcat .. "\"` */\n\n")
851020d003cSKyle Evansfh:write("#define	_FORTIFY_SOURCE	2\n")
852020d003cSKyle Evansfh:write("#define	TMPFILE_SIZE	(1024 * 32)\n")
853020d003cSKyle Evans
854020d003cSKyle Evansfh:write("\n")
855020d003cSKyle Evansfor _, inc in ipairs(includes) do
856020d003cSKyle Evans	fh:write("#include <" .. inc .. ">\n")
857020d003cSKyle Evansend
858020d003cSKyle Evans
859020d003cSKyle Evansfh:write([[
860020d003cSKyle Evans
861*cf8e5289SKyle Evansstatic FILE * __unused
862*cf8e5289SKyle Evansnew_fp(size_t __len)
863*cf8e5289SKyle Evans{
864*cf8e5289SKyle Evans	static char fpbuf[LINE_MAX];
865*cf8e5289SKyle Evans	FILE *fp;
866*cf8e5289SKyle Evans
867*cf8e5289SKyle Evans	ATF_REQUIRE(__len <= sizeof(fpbuf));
868*cf8e5289SKyle Evans
869*cf8e5289SKyle Evans	memset(fpbuf, 'A', sizeof(fpbuf) - 1);
870*cf8e5289SKyle Evans	fpbuf[sizeof(fpbuf) - 1] = '\0';
871*cf8e5289SKyle Evans
872*cf8e5289SKyle Evans	fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
873*cf8e5289SKyle Evans	ATF_REQUIRE(fp != NULL);
874*cf8e5289SKyle Evans
875*cf8e5289SKyle Evans	return (fp);
876*cf8e5289SKyle Evans}
877*cf8e5289SKyle Evans
878020d003cSKyle Evans/*
879020d003cSKyle Evans * Create a new symlink to use for readlink(2) style tests, we'll just use a
880020d003cSKyle Evans * random target name to have something interesting to look at.
881020d003cSKyle Evans */
882020d003cSKyle Evansstatic const char * __unused
883020d003cSKyle Evansnew_symlink(size_t __len)
884020d003cSKyle Evans{
885020d003cSKyle Evans	static const char linkname[] = "link";
886020d003cSKyle Evans	char target[MAXNAMLEN];
887020d003cSKyle Evans	int error;
888020d003cSKyle Evans
889020d003cSKyle Evans	ATF_REQUIRE(__len <= sizeof(target));
890020d003cSKyle Evans
891020d003cSKyle Evans	arc4random_buf(target, sizeof(target));
892020d003cSKyle Evans
893020d003cSKyle Evans	error = unlink(linkname);
894020d003cSKyle Evans	ATF_REQUIRE(error == 0 || errno == ENOENT);
895020d003cSKyle Evans
896020d003cSKyle Evans	error = symlink(target, linkname);
897020d003cSKyle Evans	ATF_REQUIRE(error == 0);
898020d003cSKyle Evans
899020d003cSKyle Evans	return (linkname);
900020d003cSKyle Evans}
901020d003cSKyle Evans
902020d003cSKyle Evans/*
903020d003cSKyle Evans * Constructs a tmpfile that we can use for testing read(2) and friends.
904020d003cSKyle Evans */
905020d003cSKyle Evansstatic int __unused
906020d003cSKyle Evansnew_tmpfile(void)
907020d003cSKyle Evans{
908020d003cSKyle Evans	char buf[1024];
909020d003cSKyle Evans	ssize_t rv;
910020d003cSKyle Evans	size_t written;
911020d003cSKyle Evans	int fd;
912020d003cSKyle Evans
913020d003cSKyle Evans	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
914020d003cSKyle Evans	ATF_REQUIRE(fd >= 0);
915020d003cSKyle Evans
916020d003cSKyle Evans	written = 0;
917020d003cSKyle Evans	while (written < TMPFILE_SIZE) {
918020d003cSKyle Evans		rv = write(fd, buf, sizeof(buf));
919020d003cSKyle Evans		ATF_REQUIRE(rv > 0);
920020d003cSKyle Evans
921020d003cSKyle Evans		written += rv;
922020d003cSKyle Evans	}
923020d003cSKyle Evans
924020d003cSKyle Evans	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
925020d003cSKyle Evans	return (fd);
926020d003cSKyle Evans}
927020d003cSKyle Evans
928020d003cSKyle Evansstatic void
929020d003cSKyle Evansdisable_coredumps(void)
930020d003cSKyle Evans{
931020d003cSKyle Evans	struct rlimit rl = { 0 };
932020d003cSKyle Evans
933020d003cSKyle Evans	if (setrlimit(RLIMIT_CORE, &rl) == -1)
934020d003cSKyle Evans		_exit(EX_OSERR);
935020d003cSKyle Evans}
936020d003cSKyle Evans
937*cf8e5289SKyle Evans/*
938*cf8e5289SKyle Evans * Replaces stdin with a file that we can actually read from, for tests where
939*cf8e5289SKyle Evans * we want a FILE * or fd that we can get data from.
940*cf8e5289SKyle Evans */
941*cf8e5289SKyle Evansstatic void __unused
942*cf8e5289SKyle Evansreplace_stdin(void)
943*cf8e5289SKyle Evans{
944*cf8e5289SKyle Evans	int fd;
945*cf8e5289SKyle Evans
946*cf8e5289SKyle Evans	fd = new_tmpfile();
947*cf8e5289SKyle Evans
948*cf8e5289SKyle Evans	(void)dup2(fd, STDIN_FILENO);
949*cf8e5289SKyle Evans	if (fd != STDIN_FILENO)
950*cf8e5289SKyle Evans		close(fd);
951*cf8e5289SKyle Evans}
952*cf8e5289SKyle Evans
953020d003cSKyle Evans]])
954020d003cSKyle Evans
955020d003cSKyle Evansfor _, def in pairs(tests) do
956020d003cSKyle Evans	local func = def.func
957020d003cSKyle Evans	local function write_tests(heap)
958020d003cSKyle Evans		-- Dispositions here are relative to the buffer size prescribed
959020d003cSKyle Evans		-- by the test definition.
960020d003cSKyle Evans		local dispositions = def.dispositions or { -1, 0, 1 }
961020d003cSKyle Evans
962020d003cSKyle Evans		for _, disposition in ipairs(dispositions) do
963020d003cSKyle Evans			tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
964020d003cSKyle Evans		end
965020d003cSKyle Evans	end
966020d003cSKyle Evans
967020d003cSKyle Evans	write_tests(false)
968020d003cSKyle Evans	write_tests(true)
969020d003cSKyle Evansend
970020d003cSKyle Evans
971020d003cSKyle Evansfh:write("ATF_TP_ADD_TCS(tp)\n")
972020d003cSKyle Evansfh:write("{\n")
973020d003cSKyle Evansfor idx = 1, #tests_added do
974020d003cSKyle Evans	fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
975020d003cSKyle Evansend
976020d003cSKyle Evansfh:write("\treturn (atf_no_error());\n")
977020d003cSKyle Evansfh:write("}\n")
978