xref: /freebsd/lib/libc/tests/secure/generate-fortify-tests.lua (revision 020d003c86367bb5751b6d58fb58611242802c7f)
1*020d003cSKyle Evans#!/usr/libexec/flua
2*020d003cSKyle Evans--
3*020d003cSKyle Evans-- SPDX-License-Identifier: BSD-2-Clause
4*020d003cSKyle Evans--
5*020d003cSKyle Evans-- Copyright (c) 2024, Klara, Inc.
6*020d003cSKyle Evans--
7*020d003cSKyle Evans-- Redistribution and use in source and binary forms, with or without
8*020d003cSKyle Evans-- modification, are permitted provided that the following conditions
9*020d003cSKyle Evans-- are met:
10*020d003cSKyle Evans-- 1. Redistributions of source code must retain the above copyright
11*020d003cSKyle Evans--    notice, this list of conditions and the following disclaimer.
12*020d003cSKyle Evans-- 2. Redistributions in binary form must reproduce the above copyright
13*020d003cSKyle Evans--    notice, this list of conditions and the following disclaimer in the
14*020d003cSKyle Evans--    documentation and/or other materials provided with the distribution.
15*020d003cSKyle Evans--
16*020d003cSKyle Evans-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*020d003cSKyle Evans-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*020d003cSKyle Evans-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*020d003cSKyle Evans-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*020d003cSKyle Evans-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*020d003cSKyle Evans-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*020d003cSKyle Evans-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*020d003cSKyle Evans-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*020d003cSKyle Evans-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*020d003cSKyle Evans-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*020d003cSKyle Evans-- SUCH DAMAGE.
27*020d003cSKyle Evans--
28*020d003cSKyle Evans
29*020d003cSKyle Evans-- THEORY OF OPERATION
30*020d003cSKyle Evans--
31*020d003cSKyle Evans-- generate-fortify-tests.lua is intended to test fortified functions as found
32*020d003cSKyle Evans-- mostly in the various headers in /usr/include/ssp.  Each fortified function
33*020d003cSKyle Evans-- gets three basic tests:
34*020d003cSKyle Evans--
35*020d003cSKyle Evans--   1. Write just before the end of the buffer,
36*020d003cSKyle Evans--   2. Write right at the end of the buffer,
37*020d003cSKyle Evans--   3. Write just after the end of the buffer.
38*020d003cSKyle Evans--
39*020d003cSKyle Evans-- Each test is actually generated twice: once with a buffer on the stack, and
40*020d003cSKyle Evans-- again with a buffer on the heap, to confirm that __builtin_object_size(3) can
41*020d003cSKyle Evans-- deduce the buffer size in both scenarios.  The tests work by setting up the
42*020d003cSKyle Evans-- stack with our buffer (and some padding on either side to avoid tripping any
43*020d003cSKyle Evans-- other stack or memory protection), doing any initialization as described by
44*020d003cSKyle Evans-- the test definition, then calling the fortified function with the buffer as
45*020d003cSKyle Evans-- outlined by the test definition.
46*020d003cSKyle Evans--
47*020d003cSKyle Evans-- For the 'before' and 'at' the end tests, we're ensuring that valid writes
48*020d003cSKyle Evans-- that are on the verge of being invalid aren't accidentally being detected as
49*020d003cSKyle Evans-- invalid.
50*020d003cSKyle Evans--
51*020d003cSKyle Evans-- The 'after' test is the one that actually tests the functional benefit of
52*020d003cSKyle Evans-- _FORTIFY_SOURCE by violating a boundary that should trigger an abort.  As
53*020d003cSKyle Evans-- such, this test differs more from the other two in that it has to fork() off
54*020d003cSKyle Evans-- the fortified function call so that we can monitor for a SIGABRT and
55*020d003cSKyle Evans-- pass/fail the test at function end appropriately.
56*020d003cSKyle Evans
57*020d003cSKyle Evans-- Some tests, like the FD_*() macros, may define these differently.  For
58*020d003cSKyle Evans-- instance, for fd sets we're varying the index we pass and not using arbitrary
59*020d003cSKyle Evans-- buffers.  Other tests that don't use the length in any way may physically
60*020d003cSKyle Evans-- vary the buffer size for each test case when we'd typically vary the length
61*020d003cSKyle Evans-- we're requesting a write for.
62*020d003cSKyle Evans
63*020d003cSKyle Evanslocal includes = {
64*020d003cSKyle Evans	"sys/param.h",
65*020d003cSKyle Evans	"sys/resource.h",
66*020d003cSKyle Evans	"sys/time.h",
67*020d003cSKyle Evans	"sys/wait.h",
68*020d003cSKyle Evans	"dirent.h",
69*020d003cSKyle Evans	"errno.h",
70*020d003cSKyle Evans	"fcntl.h",
71*020d003cSKyle Evans	"limits.h",
72*020d003cSKyle Evans	"signal.h",
73*020d003cSKyle Evans	"stdio.h",
74*020d003cSKyle Evans	"stdlib.h",
75*020d003cSKyle Evans	"string.h",
76*020d003cSKyle Evans	"strings.h",
77*020d003cSKyle Evans	"sysexits.h",
78*020d003cSKyle Evans	"unistd.h",
79*020d003cSKyle Evans	"atf-c.h",
80*020d003cSKyle Evans}
81*020d003cSKyle Evans
82*020d003cSKyle Evanslocal tests_added = {}
83*020d003cSKyle Evans
84*020d003cSKyle Evans-- Some of these will need to be excluded because clang sees the wrong size when
85*020d003cSKyle Evans-- an array is embedded inside a struct, we'll get something that looks more
86*020d003cSKyle Evans-- like __builtin_object_size(ptr, 0) than it does the correct
87*020d003cSKyle Evans-- __builtin_object_size(ptr, 1) (i.e., includes the padding after).  This is
88*020d003cSKyle Evans-- almost certainly a bug in llvm.
89*020d003cSKyle Evanslocal function excludes_stack_overflow(disposition, is_heap)
90*020d003cSKyle Evans	return (not is_heap) and disposition > 0
91*020d003cSKyle Evansend
92*020d003cSKyle Evans
93*020d003cSKyle Evanslocal printf_stackvars = "\tchar srcvar[__len + 10];\n"
94*020d003cSKyle Evanslocal printf_init = [[
95*020d003cSKyle Evans	memset(srcvar, 'A', sizeof(srcvar) - 1);
96*020d003cSKyle Evans	srcvar[sizeof(srcvar) - 1] = '\0';
97*020d003cSKyle Evans]]
98*020d003cSKyle Evans
99*020d003cSKyle Evanslocal string_stackvars = "\tchar src[__len];\n"
100*020d003cSKyle Evanslocal string_init = [[
101*020d003cSKyle Evans	memset(__stack.__buf, 0, __len);
102*020d003cSKyle Evans	memset(src, 'A', __len - 1);
103*020d003cSKyle Evans	src[__len - 1] = '\0';
104*020d003cSKyle Evans]]
105*020d003cSKyle Evans
106*020d003cSKyle Evans-- Each test entry describes how to test a given function.  We need to know how
107*020d003cSKyle Evans-- to construct the buffer, we need to know the argument set we're dealing with,
108*020d003cSKyle Evans-- and we need to know what we're passing to each argument.  We could be passing
109*020d003cSKyle Evans-- fixed values, or we could be passing the __buf under test.
110*020d003cSKyle Evans--
111*020d003cSKyle Evans-- definition:
112*020d003cSKyle Evans--   func: name of the function under test to call
113*020d003cSKyle Evans--   bufsize: size of buffer to generate, defaults to 42
114*020d003cSKyle Evans--   buftype: type of buffer to generate, defaults to unsigned char[]
115*020d003cSKyle Evans--   arguments: __buf, __len, or the name of a variable placed on the stack
116*020d003cSKyle Evans--   exclude: a function(disposition, is_heap) that returns true if this combo
117*020d003cSKyle Evans--     should be excluded.
118*020d003cSKyle Evans--   stackvars: extra variables to be placed on the stack, should be a string
119*020d003cSKyle Evans--     optionally formatted with tabs and newlines
120*020d003cSKyle Evans--   init: extra code to inject just before the function call for initialization
121*020d003cSKyle Evans--     of the buffer or any of the above-added stackvars; also a string
122*020d003cSKyle Evans--   uses_len: bool-ish, necessary if arguments doesn't include either __idx or
123*020d003cSKyle Evans--     or __len so that the test generator doesn't try to vary the size of the
124*020d003cSKyle Evans--     buffer instead of just manipulating __idx/__len to try and induce an
125*020d003cSKyle Evans--     overflow.
126*020d003cSKyle Evans--
127*020d003cSKyle Evans-- Most tests will just use the default bufsize/buftype, but under some
128*020d003cSKyle Evans-- circumstances it's useful to use a different type (e.g., for alignment
129*020d003cSKyle Evans-- requirements).
130*020d003cSKyle Evanslocal all_tests = {
131*020d003cSKyle Evans	stdio = {
132*020d003cSKyle Evans		-- <stdio.h>
133*020d003cSKyle Evans		{
134*020d003cSKyle Evans			func = "sprintf",
135*020d003cSKyle Evans			arguments = {
136*020d003cSKyle Evans				"__buf",
137*020d003cSKyle Evans				"\"%.*s\"",
138*020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
139*020d003cSKyle Evans				"srcvar",
140*020d003cSKyle Evans			},
141*020d003cSKyle Evans			exclude = excludes_stack_overflow,
142*020d003cSKyle Evans			stackvars = printf_stackvars,
143*020d003cSKyle Evans			init = printf_init,
144*020d003cSKyle Evans		},
145*020d003cSKyle Evans		{
146*020d003cSKyle Evans			func = "snprintf",
147*020d003cSKyle Evans			arguments = {
148*020d003cSKyle Evans				"__buf",
149*020d003cSKyle Evans				"__len",
150*020d003cSKyle Evans				"\"%.*s\"",
151*020d003cSKyle Evans				"(int)__len - 1",	-- - 1 for NUL terminator
152*020d003cSKyle Evans				"srcvar",
153*020d003cSKyle Evans			},
154*020d003cSKyle Evans			exclude = excludes_stack_overflow,
155*020d003cSKyle Evans			stackvars = printf_stackvars,
156*020d003cSKyle Evans			init = printf_init,
157*020d003cSKyle Evans		},
158*020d003cSKyle Evans	},
159*020d003cSKyle Evans	string = {
160*020d003cSKyle Evans		-- <string.h>
161*020d003cSKyle Evans		{
162*020d003cSKyle Evans			func = "memcpy",
163*020d003cSKyle Evans			arguments = {
164*020d003cSKyle Evans				"__buf",
165*020d003cSKyle Evans				"src",
166*020d003cSKyle Evans				"__len",
167*020d003cSKyle Evans			},
168*020d003cSKyle Evans			exclude = excludes_stack_overflow,
169*020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
170*020d003cSKyle Evans		},
171*020d003cSKyle Evans		{
172*020d003cSKyle Evans			func = "memmove",
173*020d003cSKyle Evans			arguments = {
174*020d003cSKyle Evans				"__buf",
175*020d003cSKyle Evans				"src",
176*020d003cSKyle Evans				"__len",
177*020d003cSKyle Evans			},
178*020d003cSKyle Evans			exclude = excludes_stack_overflow,
179*020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
180*020d003cSKyle Evans		},
181*020d003cSKyle Evans		{
182*020d003cSKyle Evans			func = "memset",
183*020d003cSKyle Evans			arguments = {
184*020d003cSKyle Evans				"__buf",
185*020d003cSKyle Evans				"0",
186*020d003cSKyle Evans				"__len",
187*020d003cSKyle Evans			},
188*020d003cSKyle Evans			exclude = excludes_stack_overflow,
189*020d003cSKyle Evans		},
190*020d003cSKyle Evans		{
191*020d003cSKyle Evans			func = "stpcpy",
192*020d003cSKyle Evans			arguments = {
193*020d003cSKyle Evans				"__buf",
194*020d003cSKyle Evans				"src",
195*020d003cSKyle Evans			},
196*020d003cSKyle Evans			exclude = excludes_stack_overflow,
197*020d003cSKyle Evans			stackvars = string_stackvars,
198*020d003cSKyle Evans			init = string_init,
199*020d003cSKyle Evans			uses_len = true,
200*020d003cSKyle Evans		},
201*020d003cSKyle Evans		{
202*020d003cSKyle Evans			func = "stpncpy",
203*020d003cSKyle Evans			arguments = {
204*020d003cSKyle Evans				"__buf",
205*020d003cSKyle Evans				"src",
206*020d003cSKyle Evans				"__len",
207*020d003cSKyle Evans			},
208*020d003cSKyle Evans			exclude = excludes_stack_overflow,
209*020d003cSKyle Evans			stackvars = string_stackvars,
210*020d003cSKyle Evans			init = string_init,
211*020d003cSKyle Evans		},
212*020d003cSKyle Evans		{
213*020d003cSKyle Evans			func = "strcat",
214*020d003cSKyle Evans			arguments = {
215*020d003cSKyle Evans				"__buf",
216*020d003cSKyle Evans				"src",
217*020d003cSKyle Evans			},
218*020d003cSKyle Evans			exclude = excludes_stack_overflow,
219*020d003cSKyle Evans			stackvars = string_stackvars,
220*020d003cSKyle Evans			init = string_init,
221*020d003cSKyle Evans			uses_len = true,
222*020d003cSKyle Evans		},
223*020d003cSKyle Evans		{
224*020d003cSKyle Evans			func = "strncat",
225*020d003cSKyle Evans			arguments = {
226*020d003cSKyle Evans				"__buf",
227*020d003cSKyle Evans				"src",
228*020d003cSKyle Evans				"__len",
229*020d003cSKyle Evans			},
230*020d003cSKyle Evans			exclude = excludes_stack_overflow,
231*020d003cSKyle Evans			stackvars = string_stackvars,
232*020d003cSKyle Evans			init = string_init,
233*020d003cSKyle Evans		},
234*020d003cSKyle Evans		{
235*020d003cSKyle Evans			func = "strcpy",
236*020d003cSKyle Evans			arguments = {
237*020d003cSKyle Evans				"__buf",
238*020d003cSKyle Evans				"src",
239*020d003cSKyle Evans			},
240*020d003cSKyle Evans			exclude = excludes_stack_overflow,
241*020d003cSKyle Evans			stackvars = string_stackvars,
242*020d003cSKyle Evans			init = string_init,
243*020d003cSKyle Evans			uses_len = true,
244*020d003cSKyle Evans		},
245*020d003cSKyle Evans		{
246*020d003cSKyle Evans			func = "strncpy",
247*020d003cSKyle Evans			arguments = {
248*020d003cSKyle Evans				"__buf",
249*020d003cSKyle Evans				"src",
250*020d003cSKyle Evans				"__len",
251*020d003cSKyle Evans			},
252*020d003cSKyle Evans			exclude = excludes_stack_overflow,
253*020d003cSKyle Evans			stackvars = string_stackvars,
254*020d003cSKyle Evans			init = string_init,
255*020d003cSKyle Evans		},
256*020d003cSKyle Evans	},
257*020d003cSKyle Evans	strings = {
258*020d003cSKyle Evans		-- <strings.h>
259*020d003cSKyle Evans		{
260*020d003cSKyle Evans			func = "bcopy",
261*020d003cSKyle Evans			arguments = {
262*020d003cSKyle Evans				"src",
263*020d003cSKyle Evans				"__buf",
264*020d003cSKyle Evans				"__len",
265*020d003cSKyle Evans			},
266*020d003cSKyle Evans			exclude = excludes_stack_overflow,
267*020d003cSKyle Evans			stackvars = "\tchar src[__len + 10];\n",
268*020d003cSKyle Evans		},
269*020d003cSKyle Evans		{
270*020d003cSKyle Evans			func = "bzero",
271*020d003cSKyle Evans			arguments = {
272*020d003cSKyle Evans				"__buf",
273*020d003cSKyle Evans				"__len",
274*020d003cSKyle Evans			},
275*020d003cSKyle Evans			exclude = excludes_stack_overflow,
276*020d003cSKyle Evans		},
277*020d003cSKyle Evans	},
278*020d003cSKyle Evans	unistd = {
279*020d003cSKyle Evans		-- <unistd.h>
280*020d003cSKyle Evans		{
281*020d003cSKyle Evans			func = "getcwd",
282*020d003cSKyle Evans			bufsize = "8",
283*020d003cSKyle Evans			arguments = {
284*020d003cSKyle Evans				"__buf",
285*020d003cSKyle Evans				"__len",
286*020d003cSKyle Evans			},
287*020d003cSKyle Evans			exclude = excludes_stack_overflow,
288*020d003cSKyle Evans		},
289*020d003cSKyle Evans		{
290*020d003cSKyle Evans			func = "read",
291*020d003cSKyle Evans			bufsize = "41",
292*020d003cSKyle Evans			arguments = {
293*020d003cSKyle Evans				"fd",
294*020d003cSKyle Evans				"__buf",
295*020d003cSKyle Evans				"__len",
296*020d003cSKyle Evans			},
297*020d003cSKyle Evans			exclude = excludes_stack_overflow,
298*020d003cSKyle Evans			stackvars = "\tint fd;\n",
299*020d003cSKyle Evans			init = [[
300*020d003cSKyle Evans	fd = new_tmpfile();	/* Cannot fail */
301*020d003cSKyle Evans]],
302*020d003cSKyle Evans		},
303*020d003cSKyle Evans		{
304*020d003cSKyle Evans			func = "readlink",
305*020d003cSKyle Evans			arguments = {
306*020d003cSKyle Evans				"path",
307*020d003cSKyle Evans				"__buf",
308*020d003cSKyle Evans				"__len",
309*020d003cSKyle Evans			},
310*020d003cSKyle Evans			exclude = excludes_stack_overflow,
311*020d003cSKyle Evans			stackvars = "\tconst char *path;\n",
312*020d003cSKyle Evans			init = [[
313*020d003cSKyle Evans	path = new_symlink(__len);		/* Cannot fail */
314*020d003cSKyle Evans]],
315*020d003cSKyle Evans		},
316*020d003cSKyle Evans	},
317*020d003cSKyle Evans}
318*020d003cSKyle Evans
319*020d003cSKyle Evanslocal function write_test_boilerplate(fh, name, body)
320*020d003cSKyle Evans	fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
321*020d003cSKyle Evans	fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
322*020d003cSKyle Evans	fh:write("{\n" .. body .. "\n}\n\n")
323*020d003cSKyle Evans	return name
324*020d003cSKyle Evansend
325*020d003cSKyle Evans
326*020d003cSKyle Evanslocal function generate_test_name(func, variant, disposition, heap)
327*020d003cSKyle Evans	local basename = func
328*020d003cSKyle Evans	if variant then
329*020d003cSKyle Evans		basename = basename .. "_" .. variant
330*020d003cSKyle Evans	end
331*020d003cSKyle Evans	if heap then
332*020d003cSKyle Evans		basename = basename .. "_heap"
333*020d003cSKyle Evans	end
334*020d003cSKyle Evans	if disposition < 0 then
335*020d003cSKyle Evans		return basename .. "_before_end"
336*020d003cSKyle Evans	elseif disposition == 0 then
337*020d003cSKyle Evans		return basename .. "_end"
338*020d003cSKyle Evans	else
339*020d003cSKyle Evans		return basename .. "_after_end"
340*020d003cSKyle Evans	end
341*020d003cSKyle Evansend
342*020d003cSKyle Evans
343*020d003cSKyle Evanslocal function array_type(buftype)
344*020d003cSKyle Evans	if not buftype:match("%[%]") then
345*020d003cSKyle Evans		return nil
346*020d003cSKyle Evans	end
347*020d003cSKyle Evans
348*020d003cSKyle Evans	return buftype:gsub("%[%]", "")
349*020d003cSKyle Evansend
350*020d003cSKyle Evans
351*020d003cSKyle Evanslocal function configurable(def, idx)
352*020d003cSKyle Evans	local cfgitem = def[idx]
353*020d003cSKyle Evans
354*020d003cSKyle Evans	if not cfgitem then
355*020d003cSKyle Evans		return nil
356*020d003cSKyle Evans	end
357*020d003cSKyle Evans
358*020d003cSKyle Evans	if type(cfgitem) == "function" then
359*020d003cSKyle Evans		return cfgitem()
360*020d003cSKyle Evans	end
361*020d003cSKyle Evans
362*020d003cSKyle Evans	return cfgitem
363*020d003cSKyle Evansend
364*020d003cSKyle Evans
365*020d003cSKyle Evanslocal function generate_stackframe(buftype, bufsize, disposition, heap, def)
366*020d003cSKyle Evans	local function len_offset(inverted, disposition)
367*020d003cSKyle Evans		-- Tests that don't use __len in their arguments may use an
368*020d003cSKyle Evans		-- inverted sense because we can't just specify a length that
369*020d003cSKyle Evans		-- would induce an access just after the end.  Instead, we have
370*020d003cSKyle Evans		-- to manipulate the buffer size to be too short so that the
371*020d003cSKyle Evans		-- function under test would write one too many.
372*020d003cSKyle Evans		if disposition < 0 then
373*020d003cSKyle Evans			return ((inverted and " + ") or " - ") .. "1"
374*020d003cSKyle Evans		elseif disposition == 0 then
375*020d003cSKyle Evans			return ""
376*020d003cSKyle Evans		else
377*020d003cSKyle Evans			return ((inverted and " - ") or " + ") .. "1"
378*020d003cSKyle Evans		end
379*020d003cSKyle Evans	end
380*020d003cSKyle Evans
381*020d003cSKyle Evans	local function test_uses_len(def)
382*020d003cSKyle Evans		if def.uses_len then
383*020d003cSKyle Evans			return true
384*020d003cSKyle Evans		end
385*020d003cSKyle Evans
386*020d003cSKyle Evans		for _, arg in ipairs(def.arguments) do
387*020d003cSKyle Evans			if arg:match("__len") or arg:match("__idx") then
388*020d003cSKyle Evans				return true
389*020d003cSKyle Evans			end
390*020d003cSKyle Evans		end
391*020d003cSKyle Evans
392*020d003cSKyle Evans		return false
393*020d003cSKyle Evans	end
394*020d003cSKyle Evans
395*020d003cSKyle Evans
396*020d003cSKyle Evans	-- This is perhaps a little convoluted, but we toss the buffer into a
397*020d003cSKyle Evans	-- struct on the stack to guarantee that we have at least one valid
398*020d003cSKyle Evans	-- byte on either side of the buffer -- a measure to make sure that
399*020d003cSKyle Evans	-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
400*020d003cSKyle Evans	-- rather than some other stack or memory protection.
401*020d003cSKyle Evans	local vars = "\tstruct {\n"
402*020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_l;\n"
403*020d003cSKyle Evans
404*020d003cSKyle Evans	local uses_len = test_uses_len(def)
405*020d003cSKyle Evans	local bufsize_offset = len_offset(not uses_len, disposition)
406*020d003cSKyle Evans	local buftype_elem = array_type(buftype)
407*020d003cSKyle Evans	local size_expr = bufsize
408*020d003cSKyle Evans
409*020d003cSKyle Evans	if not uses_len then
410*020d003cSKyle Evans		-- If the length isn't in use, we have to vary the buffer size
411*020d003cSKyle Evans		-- since the fortified function likely has some internal size
412*020d003cSKyle Evans		-- constraint that it's supposed to be checking.
413*020d003cSKyle Evans		size_expr = size_expr .. bufsize_offset
414*020d003cSKyle Evans	end
415*020d003cSKyle Evans
416*020d003cSKyle Evans	if not heap and buftype_elem then
417*020d003cSKyle Evans		-- Array type: size goes after identifier
418*020d003cSKyle Evans		vars = vars .. "\t\t" .. buftype_elem ..
419*020d003cSKyle Evans		    " __buf[" .. size_expr .. "];\n"
420*020d003cSKyle Evans	else
421*020d003cSKyle Evans		local basic_type = buftype_elem or buftype
422*020d003cSKyle Evans
423*020d003cSKyle Evans		-- Heap tests obviously just put a pointer on the stack that
424*020d003cSKyle Evans		-- points to our new allocation, but we leave it in the padded
425*020d003cSKyle Evans		-- struct just to simplify our generator.
426*020d003cSKyle Evans		if heap then
427*020d003cSKyle Evans			basic_type = basic_type .. " *"
428*020d003cSKyle Evans		end
429*020d003cSKyle Evans		vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
430*020d003cSKyle Evans	end
431*020d003cSKyle Evans
432*020d003cSKyle Evans	-- padding_r is our just-past-the-end padding that we use to make sure
433*020d003cSKyle Evans	-- that there's a valid portion after the buffer that isn't being
434*020d003cSKyle Evans	-- included in our function calls.  If we didn't have it, then we'd have
435*020d003cSKyle Evans	-- a hard time feeling confident that an abort on the just-after tests
436*020d003cSKyle Evans	-- isn't maybe from some other memory or stack protection.
437*020d003cSKyle Evans	vars = vars .. "\t\tuint8_t padding_r;\n"
438*020d003cSKyle Evans	vars = vars .. "\t} __stack;\n"
439*020d003cSKyle Evans
440*020d003cSKyle Evans	-- Not all tests will use __bufsz, but some do for, e.g., clearing
441*020d003cSKyle Evans	-- memory..
442*020d003cSKyle Evans	vars = vars .. "\tconst size_t __bufsz __unused = "
443*020d003cSKyle Evans	if heap then
444*020d003cSKyle Evans		local scalar = 1
445*020d003cSKyle Evans		if buftype_elem then
446*020d003cSKyle Evans			scalar = size_expr
447*020d003cSKyle Evans		end
448*020d003cSKyle Evans
449*020d003cSKyle Evans		vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
450*020d003cSKyle Evans	else
451*020d003cSKyle Evans		vars = vars .. "sizeof(__stack.__buf);\n"
452*020d003cSKyle Evans	end
453*020d003cSKyle Evans
454*020d003cSKyle Evans	vars = vars .. "\tconst size_t __len = " .. bufsize ..
455*020d003cSKyle Evans	    bufsize_offset .. ";\n"
456*020d003cSKyle Evans	vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
457*020d003cSKyle Evans
458*020d003cSKyle Evans	-- For overflow testing, we need to fork() because we're expecting the
459*020d003cSKyle Evans	-- test to ultimately abort()/_exit().  Then we can collect the exit
460*020d003cSKyle Evans	-- status and report appropriately.
461*020d003cSKyle Evans	if disposition > 0 then
462*020d003cSKyle Evans		vars = vars .. "\tpid_t __child;\n"
463*020d003cSKyle Evans		vars = vars .. "\tint __status;\n"
464*020d003cSKyle Evans	end
465*020d003cSKyle Evans
466*020d003cSKyle Evans	-- Any other stackvars defined by the test get placed after everything
467*020d003cSKyle Evans	-- else.
468*020d003cSKyle Evans	vars = vars .. (configurable(def, "stackvars") or "")
469*020d003cSKyle Evans
470*020d003cSKyle Evans	return vars
471*020d003cSKyle Evansend
472*020d003cSKyle Evans
473*020d003cSKyle Evanslocal function write_test(fh, func, disposition, heap, def)
474*020d003cSKyle Evans	local testname = generate_test_name(func, def.variant, disposition, heap)
475*020d003cSKyle Evans	local buftype = def.buftype or "unsigned char[]"
476*020d003cSKyle Evans	local bufsize = def.bufsize or 42
477*020d003cSKyle Evans	local body = ""
478*020d003cSKyle Evans
479*020d003cSKyle Evans	if def.exclude and def.exclude(disposition, heap) then
480*020d003cSKyle Evans		return
481*020d003cSKyle Evans	end
482*020d003cSKyle Evans
483*020d003cSKyle Evans	local function need_addr(buftype)
484*020d003cSKyle Evans		return not (buftype:match("%[%]") or buftype:match("%*"))
485*020d003cSKyle Evans	end
486*020d003cSKyle Evans
487*020d003cSKyle Evans	if heap then
488*020d003cSKyle Evans		body = body .. "#define BUF __stack.__buf\n"
489*020d003cSKyle Evans	else
490*020d003cSKyle Evans		body = body .. "#define BUF &__stack.__buf\n"
491*020d003cSKyle Evans	end
492*020d003cSKyle Evans
493*020d003cSKyle Evans	-- Setup the buffer
494*020d003cSKyle Evans	body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
495*020d003cSKyle Evans	    "\n"
496*020d003cSKyle Evans
497*020d003cSKyle Evans	-- Any early initialization goes before we would fork for the just-after
498*020d003cSKyle Evans	-- tests, because they may want to skip the test based on some criteria
499*020d003cSKyle Evans	-- and we can't propagate that up very easily once we're forked.
500*020d003cSKyle Evans	local early_init = configurable(def, "early_init")
501*020d003cSKyle Evans	body = body .. (early_init or "")
502*020d003cSKyle Evans	if early_init then
503*020d003cSKyle Evans		body = body .. "\n"
504*020d003cSKyle Evans	end
505*020d003cSKyle Evans
506*020d003cSKyle Evans	-- Fork off, iff we're testing some access past the end of the buffer.
507*020d003cSKyle Evans	if disposition > 0 then
508*020d003cSKyle Evans		body = body .. [[
509*020d003cSKyle Evans	__child = fork();
510*020d003cSKyle Evans	ATF_REQUIRE(__child >= 0);
511*020d003cSKyle Evans	if (__child > 0)
512*020d003cSKyle Evans		goto monitor;
513*020d003cSKyle Evans
514*020d003cSKyle Evans	/* Child */
515*020d003cSKyle Evans	disable_coredumps();
516*020d003cSKyle Evans]]
517*020d003cSKyle Evans	end
518*020d003cSKyle Evans
519*020d003cSKyle Evans	local bufvar = "__stack.__buf"
520*020d003cSKyle Evans	if heap then
521*020d003cSKyle Evans		-- Buffer needs to be initialized because it's a heap allocation.
522*020d003cSKyle Evans		body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
523*020d003cSKyle Evans	end
524*020d003cSKyle Evans
525*020d003cSKyle Evans	-- Non-early init happens just after the fork in the child, not the
526*020d003cSKyle Evans	-- monitor.  This is used to setup any other buffers we may need, for
527*020d003cSKyle Evans	-- instance.
528*020d003cSKyle Evans	local extra_init = configurable(def, "init")
529*020d003cSKyle Evans	body = body .. (extra_init or "")
530*020d003cSKyle Evans
531*020d003cSKyle Evans	if heap or extra_init then
532*020d003cSKyle Evans		body = body .. "\n"
533*020d003cSKyle Evans	end
534*020d003cSKyle Evans
535*020d003cSKyle Evans	-- Setup the function call with arguments as described in the test
536*020d003cSKyle Evans	-- definition.
537*020d003cSKyle Evans	body = body .. "\t" .. func .. "("
538*020d003cSKyle Evans
539*020d003cSKyle Evans	for idx, arg in ipairs(def.arguments) do
540*020d003cSKyle Evans		if idx > 1 then
541*020d003cSKyle Evans			body = body .. ", "
542*020d003cSKyle Evans		end
543*020d003cSKyle Evans
544*020d003cSKyle Evans		if arg == "__buf" then
545*020d003cSKyle Evans			if not heap and need_addr(buftype) then
546*020d003cSKyle Evans				body = body .. "&"
547*020d003cSKyle Evans			end
548*020d003cSKyle Evans
549*020d003cSKyle Evans			body = body .. bufvar
550*020d003cSKyle Evans		else
551*020d003cSKyle Evans			local argname = arg
552*020d003cSKyle Evans
553*020d003cSKyle Evans			if def.value_of then
554*020d003cSKyle Evans				argname = argname or def.value_of(arg)
555*020d003cSKyle Evans			end
556*020d003cSKyle Evans
557*020d003cSKyle Evans			body = body .. argname
558*020d003cSKyle Evans		end
559*020d003cSKyle Evans	end
560*020d003cSKyle Evans
561*020d003cSKyle Evans	body = body .. ");\n"
562*020d003cSKyle Evans
563*020d003cSKyle Evans	-- Monitor stuff follows, for OOB access.
564*020d003cSKyle Evans	if disposition <= 0 then
565*020d003cSKyle Evans		goto skip
566*020d003cSKyle Evans	end
567*020d003cSKyle Evans
568*020d003cSKyle Evans	body = body .. [[
569*020d003cSKyle Evans	_exit(EX_SOFTWARE);	/* Should have aborted. */
570*020d003cSKyle Evans
571*020d003cSKyle Evansmonitor:
572*020d003cSKyle Evans	while (waitpid(__child, &__status, 0) != __child) {
573*020d003cSKyle Evans		ATF_REQUIRE_EQ(EINTR, errno);
574*020d003cSKyle Evans	}
575*020d003cSKyle Evans
576*020d003cSKyle Evans	if (!WIFSIGNALED(__status)) {
577*020d003cSKyle Evans		switch (WEXITSTATUS(__status)) {
578*020d003cSKyle Evans		case EX_SOFTWARE:
579*020d003cSKyle Evans			atf_tc_fail("FORTIFY_SOURCE failed to abort");
580*020d003cSKyle Evans			break;
581*020d003cSKyle Evans		case EX_OSERR:
582*020d003cSKyle Evans			atf_tc_fail("setrlimit(2) failed");
583*020d003cSKyle Evans			break;
584*020d003cSKyle Evans		default:
585*020d003cSKyle Evans			atf_tc_fail("child exited with status %d",
586*020d003cSKyle Evans			    WEXITSTATUS(__status));
587*020d003cSKyle Evans		}
588*020d003cSKyle Evans	} else {
589*020d003cSKyle Evans		ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
590*020d003cSKyle Evans	}
591*020d003cSKyle Evans]]
592*020d003cSKyle Evans
593*020d003cSKyle Evans::skip::
594*020d003cSKyle Evans	body = body .. "#undef BUF\n"
595*020d003cSKyle Evans	return write_test_boilerplate(fh, testname, body)
596*020d003cSKyle Evansend
597*020d003cSKyle Evans
598*020d003cSKyle Evans-- main()
599*020d003cSKyle Evanslocal tests
600*020d003cSKyle Evanslocal tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
601*020d003cSKyle Evansfor k, defs in pairs(all_tests) do
602*020d003cSKyle Evans	if k == tcat then
603*020d003cSKyle Evans		tests = defs
604*020d003cSKyle Evans		break
605*020d003cSKyle Evans	end
606*020d003cSKyle Evansend
607*020d003cSKyle Evans
608*020d003cSKyle Evansassert(tests, "category " .. tcat .. " not found")
609*020d003cSKyle Evans
610*020d003cSKyle Evanslocal fh = io.stdout
611*020d003cSKyle Evansfh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
612*020d003cSKyle Evans    tcat .. "\"` */\n\n")
613*020d003cSKyle Evansfh:write("#define	_FORTIFY_SOURCE	2\n")
614*020d003cSKyle Evansfh:write("#define	TMPFILE_SIZE	(1024 * 32)\n")
615*020d003cSKyle Evans
616*020d003cSKyle Evansfh:write("\n")
617*020d003cSKyle Evansfor _, inc in ipairs(includes) do
618*020d003cSKyle Evans	fh:write("#include <" .. inc .. ">\n")
619*020d003cSKyle Evansend
620*020d003cSKyle Evans
621*020d003cSKyle Evansfh:write([[
622*020d003cSKyle Evans
623*020d003cSKyle Evans/*
624*020d003cSKyle Evans * Create a new symlink to use for readlink(2) style tests, we'll just use a
625*020d003cSKyle Evans * random target name to have something interesting to look at.
626*020d003cSKyle Evans */
627*020d003cSKyle Evansstatic const char * __unused
628*020d003cSKyle Evansnew_symlink(size_t __len)
629*020d003cSKyle Evans{
630*020d003cSKyle Evans	static const char linkname[] = "link";
631*020d003cSKyle Evans	char target[MAXNAMLEN];
632*020d003cSKyle Evans	int error;
633*020d003cSKyle Evans
634*020d003cSKyle Evans	ATF_REQUIRE(__len <= sizeof(target));
635*020d003cSKyle Evans
636*020d003cSKyle Evans	arc4random_buf(target, sizeof(target));
637*020d003cSKyle Evans
638*020d003cSKyle Evans	error = unlink(linkname);
639*020d003cSKyle Evans	ATF_REQUIRE(error == 0 || errno == ENOENT);
640*020d003cSKyle Evans
641*020d003cSKyle Evans	error = symlink(target, linkname);
642*020d003cSKyle Evans	ATF_REQUIRE(error == 0);
643*020d003cSKyle Evans
644*020d003cSKyle Evans	return (linkname);
645*020d003cSKyle Evans}
646*020d003cSKyle Evans
647*020d003cSKyle Evans/*
648*020d003cSKyle Evans * Constructs a tmpfile that we can use for testing read(2) and friends.
649*020d003cSKyle Evans */
650*020d003cSKyle Evansstatic int __unused
651*020d003cSKyle Evansnew_tmpfile(void)
652*020d003cSKyle Evans{
653*020d003cSKyle Evans	char buf[1024];
654*020d003cSKyle Evans	ssize_t rv;
655*020d003cSKyle Evans	size_t written;
656*020d003cSKyle Evans	int fd;
657*020d003cSKyle Evans
658*020d003cSKyle Evans	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
659*020d003cSKyle Evans	ATF_REQUIRE(fd >= 0);
660*020d003cSKyle Evans
661*020d003cSKyle Evans	written = 0;
662*020d003cSKyle Evans	while (written < TMPFILE_SIZE) {
663*020d003cSKyle Evans		rv = write(fd, buf, sizeof(buf));
664*020d003cSKyle Evans		ATF_REQUIRE(rv > 0);
665*020d003cSKyle Evans
666*020d003cSKyle Evans		written += rv;
667*020d003cSKyle Evans	}
668*020d003cSKyle Evans
669*020d003cSKyle Evans	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
670*020d003cSKyle Evans	return (fd);
671*020d003cSKyle Evans}
672*020d003cSKyle Evans
673*020d003cSKyle Evansstatic void
674*020d003cSKyle Evansdisable_coredumps(void)
675*020d003cSKyle Evans{
676*020d003cSKyle Evans	struct rlimit rl = { 0 };
677*020d003cSKyle Evans
678*020d003cSKyle Evans	if (setrlimit(RLIMIT_CORE, &rl) == -1)
679*020d003cSKyle Evans		_exit(EX_OSERR);
680*020d003cSKyle Evans}
681*020d003cSKyle Evans
682*020d003cSKyle Evans]])
683*020d003cSKyle Evans
684*020d003cSKyle Evansfor _, def in pairs(tests) do
685*020d003cSKyle Evans	local func = def.func
686*020d003cSKyle Evans	local function write_tests(heap)
687*020d003cSKyle Evans		-- Dispositions here are relative to the buffer size prescribed
688*020d003cSKyle Evans		-- by the test definition.
689*020d003cSKyle Evans		local dispositions = def.dispositions or { -1, 0, 1 }
690*020d003cSKyle Evans
691*020d003cSKyle Evans		for _, disposition in ipairs(dispositions) do
692*020d003cSKyle Evans			tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
693*020d003cSKyle Evans		end
694*020d003cSKyle Evans	end
695*020d003cSKyle Evans
696*020d003cSKyle Evans	write_tests(false)
697*020d003cSKyle Evans	write_tests(true)
698*020d003cSKyle Evansend
699*020d003cSKyle Evans
700*020d003cSKyle Evansfh:write("ATF_TP_ADD_TCS(tp)\n")
701*020d003cSKyle Evansfh:write("{\n")
702*020d003cSKyle Evansfor idx = 1, #tests_added do
703*020d003cSKyle Evans	fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
704*020d003cSKyle Evansend
705*020d003cSKyle Evansfh:write("\treturn (atf_no_error());\n")
706*020d003cSKyle Evansfh:write("}\n")
707