Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/tests/secure/generate-fortify-tests.lua
39553 views
1
#!/usr/libexec/flua
2
--
3
-- SPDX-License-Identifier: BSD-2-Clause
4
--
5
-- Copyright (c) 2024, Klara, Inc.
6
--
7
-- Redistribution and use in source and binary forms, with or without
8
-- modification, are permitted provided that the following conditions
9
-- are met:
10
-- 1. Redistributions of source code must retain the above copyright
11
-- notice, this list of conditions and the following disclaimer.
12
-- 2. Redistributions in binary form must reproduce the above copyright
13
-- notice, this list of conditions and the following disclaimer in the
14
-- documentation and/or other materials provided with the distribution.
15
--
16
-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
-- SUCH DAMAGE.
27
--
28
29
-- THEORY OF OPERATION
30
--
31
-- generate-fortify-tests.lua is intended to test fortified functions as found
32
-- mostly in the various headers in /usr/include/ssp. Each fortified function
33
-- gets three basic tests:
34
--
35
-- 1. Write just before the end of the buffer,
36
-- 2. Write right at the end of the buffer,
37
-- 3. Write just after the end of the buffer.
38
--
39
-- Each test is actually generated twice: once with a buffer on the stack, and
40
-- again with a buffer on the heap, to confirm that __builtin_object_size(3) can
41
-- deduce the buffer size in both scenarios. The tests work by setting up the
42
-- stack with our buffer (and some padding on either side to avoid tripping any
43
-- other stack or memory protection), doing any initialization as described by
44
-- the test definition, then calling the fortified function with the buffer as
45
-- outlined by the test definition.
46
--
47
-- For the 'before' and 'at' the end tests, we're ensuring that valid writes
48
-- that are on the verge of being invalid aren't accidentally being detected as
49
-- invalid.
50
--
51
-- The 'after' test is the one that actually tests the functional benefit of
52
-- _FORTIFY_SOURCE by violating a boundary that should trigger an abort. As
53
-- such, this test differs more from the other two in that it has to fork() off
54
-- the fortified function call so that we can monitor for a SIGABRT and
55
-- pass/fail the test at function end appropriately.
56
57
-- Some tests, like the FD_*() macros, may define these differently. For
58
-- instance, for fd sets we're varying the index we pass and not using arbitrary
59
-- buffers. Other tests that don't use the length in any way may physically
60
-- vary the buffer size for each test case when we'd typically vary the length
61
-- we're requesting a write for.
62
63
local includes = {
64
"sys/param.h",
65
"sys/jail.h",
66
"sys/random.h",
67
"sys/resource.h",
68
"sys/select.h",
69
"sys/socket.h",
70
"sys/time.h",
71
"sys/uio.h",
72
"sys/wait.h",
73
"dirent.h",
74
"errno.h",
75
"fcntl.h",
76
"limits.h",
77
"poll.h",
78
"signal.h",
79
"stdio.h",
80
"stdlib.h",
81
"string.h",
82
"strings.h",
83
"sysexits.h",
84
"unistd.h",
85
"wchar.h",
86
"atf-c.h",
87
}
88
89
local tests_added = {}
90
91
-- Configuration for tests that want the host/domainname
92
local hostname = "host.example.com"
93
local domainname = "example.com"
94
95
-- Some of these will need to be excluded because clang sees the wrong size when
96
-- an array is embedded inside a struct, we'll get something that looks more
97
-- like __builtin_object_size(ptr, 0) than it does the correct
98
-- __builtin_object_size(ptr, 1) (i.e., includes the padding after). This is
99
-- almost certainly a bug in llvm.
100
local function excludes_stack_overflow(disposition, is_heap)
101
return (not is_heap) and disposition > 0
102
end
103
104
local poll_init = [[
105
for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
106
__stack.__buf[i].fd = -1;
107
}
108
]]
109
110
local printf_stackvars = "\tchar srcvar[__len + 10];\n"
111
local printf_init = [[
112
memset(srcvar, 'A', sizeof(srcvar) - 1);
113
srcvar[sizeof(srcvar) - 1] = '\0';
114
]]
115
116
local readv_stackvars = "\tstruct iovec iov[1];\n"
117
local readv_init = [[
118
iov[0].iov_base = __stack.__buf;
119
iov[0].iov_len = __len;
120
121
replace_stdin();
122
]]
123
124
local socket_stackvars = "\tint sock[2] = { -1, -1 };\n"
125
local recvfrom_sockaddr_stackvars = socket_stackvars .. [[
126
char data[16];
127
socklen_t socklen;
128
]]
129
local recvmsg_stackvars = socket_stackvars .. "\tstruct msghdr msg;\n"
130
local socket_init = [[
131
new_socket(sock);
132
]]
133
local socket_socklen_init = socket_init .. [[
134
socklen = __len;
135
]]
136
137
local stdio_init = [[
138
replace_stdin();
139
]]
140
141
local string_stackvars = "\tchar src[__len];\n"
142
local string_init = [[
143
memset(__stack.__buf, 0, __len);
144
memset(src, 'A', __len - 1);
145
src[__len - 1] = '\0';
146
]]
147
148
local wstring_stackvars = "\twchar_t src[__len];\n"
149
local wstring_init = [[
150
wmemset(__stack.__buf, 0, __len);
151
wmemset(src, 'A', __len - 1);
152
src[__len - 1] = '\0';
153
]]
154
155
-- Each test entry describes how to test a given function. We need to know how
156
-- to construct the buffer, we need to know the argument set we're dealing with,
157
-- and we need to know what we're passing to each argument. We could be passing
158
-- fixed values, or we could be passing the __buf under test.
159
--
160
-- definition:
161
-- func: name of the function under test to call
162
-- bufsize: size of buffer to generate, defaults to 42
163
-- buftype: type of buffer to generate, defaults to unsigned char[]
164
-- arguments: __buf, __len, or the name of a variable placed on the stack
165
-- exclude: a function(disposition, is_heap) that returns true if this combo
166
-- should be excluded.
167
-- stackvars: extra variables to be placed on the stack, should be a string
168
-- optionally formatted with tabs and newlines
169
-- init: extra code to inject just before the function call for initialization
170
-- of the buffer or any of the above-added stackvars; also a string
171
-- uses_len: bool-ish, necessary if arguments doesn't include either __idx or
172
-- or __len so that the test generator doesn't try to vary the size of the
173
-- buffer instead of just manipulating __idx/__len to try and induce an
174
-- overflow.
175
--
176
-- Most tests will just use the default bufsize/buftype, but under some
177
-- circumstances it's useful to use a different type (e.g., for alignment
178
-- requirements).
179
local all_tests = {
180
random = {
181
-- <sys/random.h>
182
{
183
func = "getrandom",
184
arguments = {
185
"__buf",
186
"__len",
187
"0",
188
},
189
exclude = excludes_stack_overflow,
190
},
191
},
192
select = {
193
-- <sys/select.h>
194
{
195
func = "FD_SET",
196
bufsize = "FD_SETSIZE",
197
buftype = "fd_set",
198
arguments = {
199
"__idx",
200
"__buf",
201
},
202
},
203
{
204
func = "FD_CLR",
205
bufsize = "FD_SETSIZE",
206
buftype = "fd_set",
207
arguments = {
208
"__idx",
209
"__buf",
210
},
211
},
212
{
213
func = "FD_ISSET",
214
bufsize = "FD_SETSIZE",
215
buftype = "fd_set",
216
arguments = {
217
"__idx",
218
"__buf",
219
},
220
},
221
},
222
socket = {
223
-- <sys/socket.h>
224
{
225
func = "getpeername",
226
buftype = "struct sockaddr",
227
bufsize = "sizeof(struct sockaddr)",
228
arguments = {
229
"sock[0]",
230
"__buf",
231
"&socklen",
232
},
233
exclude = excludes_stack_overflow,
234
stackvars = socket_stackvars .. "\tsocklen_t socklen;",
235
init = socket_socklen_init,
236
uses_len = true,
237
},
238
{
239
func = "getsockname",
240
buftype = "struct sockaddr",
241
bufsize = "sizeof(struct sockaddr)",
242
arguments = {
243
"sock[0]",
244
"__buf",
245
"&socklen",
246
},
247
exclude = excludes_stack_overflow,
248
stackvars = socket_stackvars .. "\tsocklen_t socklen;",
249
init = socket_socklen_init,
250
uses_len = true,
251
},
252
{
253
func = "recv",
254
arguments = {
255
"sock[0]",
256
"__buf",
257
"__len",
258
"0",
259
},
260
exclude = excludes_stack_overflow,
261
stackvars = socket_stackvars,
262
init = socket_init,
263
},
264
{
265
func = "recvfrom",
266
arguments = {
267
"sock[0]",
268
"__buf",
269
"__len",
270
"0",
271
"NULL",
272
"NULL",
273
},
274
exclude = excludes_stack_overflow,
275
stackvars = socket_stackvars,
276
init = socket_init,
277
},
278
{
279
func = "recvfrom",
280
variant = "sockaddr",
281
buftype = "struct sockaddr",
282
bufsize = "sizeof(struct sockaddr)",
283
arguments = {
284
"sock[0]",
285
"data",
286
"sizeof(data)",
287
"0",
288
"__buf",
289
"&socklen",
290
},
291
exclude = excludes_stack_overflow,
292
stackvars = recvfrom_sockaddr_stackvars,
293
init = socket_socklen_init,
294
uses_len = true,
295
},
296
{
297
func = "recvmsg",
298
variant = "msg_name",
299
buftype = "struct sockaddr",
300
bufsize = "sizeof(struct sockaddr)",
301
arguments = {
302
"sock[0]",
303
"&msg",
304
"0",
305
},
306
exclude = excludes_stack_overflow,
307
stackvars = recvmsg_stackvars,
308
init = [[
309
memset(&msg, 0, sizeof(msg));
310
msg.msg_name = BUF;
311
msg.msg_namelen = __len;
312
]],
313
uses_len = true,
314
},
315
{
316
func = "recvmsg",
317
variant = "msg_iov",
318
arguments = {
319
"sock[0]",
320
"&msg",
321
"0",
322
},
323
exclude = excludes_stack_overflow,
324
stackvars = recvmsg_stackvars .. "\tstruct iovec iov[2];\n",
325
init = [[
326
memset(&msg, 0, sizeof(msg));
327
memset(&iov[0], 0, sizeof(iov));
328
329
/*
330
* We position the buffer second just so that we can confirm that the
331
* fortification bits are traversing the iovec correctly.
332
*/
333
iov[1].iov_base = BUF;
334
iov[1].iov_len = __len;
335
336
msg.msg_iov = &iov[0];
337
msg.msg_iovlen = nitems(iov);
338
]],
339
uses_len = true,
340
},
341
{
342
func = "recvmsg",
343
variant = "msg_control",
344
bufsize = "CMSG_SPACE(sizeof(int))",
345
arguments = {
346
"sock[0]",
347
"&msg",
348
"0",
349
},
350
exclude = excludes_stack_overflow,
351
stackvars = recvmsg_stackvars,
352
init = [[
353
memset(&msg, 0, sizeof(msg));
354
355
msg.msg_control = BUF;
356
msg.msg_controllen = __len;
357
]],
358
uses_len = true,
359
},
360
{
361
func = "recvmmsg",
362
variant = "msgvec",
363
buftype = "struct mmsghdr[]",
364
bufsize = "2",
365
arguments = {
366
"sock[0]",
367
"__buf",
368
"__len",
369
"0",
370
"NULL",
371
},
372
stackvars = socket_stackvars,
373
},
374
{
375
-- We'll assume that recvmsg is covering msghdr
376
-- validation thoroughly enough, we'll just try tossing
377
-- an error in the second element of a msgvec to try and
378
-- make sure that each one is being validated.
379
func = "recvmmsg",
380
variant = "msghdr",
381
arguments = {
382
"sock[0]",
383
"&msgvec[0]",
384
"nitems(msgvec)",
385
"0",
386
"NULL",
387
},
388
exclude = excludes_stack_overflow,
389
stackvars = socket_stackvars .. "\tstruct mmsghdr msgvec[2];\n",
390
init = [[
391
memset(&msgvec[0], 0, sizeof(msgvec));
392
393
/*
394
* Same as above, make sure fortification isn't ignoring n > 1 elements
395
* of the msgvec.
396
*/
397
msgvec[1].msg_hdr.msg_control = BUF;
398
msgvec[1].msg_hdr.msg_controllen = __len;
399
]],
400
uses_len = true,
401
},
402
},
403
uio = {
404
-- <sys/uio.h>
405
{
406
func = "readv",
407
buftype = "struct iovec[]",
408
bufsize = 2,
409
arguments = {
410
"STDIN_FILENO",
411
"__buf",
412
"__len",
413
},
414
},
415
{
416
func = "readv",
417
variant = "iov",
418
arguments = {
419
"STDIN_FILENO",
420
"iov",
421
"nitems(iov)",
422
},
423
exclude = excludes_stack_overflow,
424
stackvars = readv_stackvars,
425
init = readv_init,
426
uses_len = true,
427
},
428
{
429
func = "preadv",
430
buftype = "struct iovec[]",
431
bufsize = 2,
432
arguments = {
433
"STDIN_FILENO",
434
"__buf",
435
"__len",
436
"0",
437
},
438
},
439
{
440
func = "preadv",
441
variant = "iov",
442
arguments = {
443
"STDIN_FILENO",
444
"iov",
445
"nitems(iov)",
446
"0",
447
},
448
exclude = excludes_stack_overflow,
449
stackvars = readv_stackvars,
450
init = readv_init,
451
uses_len = true,
452
},
453
},
454
poll = {
455
-- <poll.h>
456
{
457
func = "poll",
458
bufsize = "4",
459
buftype = "struct pollfd[]",
460
arguments = {
461
"__buf",
462
"__len",
463
"0",
464
},
465
init = poll_init,
466
},
467
{
468
func = "ppoll",
469
bufsize = "4",
470
buftype = "struct pollfd[]",
471
arguments = {
472
"__buf",
473
"__len",
474
"&tv",
475
"NULL",
476
},
477
stackvars = "\tstruct timespec tv = { 0 };\n",
478
init = poll_init,
479
},
480
},
481
signal = {
482
-- <signal.h>
483
{
484
func = "sig2str",
485
bufsize = "SIG2STR_MAX",
486
arguments = {
487
"1",
488
"__buf",
489
},
490
exclude = excludes_stack_overflow,
491
},
492
},
493
stdio = {
494
-- <stdio.h>
495
{
496
func = "ctermid",
497
bufsize = "L_ctermid",
498
arguments = {
499
"__buf",
500
},
501
exclude = excludes_stack_overflow,
502
},
503
{
504
func = "ctermid_r",
505
bufsize = "L_ctermid",
506
arguments = {
507
"__buf",
508
},
509
exclude = excludes_stack_overflow,
510
},
511
{
512
func = "fread",
513
arguments = {
514
"__buf",
515
"__len",
516
"1",
517
"stdin",
518
},
519
exclude = excludes_stack_overflow,
520
init = stdio_init,
521
},
522
{
523
func = "fread_unlocked",
524
arguments = {
525
"__buf",
526
"__len",
527
"1",
528
"stdin",
529
},
530
exclude = excludes_stack_overflow,
531
init = stdio_init,
532
},
533
{
534
func = "gets_s",
535
arguments = {
536
"__buf",
537
"__len",
538
},
539
exclude = excludes_stack_overflow,
540
init = stdio_init,
541
},
542
{
543
func = "sprintf",
544
arguments = {
545
"__buf",
546
"\"%.*s\"",
547
"(int)__len - 1", -- - 1 for NUL terminator
548
"srcvar",
549
},
550
exclude = excludes_stack_overflow,
551
stackvars = printf_stackvars,
552
init = printf_init,
553
},
554
{
555
func = "snprintf",
556
arguments = {
557
"__buf",
558
"__len",
559
"\"%.*s\"",
560
"(int)__len - 1", -- - 1 for NUL terminator
561
"srcvar",
562
},
563
exclude = excludes_stack_overflow,
564
stackvars = printf_stackvars,
565
init = printf_init,
566
},
567
{
568
func = "tmpnam",
569
bufsize = "L_tmpnam",
570
arguments = {
571
"__buf",
572
},
573
exclude = excludes_stack_overflow,
574
},
575
{
576
func = "fgets",
577
arguments = {
578
"__buf",
579
"__len",
580
"fp",
581
},
582
exclude = excludes_stack_overflow,
583
stackvars = "\tFILE *fp;\n",
584
init = [[
585
fp = new_fp(__len);
586
]],
587
},
588
},
589
stdlib = {
590
-- <stdlib.h>
591
{
592
func = "arc4random_buf",
593
arguments = {
594
"__buf",
595
"__len",
596
},
597
exclude = excludes_stack_overflow,
598
},
599
{
600
func = "getenv_r",
601
arguments = {
602
"\"PATH\"",
603
"__buf",
604
"__len",
605
},
606
exclude = excludes_stack_overflow,
607
},
608
{
609
func = "realpath",
610
bufsize = "PATH_MAX",
611
arguments = {
612
"\".\"",
613
"__buf",
614
},
615
exclude = excludes_stack_overflow,
616
},
617
},
618
string = {
619
-- <string.h>
620
{
621
func = "memcpy",
622
arguments = {
623
"__buf",
624
"src",
625
"__len",
626
},
627
exclude = excludes_stack_overflow,
628
stackvars = "\tchar src[__len + 10];\n",
629
},
630
{
631
func = "mempcpy",
632
arguments = {
633
"__buf",
634
"src",
635
"__len",
636
},
637
exclude = excludes_stack_overflow,
638
stackvars = "\tchar src[__len + 10];\n",
639
},
640
{
641
func = "memmove",
642
arguments = {
643
"__buf",
644
"src",
645
"__len",
646
},
647
exclude = excludes_stack_overflow,
648
stackvars = "\tchar src[__len + 10];\n",
649
},
650
{
651
func = "memset",
652
arguments = {
653
"__buf",
654
"0",
655
"__len",
656
},
657
exclude = excludes_stack_overflow,
658
},
659
{
660
func = "memset_explicit",
661
arguments = {
662
"__buf",
663
"0",
664
"__len",
665
},
666
exclude = excludes_stack_overflow,
667
},
668
{
669
func = "stpcpy",
670
arguments = {
671
"__buf",
672
"src",
673
},
674
exclude = excludes_stack_overflow,
675
stackvars = string_stackvars,
676
init = string_init,
677
uses_len = true,
678
},
679
{
680
func = "stpncpy",
681
arguments = {
682
"__buf",
683
"src",
684
"__len",
685
},
686
exclude = excludes_stack_overflow,
687
stackvars = string_stackvars,
688
init = string_init,
689
},
690
{
691
func = "strcat",
692
arguments = {
693
"__buf",
694
"src",
695
},
696
exclude = excludes_stack_overflow,
697
stackvars = string_stackvars,
698
init = string_init,
699
uses_len = true,
700
},
701
{
702
func = "strlcat",
703
arguments = {
704
"__buf",
705
"src",
706
"__len",
707
},
708
exclude = excludes_stack_overflow,
709
stackvars = string_stackvars,
710
init = string_init,
711
},
712
{
713
func = "strncat",
714
arguments = {
715
"__buf",
716
"src",
717
"__len",
718
},
719
exclude = excludes_stack_overflow,
720
stackvars = string_stackvars,
721
init = string_init,
722
},
723
{
724
func = "strcpy",
725
arguments = {
726
"__buf",
727
"src",
728
},
729
exclude = excludes_stack_overflow,
730
stackvars = string_stackvars,
731
init = string_init,
732
uses_len = true,
733
},
734
{
735
func = "strlcpy",
736
arguments = {
737
"__buf",
738
"src",
739
"__len",
740
},
741
exclude = excludes_stack_overflow,
742
stackvars = string_stackvars,
743
init = string_init,
744
},
745
{
746
func = "strncpy",
747
arguments = {
748
"__buf",
749
"src",
750
"__len",
751
},
752
exclude = excludes_stack_overflow,
753
stackvars = string_stackvars,
754
init = string_init,
755
},
756
},
757
strings = {
758
-- <strings.h>
759
{
760
func = "bcopy",
761
arguments = {
762
"src",
763
"__buf",
764
"__len",
765
},
766
exclude = excludes_stack_overflow,
767
stackvars = "\tchar src[__len + 10];\n",
768
},
769
{
770
func = "bzero",
771
arguments = {
772
"__buf",
773
"__len",
774
},
775
exclude = excludes_stack_overflow,
776
},
777
{
778
func = "explicit_bzero",
779
arguments = {
780
"__buf",
781
"__len",
782
},
783
exclude = excludes_stack_overflow,
784
},
785
},
786
unistd = {
787
-- <unistd.h>
788
{
789
func = "getcwd",
790
bufsize = "8",
791
arguments = {
792
"__buf",
793
"__len",
794
},
795
exclude = excludes_stack_overflow,
796
},
797
{
798
func = "getgrouplist",
799
bufsize = "4",
800
buftype = "gid_t[]",
801
arguments = {
802
"\"root\"",
803
"0",
804
"__buf",
805
"&intlen",
806
},
807
exclude = excludes_stack_overflow,
808
stackvars = "\tint intlen = (int)__len;\n",
809
uses_len = true,
810
},
811
{
812
func = "getgroups",
813
bufsize = "4",
814
buftype = "gid_t[]",
815
arguments = {
816
"__len",
817
"__buf",
818
},
819
exclude = excludes_stack_overflow,
820
},
821
{
822
func = "getloginclass",
823
arguments = {
824
"__buf",
825
"__len",
826
},
827
exclude = excludes_stack_overflow,
828
},
829
{
830
func = "pread",
831
bufsize = "41",
832
arguments = {
833
"fd",
834
"__buf",
835
"__len",
836
"0",
837
},
838
exclude = excludes_stack_overflow,
839
stackvars = "\tint fd;\n",
840
init = [[
841
fd = new_tmpfile(); /* Cannot fail */
842
]],
843
},
844
{
845
func = "read",
846
bufsize = "41",
847
arguments = {
848
"fd",
849
"__buf",
850
"__len",
851
},
852
exclude = excludes_stack_overflow,
853
stackvars = "\tint fd;\n",
854
init = [[
855
fd = new_tmpfile(); /* Cannot fail */
856
]],
857
},
858
{
859
func = "readlink",
860
arguments = {
861
"path",
862
"__buf",
863
"__len",
864
},
865
exclude = excludes_stack_overflow,
866
stackvars = "\tconst char *path;\n",
867
init = [[
868
path = new_symlink(__len); /* Cannot fail */
869
]],
870
},
871
{
872
func = "readlinkat",
873
arguments = {
874
"AT_FDCWD",
875
"path",
876
"__buf",
877
"__len",
878
},
879
exclude = excludes_stack_overflow,
880
stackvars = "\tconst char *path;\n",
881
init = [[
882
path = new_symlink(__len); /* Cannot fail */
883
]],
884
},
885
{
886
func = "getdomainname",
887
bufsize = #domainname + 1,
888
arguments = {
889
"__buf",
890
"__len",
891
},
892
need_root = true,
893
exclude = excludes_stack_overflow,
894
early_init = " dhost_jail();",
895
},
896
{
897
func = "getentropy",
898
arguments = {
899
"__buf",
900
"__len",
901
},
902
exclude = excludes_stack_overflow,
903
},
904
{
905
func = "gethostname",
906
bufsize = #hostname + 1,
907
arguments = {
908
"__buf",
909
"__len",
910
},
911
need_root = true,
912
exclude = excludes_stack_overflow,
913
early_init = " dhost_jail();",
914
},
915
{
916
func = "getlogin_r",
917
bufsize = "MAXLOGNAME + 1",
918
arguments = {
919
"__buf",
920
"__len",
921
},
922
exclude = excludes_stack_overflow,
923
},
924
{
925
func = "ttyname_r",
926
arguments = {
927
"fd",
928
"__buf",
929
"__len",
930
},
931
exclude = excludes_stack_overflow,
932
stackvars = "\tint fd;\n",
933
early_init = [[
934
fd = STDIN_FILENO;
935
if (!isatty(fd))
936
atf_tc_skip("stdin is not an fd");
937
]]
938
},
939
},
940
wchar = {
941
-- <wchar.h>
942
{
943
func = "wmemcpy",
944
buftype = "wchar_t[]",
945
arguments = {
946
"__buf",
947
"src",
948
"__len",
949
},
950
exclude = excludes_stack_overflow,
951
stackvars = "\twchar_t src[__len + 10];\n",
952
},
953
{
954
func = "wmempcpy",
955
buftype = "wchar_t[]",
956
arguments = {
957
"__buf",
958
"src",
959
"__len",
960
},
961
exclude = excludes_stack_overflow,
962
stackvars = "\twchar_t src[__len + 10];\n",
963
},
964
{
965
func = "wmemmove",
966
buftype = "wchar_t[]",
967
arguments = {
968
"__buf",
969
"src",
970
"__len",
971
},
972
exclude = excludes_stack_overflow,
973
stackvars = "\twchar_t src[__len + 10];\n",
974
},
975
{
976
func = "wmemset",
977
buftype = "wchar_t[]",
978
arguments = {
979
"__buf",
980
"L'0'",
981
"__len",
982
},
983
exclude = excludes_stack_overflow,
984
},
985
{
986
func = "wcpcpy",
987
buftype = "wchar_t[]",
988
arguments = {
989
"__buf",
990
"src",
991
},
992
exclude = excludes_stack_overflow,
993
stackvars = wstring_stackvars,
994
init = wstring_init,
995
uses_len = true,
996
},
997
{
998
func = "wcpncpy",
999
buftype = "wchar_t[]",
1000
arguments = {
1001
"__buf",
1002
"src",
1003
"__len",
1004
},
1005
exclude = excludes_stack_overflow,
1006
stackvars = wstring_stackvars,
1007
init = wstring_init,
1008
},
1009
{
1010
func = "wcscat",
1011
buftype = "wchar_t[]",
1012
arguments = {
1013
"__buf",
1014
"src",
1015
},
1016
exclude = excludes_stack_overflow,
1017
stackvars = wstring_stackvars,
1018
init = wstring_init,
1019
uses_len = true,
1020
},
1021
{
1022
func = "wcslcat",
1023
buftype = "wchar_t[]",
1024
arguments = {
1025
"__buf",
1026
"src",
1027
"__len",
1028
},
1029
exclude = excludes_stack_overflow,
1030
stackvars = wstring_stackvars,
1031
init = wstring_init,
1032
},
1033
{
1034
func = "wcsncat",
1035
buftype = "wchar_t[]",
1036
arguments = {
1037
"__buf",
1038
"src",
1039
"__len",
1040
},
1041
exclude = excludes_stack_overflow,
1042
stackvars = wstring_stackvars,
1043
init = wstring_init,
1044
},
1045
{
1046
func = "wcscpy",
1047
buftype = "wchar_t[]",
1048
arguments = {
1049
"__buf",
1050
"src",
1051
},
1052
exclude = excludes_stack_overflow,
1053
stackvars = wstring_stackvars,
1054
init = wstring_init,
1055
uses_len = true,
1056
},
1057
{
1058
func = "wcslcpy",
1059
buftype = "wchar_t[]",
1060
arguments = {
1061
"__buf",
1062
"src",
1063
"__len",
1064
},
1065
exclude = excludes_stack_overflow,
1066
stackvars = wstring_stackvars,
1067
init = wstring_init,
1068
},
1069
{
1070
func = "wcsncpy",
1071
buftype = "wchar_t[]",
1072
arguments = {
1073
"__buf",
1074
"src",
1075
"__len",
1076
},
1077
exclude = excludes_stack_overflow,
1078
stackvars = wstring_stackvars,
1079
init = wstring_init,
1080
},
1081
},
1082
}
1083
1084
local function write_test_boilerplate(fh, name, body, def)
1085
fh:write("ATF_TC(" .. name .. ");\n")
1086
fh:write("ATF_TC_HEAD(" .. name .. ", tc)\n")
1087
fh:write("{\n")
1088
if def.need_root then
1089
fh:write(" atf_tc_set_md_var(tc, \"require.user\", \"root\");\n")
1090
end
1091
fh:write("}\n")
1092
1093
fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
1094
fh:write("{\n" .. body .. "\n}\n\n")
1095
return name
1096
end
1097
1098
local function generate_test_name(func, variant, disposition, heap)
1099
local basename = func
1100
if variant then
1101
basename = basename .. "_" .. variant
1102
end
1103
if heap then
1104
basename = basename .. "_heap"
1105
end
1106
if disposition < 0 then
1107
return basename .. "_before_end"
1108
elseif disposition == 0 then
1109
return basename .. "_end"
1110
else
1111
return basename .. "_after_end"
1112
end
1113
end
1114
1115
local function array_type(buftype)
1116
if not buftype:match("%[%]") then
1117
return nil
1118
end
1119
1120
return buftype:gsub("%[%]", "")
1121
end
1122
1123
local function configurable(def, idx)
1124
local cfgitem = def[idx]
1125
1126
if not cfgitem then
1127
return nil
1128
end
1129
1130
if type(cfgitem) == "function" then
1131
return cfgitem()
1132
end
1133
1134
return cfgitem
1135
end
1136
1137
local function generate_stackframe(buftype, bufsize, disposition, heap, def)
1138
local function len_offset(inverted)
1139
-- Tests that don't use __len in their arguments may use an
1140
-- inverted sense because we can't just specify a length that
1141
-- would induce an access just after the end. Instead, we have
1142
-- to manipulate the buffer size to be too short so that the
1143
-- function under test would write one too many.
1144
if disposition < 0 then
1145
return ((inverted and " + ") or " - ") .. "1"
1146
elseif disposition == 0 then
1147
return ""
1148
else
1149
return ((inverted and " - ") or " + ") .. "1"
1150
end
1151
end
1152
1153
local function test_uses_len()
1154
if def.uses_len then
1155
return true
1156
end
1157
1158
for _, arg in ipairs(def.arguments) do
1159
if arg:match("__len") or arg:match("__idx") then
1160
return true
1161
end
1162
end
1163
1164
return false
1165
end
1166
1167
1168
-- This is perhaps a little convoluted, but we toss the buffer into a
1169
-- struct on the stack to guarantee that we have at least one valid
1170
-- byte on either side of the buffer -- a measure to make sure that
1171
-- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
1172
-- rather than some other stack or memory protection.
1173
local vars = "\tstruct {\n"
1174
vars = vars .. "\t\tuint8_t padding_l;\n"
1175
1176
local uses_len = test_uses_len()
1177
local bufsize_offset = len_offset(not uses_len)
1178
local buftype_elem = array_type(buftype)
1179
local size_expr = bufsize
1180
1181
if not uses_len then
1182
-- If the length isn't in use, we have to vary the buffer size
1183
-- since the fortified function likely has some internal size
1184
-- constraint that it's supposed to be checking.
1185
size_expr = size_expr .. bufsize_offset
1186
end
1187
1188
if not heap and buftype_elem then
1189
-- Array type: size goes after identifier
1190
vars = vars .. "\t\t" .. buftype_elem ..
1191
" __buf[" .. size_expr .. "];\n"
1192
else
1193
local basic_type = buftype_elem or buftype
1194
1195
-- Heap tests obviously just put a pointer on the stack that
1196
-- points to our new allocation, but we leave it in the padded
1197
-- struct just to simplify our generator.
1198
if heap then
1199
basic_type = basic_type .. " *"
1200
end
1201
vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
1202
end
1203
1204
-- padding_r is our just-past-the-end padding that we use to make sure
1205
-- that there's a valid portion after the buffer that isn't being
1206
-- included in our function calls. If we didn't have it, then we'd have
1207
-- a hard time feeling confident that an abort on the just-after tests
1208
-- isn't maybe from some other memory or stack protection.
1209
vars = vars .. "\t\tuint8_t padding_r;\n"
1210
vars = vars .. "\t} __stack;\n"
1211
1212
-- Not all tests will use __bufsz, but some do for, e.g., clearing
1213
-- memory..
1214
vars = vars .. "\tconst size_t __bufsz __unused = "
1215
if heap then
1216
local scalar = 1
1217
if buftype_elem then
1218
scalar = size_expr
1219
end
1220
1221
vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
1222
else
1223
vars = vars .. "sizeof(__stack.__buf);\n"
1224
end
1225
1226
vars = vars .. "\tconst size_t __len = " .. bufsize ..
1227
bufsize_offset .. ";\n"
1228
vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
1229
1230
-- For overflow testing, we need to fork() because we're expecting the
1231
-- test to ultimately abort()/_exit(). Then we can collect the exit
1232
-- status and report appropriately.
1233
if disposition > 0 then
1234
vars = vars .. "\tpid_t __child;\n"
1235
vars = vars .. "\tint __status;\n"
1236
end
1237
1238
-- Any other stackvars defined by the test get placed after everything
1239
-- else.
1240
vars = vars .. (configurable(def, "stackvars") or "")
1241
1242
return vars
1243
end
1244
1245
local function write_test(fh, func, disposition, heap, def)
1246
local testname = generate_test_name(func, def.variant, disposition, heap)
1247
local buftype = def.buftype or "unsigned char[]"
1248
local bufsize = def.bufsize or 42
1249
local body = ""
1250
1251
if def.exclude and def.exclude(disposition, heap) then
1252
return
1253
end
1254
1255
local function need_addr()
1256
return not (buftype:match("%[%]") or buftype:match("%*"))
1257
end
1258
1259
if heap then
1260
body = body .. "#define BUF __stack.__buf\n"
1261
else
1262
body = body .. "#define BUF &__stack.__buf\n"
1263
end
1264
1265
-- Setup the buffer
1266
body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
1267
"\n"
1268
1269
-- Any early initialization goes before we would fork for the just-after
1270
-- tests, because they may want to skip the test based on some criteria
1271
-- and we can't propagate that up very easily once we're forked.
1272
local early_init = configurable(def, "early_init")
1273
body = body .. (early_init or "")
1274
if early_init then
1275
body = body .. "\n"
1276
end
1277
1278
-- Fork off, iff we're testing some access past the end of the buffer.
1279
if disposition > 0 then
1280
body = body .. [[
1281
__child = fork();
1282
ATF_REQUIRE(__child >= 0);
1283
if (__child > 0)
1284
goto monitor;
1285
1286
/* Child */
1287
disable_coredumps();
1288
]]
1289
end
1290
1291
local bufvar = "__stack.__buf"
1292
if heap then
1293
-- Buffer needs to be initialized because it's a heap allocation.
1294
body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
1295
end
1296
1297
-- Non-early init happens just after the fork in the child, not the
1298
-- monitor. This is used to setup any other buffers we may need, for
1299
-- instance.
1300
local extra_init = configurable(def, "init")
1301
body = body .. (extra_init or "")
1302
1303
if heap or extra_init then
1304
body = body .. "\n"
1305
end
1306
1307
-- Setup the function call with arguments as described in the test
1308
-- definition.
1309
body = body .. "\t" .. func .. "("
1310
1311
for idx, arg in ipairs(def.arguments) do
1312
if idx > 1 then
1313
body = body .. ", "
1314
end
1315
1316
if arg == "__buf" then
1317
if not heap and need_addr() then
1318
body = body .. "&"
1319
end
1320
1321
body = body .. bufvar
1322
else
1323
local argname = arg
1324
1325
if def.value_of then
1326
argname = argname or def.value_of(arg)
1327
end
1328
1329
body = body .. argname
1330
end
1331
end
1332
1333
body = body .. ");\n"
1334
1335
-- Monitor stuff follows, for OOB access.
1336
if disposition <= 0 then
1337
goto skip
1338
end
1339
1340
body = body .. [[
1341
_exit(EX_SOFTWARE); /* Should have aborted. */
1342
1343
monitor:
1344
while (waitpid(__child, &__status, 0) != __child) {
1345
ATF_REQUIRE_EQ(EINTR, errno);
1346
}
1347
1348
if (!WIFSIGNALED(__status)) {
1349
switch (WEXITSTATUS(__status)) {
1350
case EX_SOFTWARE:
1351
atf_tc_fail("FORTIFY_SOURCE failed to abort");
1352
break;
1353
case EX_OSERR:
1354
atf_tc_fail("setrlimit(2) failed");
1355
break;
1356
default:
1357
atf_tc_fail("child exited with status %d",
1358
WEXITSTATUS(__status));
1359
}
1360
} else {
1361
ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
1362
}
1363
]]
1364
1365
::skip::
1366
body = body .. "#undef BUF\n"
1367
return write_test_boilerplate(fh, testname, body, def)
1368
end
1369
1370
-- main()
1371
local tests
1372
local tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
1373
for k, defs in pairs(all_tests) do
1374
if k == tcat then
1375
tests = defs
1376
break
1377
end
1378
end
1379
1380
assert(tests, "category " .. tcat .. " not found")
1381
1382
local fh = io.stdout
1383
fh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
1384
tcat .. "\"` */\n\n")
1385
fh:write("#define _FORTIFY_SOURCE 2\n")
1386
fh:write("#define TMPFILE_SIZE (1024 * 32)\n")
1387
1388
fh:write("\n")
1389
for _, inc in ipairs(includes) do
1390
fh:write("#include <" .. inc .. ">\n")
1391
end
1392
1393
fh:write([[
1394
1395
static FILE * __unused
1396
new_fp(size_t __len)
1397
{
1398
static char fpbuf[LINE_MAX];
1399
FILE *fp;
1400
1401
ATF_REQUIRE(__len <= sizeof(fpbuf));
1402
1403
memset(fpbuf, 'A', sizeof(fpbuf) - 1);
1404
fpbuf[sizeof(fpbuf) - 1] = '\0';
1405
1406
fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
1407
ATF_REQUIRE(fp != NULL);
1408
1409
return (fp);
1410
}
1411
1412
/*
1413
* Create a new symlink to use for readlink(2) style tests, we'll just use a
1414
* random target name to have something interesting to look at.
1415
*/
1416
static const char * __unused
1417
new_symlink(size_t __len)
1418
{
1419
static const char linkname[] = "link";
1420
char target[MAXNAMLEN];
1421
int error;
1422
1423
ATF_REQUIRE(__len <= sizeof(target));
1424
1425
arc4random_buf(target, sizeof(target));
1426
1427
error = unlink(linkname);
1428
ATF_REQUIRE(error == 0 || errno == ENOENT);
1429
1430
error = symlink(target, linkname);
1431
ATF_REQUIRE(error == 0);
1432
1433
return (linkname);
1434
}
1435
1436
/*
1437
* For our purposes, first descriptor will be the reader; we'll send both
1438
* raw data and a control message over it so that the result can be used for
1439
* any of our recv*() tests.
1440
*/
1441
static void __unused
1442
new_socket(int sock[2])
1443
{
1444
unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
1445
static char sockbuf[256];
1446
ssize_t rv;
1447
size_t total = 0;
1448
struct msghdr hdr = { 0 };
1449
struct cmsghdr *cmsg;
1450
int error, fd;
1451
1452
error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
1453
ATF_REQUIRE(error == 0);
1454
1455
while (total != sizeof(sockbuf)) {
1456
rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
1457
1458
ATF_REQUIRE_MSG(rv > 0,
1459
"expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
1460
rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
1461
ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
1462
"%zd exceeds total %zu", rv, sizeof(sockbuf));
1463
total += rv;
1464
}
1465
1466
hdr.msg_control = ctrl;
1467
hdr.msg_controllen = sizeof(ctrl);
1468
1469
cmsg = CMSG_FIRSTHDR(&hdr);
1470
cmsg->cmsg_level = SOL_SOCKET;
1471
cmsg->cmsg_type = SCM_RIGHTS;
1472
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1473
fd = STDIN_FILENO;
1474
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
1475
1476
error = sendmsg(sock[1], &hdr, 0);
1477
ATF_REQUIRE(error != -1);
1478
}
1479
1480
/*
1481
* Constructs a tmpfile that we can use for testing read(2) and friends.
1482
*/
1483
static int __unused
1484
new_tmpfile(void)
1485
{
1486
char buf[1024];
1487
ssize_t rv;
1488
size_t written;
1489
int fd;
1490
1491
fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
1492
ATF_REQUIRE(fd >= 0);
1493
1494
written = 0;
1495
while (written < TMPFILE_SIZE) {
1496
rv = write(fd, buf, sizeof(buf));
1497
ATF_REQUIRE(rv > 0);
1498
1499
written += rv;
1500
}
1501
1502
ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
1503
return (fd);
1504
}
1505
1506
static void
1507
disable_coredumps(void)
1508
{
1509
struct rlimit rl = { 0 };
1510
1511
if (setrlimit(RLIMIT_CORE, &rl) == -1)
1512
_exit(EX_OSERR);
1513
}
1514
1515
/*
1516
* Replaces stdin with a file that we can actually read from, for tests where
1517
* we want a FILE * or fd that we can get data from.
1518
*/
1519
static void __unused
1520
replace_stdin(void)
1521
{
1522
int fd;
1523
1524
fd = new_tmpfile();
1525
1526
(void)dup2(fd, STDIN_FILENO);
1527
if (fd != STDIN_FILENO)
1528
close(fd);
1529
}
1530
1531
]])
1532
1533
if tcat == "unistd" then
1534
fh:write("#define JAIL_HOSTNAME \"" .. hostname .. "\"\n")
1535
fh:write("#define JAIL_DOMAINNAME \"" .. domainname .. "\"\n")
1536
fh:write([[
1537
static void
1538
dhost_jail(void)
1539
{
1540
struct iovec iov[4];
1541
int jid;
1542
1543
iov[0].iov_base = __DECONST(char *, "host.hostname");
1544
iov[0].iov_len = sizeof("host.hostname");
1545
iov[1].iov_base = __DECONST(char *, JAIL_HOSTNAME);
1546
iov[1].iov_len = sizeof(JAIL_HOSTNAME);
1547
iov[2].iov_base = __DECONST(char *, "host.domainname");
1548
iov[2].iov_len = sizeof("host.domainname");
1549
iov[3].iov_base = __DECONST(char *, JAIL_DOMAINNAME);
1550
iov[3].iov_len = sizeof(JAIL_DOMAINNAME);
1551
1552
jid = jail_set(iov, nitems(iov), JAIL_CREATE | JAIL_ATTACH);
1553
ATF_REQUIRE_MSG(jid > 0, "Jail creation failed: %s", strerror(errno));
1554
}
1555
1556
]])
1557
end
1558
1559
for _, def in pairs(tests) do
1560
local func = def.func
1561
local function write_tests(heap)
1562
-- Dispositions here are relative to the buffer size prescribed
1563
-- by the test definition.
1564
local dispositions = def.dispositions or { -1, 0, 1 }
1565
1566
for _, disposition in ipairs(dispositions) do
1567
tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
1568
end
1569
end
1570
1571
write_tests(false)
1572
write_tests(true)
1573
end
1574
1575
fh:write("ATF_TP_ADD_TCS(tp)\n")
1576
fh:write("{\n")
1577
for idx = 1, #tests_added do
1578
fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
1579
end
1580
fh:write("\treturn (atf_no_error());\n")
1581
fh:write("}\n")
1582
1583