首页
社区
课程
招聘
[翻译] 没有语法也没关系:在没有系统调用说明的情况下实现对 Linux 内核的模糊测试
发表于: 2025-1-18 00:17 4409

[翻译] 没有语法也没关系:在没有系统调用说明的情况下实现对 Linux 内核的模糊测试

2025-1-18 00:17
4409

整个计算生态系统的完整性取决于操作系统(OS)的安全性。不幸的是,由于操作系统代码的规模和复杂性,每年在操作系统中都会发现数百个安全问题 [32]。因此,操作系统一直是应用安全分析工具的主要用例。在近年来,模糊测试(fuzz-testing)已成为自动发现软件安全问题的主流技术。因此,模糊测试已经被用于在内核中发现数千个漏洞 [14]。然而,现代操作系统模糊测试工具(如 Syzkaller)依赖于为内核中每个被测试接口手动创建的精确且复杂的测试框架和语法规则。由于对语法规则的依赖,当前的操作系统模糊测试工具面临着扩展性问题。

本文提出了FUZZNG,一种针对操作系统系统调用的通用模糊测试方法。与 Syzkaller 不同,FUZZNG 不需要对系统调用接口进行复杂的描述即可运行。相反,FUZZNG 利用内核设计中的基本特性,重新定义并简化了模糊测试工具的输入空间。因此,FUZZNG 对每个新目标仅需要一个小型的配置文件,基本上就是一个文件和系统调用编号的列表,供模糊测试工具探索。

我们在 Linux 内核上实现了 FUZZNG。在对 10 个由 Syzkaller 提供详细描述的 Linux 组件进行测试时,FUZZNG 的代码覆盖率平均达到了 Syzkaller 的 102.5%。FUZZNG 找到了 9 个新漏洞(其中 5 个是在 Syzkaller 已经进行了多年深入测试的组件中发现的)。此外,FUZZNG 的轻量级配置文件的大小不到 Syzkaller 手动编写语法规则的 1.7%。最重要的是,FUZZNG 在没有初始输入种子或专家指导的情况下实现了这些成果。

引言

操作系统仍然是现代计算中最为安全关键的基础模块之一。操作系统在资源管理和应用程序隔离中起着至关重要的作用,这使得攻击者将其作为攻击目标,试图破坏操作系统提供的安全保证。认识到操作系统安全的重要性,模糊测试工具已经帮助识别并修复了操作系统内核中的数千个漏洞。近年来,操作系统模糊测试工具的成功强调了编写安全低级代码的难度,这甚至促使了相关领域的新兴举措,例如在 Linux 内核中支持更安全的编程语言,以及利用内存标记等硬件功能来实现针对内存损坏问题的先进低开销防御 [23][44]。大多数操作系统模糊测试工具都专注于关键的系统调用接口,该接口允许用户空间应用程序向内核请求服务。

Syzkaller [14] 是最知名的系统调用模糊测试工具,它已经成为 Linux 内核开发生命周期的重要组成部分,并在内核提交记录中被提及超过 2,700 次。因此,Syzkaller 本身已经发展成为一个庞大的项目,拥有超过 200 位贡献者。关键在于,Syzkaller 只能模糊测试具有足够详细“syzlang”语法描述的系统调用。这些语法规则对系统调用的输入和输出资源类型进行了编码和注释。因此,Syzkaller 社区的大量工作都集中在为系统调用开发和改进“syzlang”描述,这些描述对 Syzkaller 的成功至关重要。

编写这些语法规则是一个手动过程,需要对相关接口(例如系统调用集合)有详细的了解。因此,这些语法规则容易受到人为错误的影响,可能导致覆盖率不足或过拟合(使得模糊测试工具无法探索代码中所有可能被覆盖的状态和场景)。此外,Syzkaller 有时需要编写额外的框架代码来测试特别复杂的接口。例如,为了测试支持安全关键虚拟化软件的 Linux 内核虚拟机(KVM)接口,Syzkaller 开发者提交了 891 行详细的系统调用描述、243 个 KVM 相关常量,以及另外 879 行 KVM 特定的 C 代码框架(如图 1 所示)。尽管 Syzkaller 包含了数万条手工制作的“syzlang”规则,但当前的过程无法扩展到每年新增数百万行代码的 Linux 内核 [33]。学术研究已经认识到 Syzkaller 在手动创建 syzlang 语法规则时的扩展性问题,并专注于自动生成这些语法规则的研究。例如 Difuze、IMF、SyzGen 和 KSG 等研究通过静态和动态分析技术自动生成系统调用描述 [12][18][9][51]。Difuze、IMF 和 SyzGen 主要针对没有手工描述基线的接口(例如 Android 驱动程序和 macOS API)进行设计和评估。KSG 的描述似乎提高了 Syzkaller 的覆盖率,但其源码尚未公开,而基于 Syzkaller 的 Linux 模糊测试工作仍然仅依赖于手工编写的描述。一些针对描述生成的上游努力仅限于识别传递给 ioctl 系统调用的结构体参数类型 [36]。重要的是,自 2018 年以来,Syzkaller 项目一直将自动生成 Linux 系统调用描述的问题当作一个公开议题进行跟踪 [53]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
/tmp/desc.txt Mon Jan 17 05:46:11 2022 1
# Copyright 2015 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in t
include <linux/kvm.h>
include <linux/kvm_host.h>
include <uapi/linux/fcntl.h>
include <asm/kvm.h>
include <asm/mce.h>
resource fd_kvm[fd]
resource fd_kvmvm[fd]
resource fd_kvmcpu[fd]
resource fd_kvmdev[fd]
resource fd_sgx_provision[fd]
openat$kvm(fd const[AT_FDCWD], file ptr[in, string["/dev/kvm"]], flags flags[ope
openat$sgx_provision(fd const[AT_FDCWD], file ptr[in, string["/dev/sgx_provision
ioctl$KVM_CREATE_VM(fd fd_kvm, cmd const[KVM_CREATE_VM], type const[0]) fd_kvmvm
ioctl$KVM_GET_MSR_INDEX_LIST(fd fd_kvm, cmd const[KVM_GET_MSR_INDEX_LIST], arg p
ioctl$KVM_CHECK_EXTENSION(fd fd_kvm, cmd const[KVM_CHECK_EXTENSION], arg intptr)
ioctl$KVM_GET_VCPU_MMAP_SIZE(fd fd_kvm, cmd const[KVM_GET_VCPU_MMAP_SIZE])
ioctl$KVM_GET_SUPPORTED_CPUID(fd fd_kvm, cmd const[KVM_GET_SUPPORTED_CPUID], arg
ioctl$KVM_GET_EMULATED_CPUID(fd fd_kvmvm, cmd const[KVM_GET_EMULATED_CPUID], arg
ioctl$KVM_X86_GET_MCE_CAP_SUPPORTED(fd fd_kvmvm, cmd const[KVM_X86_GET_MCE_CAP_S
ioctl$KVM_GET_API_VERSION(fd fd_kvm, cmd const[KVM_GET_API_VERSION], type const[
ioctl$KVM_CREATE_VCPU(fd fd_kvmvm, cmd const[KVM_CREATE_VCPU], id intptr[0:2]) f
ioctl$KVM_CHECK_EXTENSION_VM(fd fd_kvmvm, cmd const[KVM_CHECK_EXTENSION], arg in
ioctl$KVM_GET_DIRTY_LOG(fd fd_kvmvm, cmd const[KVM_GET_DIRTY_LOG], arg ptr[in, k
ioctl$KVM_CREATE_IRQCHIP(fd fd_kvmvm, cmd const[KVM_CREATE_IRQCHIP])
ioctl$KVM_IRQ_LINE(fd fd_kvmvm, cmd const[KVM_IRQ_LINE], arg ptr[in, kvm_irq_lev
ioctl$KVM_IRQ_LINE_STATUS(fd fd_kvmvm, cmd const[KVM_IRQ_LINE_STATUS], arg ptr[i
ioctl$KVM_GET_IRQCHIP(fd fd_kvmvm, cmd const[KVM_GET_IRQCHIP], arg ptr[out, kvm_
ioctl$KVM_SET_IRQCHIP(fd fd_kvmvm, cmd const[KVM_SET_IRQCHIP], arg ptr[in, kvm_i
ioctl$KVM_XEN_HVM_CONFIG(fd fd_kvmvm, cmd const[KVM_XEN_HVM_CONFIG], arg ptr[in,
ioctl$KVM_GET_CLOCK(fd fd_kvmvm, cmd const[KVM_GET_CLOCK], arg ptr[out, kvm_cloc
ioctl$KVM_SET_CLOCK(fd fd_kvmvm, cmd const[KVM_SET_CLOCK], arg ptr[in, kvm_clock
ioctl$KVM_SET_USER_MEMORY_REGION(fd fd_kvmvm, cmd const[KVM_SET_USER_MEMORY_REGI
ioctl$KVM_SET_TSS_ADDR(fd fd_kvmvm, cmd const[KVM_SET_TSS_ADDR], arg flags[kvm_x
ioctl$KVM_SET_IDENTITY_MAP_ADDR(fd fd_kvmvm, cmd const[KVM_SET_IDENTITY_MAP_ADDR
ioctl$KVM_SET_BOOT_CPU_ID(fd fd_kvmvm, cmd const[KVM_SET_BOOT_CPU_ID], arg ptr[i
ioctl$KVM_PPC_GET_PVINFO(fd fd_kvmvm, cmd const[KVM_PPC_GET_PVINFO], arg buffer[
ioctl$KVM_ASSIGN_PCI_DEVICE(fd fd_kvmvm, cmd const[KVM_ASSIGN_PCI_DEVICE], arg p
ioctl$KVM_DEASSIGN_PCI_DEVICE(fd fd_kvmvm, cmd const[KVM_DEASSIGN_PCI_DEVICE], a
ioctl$KVM_ASSIGN_DEV_IRQ(fd fd_kvmvm, cmd const[KVM_ASSIGN_DEV_IRQ], arg ptr[in,
ioctl$KVM_DEASSIGN_DEV_IRQ(fd fd_kvmvm, cmd const[KVM_DEASSIGN_DEV_IRQ], arg ptr
ioctl$KVM_SET_GSI_ROUTING(fd fd_kvmvm, cmd const[KVM_SET_GSI_ROUTING], arg ptr[i
ioctl$KVM_ASSIGN_SET_MSIX_NR(fd fd_kvmvm, cmd const[KVM_ASSIGN_SET_MSIX_NR], arg
ioctl$KVM_ASSIGN_SET_MSIX_ENTRY(fd fd_kvmvm, cmd const[KVM_ASSIGN_SET_MSIX_ENTRY
ioctl$KVM_IOEVENTFD(fd fd_kvmvm, cmd const[KVM_IOEVENTFD], arg ptr[in, kvm_ioeve
ioctl$KVM_ASSIGN_SET_INTX_MASK(fd fd_kvmvm, cmd const[KVM_ASSIGN_SET_INTX_MASK],
ioctl$KVM_SIGNAL_MSI(fd fd_kvmvm, cmd const[KVM_SIGNAL_MSI], arg ptr[in, kvm_msi
ioctl$KVM_CREATE_PIT2(fd fd_kvmvm, cmd const[KVM_CREATE_PIT2], arg ptr[in, kvm_p
ioctl$KVM_GET_PIT(fd fd_kvmvm, cmd const[KVM_GET_PIT], arg ptr[out, kvm_pit_stat
ioctl$KVM_SET_PIT(fd fd_kvmvm, cmd const[KVM_SET_PIT], arg ptr[in, kvm_pit_state
ioctl$KVM_GET_PIT2(fd fd_kvmvm, cmd const[KVM_GET_PIT2], arg ptr[out, kvm_pit_st
ioctl$KVM_SET_PIT2(fd fd_kvmvm, cmd const[KVM_SET_PIT2], arg ptr[in, kvm_pit_sta
ioctl$KVM_PPC_GET_SMMU_INFO(fd fd_kvmvm, cmd const[KVM_PPC_GET_SMMU_INFO], arg b
ioctl$KVM_IRQFD(fd fd_kvmvm, cmd const[KVM_IRQFD], arg ptr[in, kvm_irqfd])
ioctl$KVM_PPC_ALLOCATE_HTAB(fd fd_kvmvm, cmd const[KVM_PPC_ALLOCATE_HTAB], arg p
ioctl$KVM_CREATE_DEVICE(fd fd_kvmvm, cmd const[KVM_CREATE_DEVICE], arg ptr[inout
ioctl$KVM_REGISTER_COALESCED_MMIO(fd fd_kvmvm, cmd const[KVM_REGISTER_COALESCED_
/tmp/desc.txt Mon Jan 17 05:46:11 2022 2
ioctl$KVM_UNREGISTER_COALESCED_MMIO(fd fd_kvmvm, cmd const[KVM_UNREGISTER_COALES
ioctl$KVM_SET_NR_MMU_PAGES(fd fd_kvmvm, cmd const[KVM_SET_NR_MMU_PAGES], arg int
ioctl$KVM_GET_NR_MMU_PAGES(fd fd_kvmvm, cmd const[KVM_GET_NR_MMU_PAGES], arg int
ioctl$KVM_REINJECT_CONTROL(fd fd_kvmvm, cmd const[KVM_REINJECT_CONTROL], arg ptr
ioctl$KVM_HYPERV_EVENTFD(fd fd_kvmvm, cmd const[KVM_HYPERV_EVENTFD], arg ptr[in,
ioctl$KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP],
ioctl$KVM_CAP_HALT_POLL(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[in, kvm_
ioctl$KVM_CAP_DIRTY_LOG_RING(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[in,
# NEED: arch constraints for syscalls. These are amd64/386-specific, but consts
ioctl$KVM_CAP_DISABLE_QUIRKS(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[in,
ioctl$KVM_CAP_SPLIT_IRQCHIP(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[in,
ioctl$KVM_CAP_X2APIC_API(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[in, kvm
ioctl$KVM_CAP_X86_DISABLE_EXITS(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[
ioctl$KVM_CAP_MSR_PLATFORM_INFO(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[
ioctl$KVM_CAP_EXCEPTION_PAYLOAD(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[
ioctl$KVM_CAP_X86_USER_SPACE_MSR(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr
ioctl$KVM_CAP_X86_BUS_LOCK_EXIT(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[
ioctl$KVM_CAP_SGX_ATTRIBUTE(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[in,
ioctl$KVM_CAP_VM_COPY_ENC_CONTEXT_FROM(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], a
ioctl$KVM_CAP_EXIT_HYPERCALL(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP], arg ptr[in,
ioctl$KVM_CAP_EXIT_ON_EMULATION_FAILURE(fd fd_kvmvm, cmd const[KVM_ENABLE_CAP],
ioctl$KVM_RUN(fd fd_kvmcpu, cmd const[KVM_RUN], arg const[0])
ioctl$KVM_GET_REGS(fd fd_kvmcpu, cmd const[KVM_GET_REGS], arg ptr[out, kvm_regs]
ioctl$KVM_SET_REGS(fd fd_kvmcpu, cmd const[KVM_SET_REGS], arg ptr[in, kvm_regs])
ioctl$KVM_GET_SREGS(fd fd_kvmcpu, cmd const[KVM_GET_SREGS], arg ptr[out, kvm_sre
ioctl$KVM_SET_SREGS(fd fd_kvmcpu, cmd const[KVM_SET_SREGS], arg ptr[in, kvm_sreg
ioctl$KVM_TRANSLATE(fd fd_kvmcpu, cmd const[KVM_TRANSLATE], arg ptr[in, kvm_tran
ioctl$KVM_INTERRUPT(fd fd_kvmcpu, cmd const[KVM_INTERRUPT], arg ptr[in, int32])
ioctl$KVM_GET_MSRS(fd fd_kvmcpu, cmd const[KVM_GET_MSRS], arg ptr[out, kvm_msrs]
ioctl$KVM_SET_MSRS(fd fd_kvmcpu, cmd const[KVM_SET_MSRS], arg ptr[in, kvm_msrs])
ioctl$KVM_SET_CPUID(fd fd_kvmcpu, cmd const[KVM_SET_CPUID], arg ptr[in, kvm_cpui
# NEED: we should be able to read kvm_cpuid2 with KVM_GET_CPUID2, alter few bits
ioctl$KVM_GET_CPUID2(fd fd_kvmcpu, cmd const[KVM_GET_CPUID2], arg ptr[out, kvm_c
ioctl$KVM_SET_CPUID2(fd fd_kvmcpu, cmd const[KVM_SET_CPUID2], arg ptr[in, kvm_cp
ioctl$KVM_SET_SIGNAL_MASK(fd fd_kvmcpu, cmd const[KVM_SET_SIGNAL_MASK], arg ptr[
ioctl$KVM_GET_FPU(fd fd_kvmcpu, cmd const[KVM_GET_FPU], arg ptr[out, kvm_fpu])
ioctl$KVM_SET_FPU(fd fd_kvmcpu, cmd const[KVM_SET_FPU], arg ptr[in, kvm_fpu])
ioctl$KVM_GET_VCPU_EVENTS(fd fd_kvmcpu, cmd const[KVM_GET_VCPU_EVENTS], arg ptr[
ioctl$KVM_SET_VCPU_EVENTS(fd fd_kvmcpu, cmd const[KVM_SET_VCPU_EVENTS], arg ptr[
ioctl$KVM_GET_DEBUGREGS(fd fd_kvmcpu, cmd const[KVM_GET_DEBUGREGS], arg ptr[out,
ioctl$KVM_SET_DEBUGREGS(fd fd_kvmcpu, cmd const[KVM_SET_DEBUGREGS], arg ptr[in,
ioctl$KVM_GET_MP_STATE(fd fd_kvmcpu, cmd const[KVM_GET_MP_STATE], arg ptr[out, i
ioctl$KVM_SET_MP_STATE(fd fd_kvmcpu, cmd const[KVM_SET_MP_STATE], arg ptr[in, fl
ioctl$KVM_GET_XSAVE(fd fd_kvmcpu, cmd const[KVM_GET_XSAVE], arg ptr[out, kvm_xsa
ioctl$KVM_SET_XSAVE(fd fd_kvmcpu, cmd const[KVM_SET_XSAVE], arg ptr[in, kvm_xsav
ioctl$KVM_GET_XCRS(fd fd_kvmcpu, cmd const[KVM_GET_XCRS], arg ptr[in, kvm_xcrs])
ioctl$KVM_SET_XCRS(fd fd_kvmcpu, cmd const[KVM_SET_XCRS], arg ptr[in, kvm_xcrs])
ioctl$KVM_SET_TSC_KHZ(fd fd_kvmcpu, cmd const[KVM_SET_TSC_KHZ], arg intptr)
ioctl$KVM_GET_TSC_KHZ(fd fd_kvmcpu, cmd const[KVM_GET_TSC_KHZ])
ioctl$KVM_GET_LAPIC(fd fd_kvmcpu, cmd const[KVM_GET_LAPIC], arg ptr[in, kvm_lapi
ioctl$KVM_SET_LAPIC(fd fd_kvmcpu, cmd const[KVM_SET_LAPIC], arg ptr[in, kvm_lapi
ioctl$KVM_DIRTY_TLB(fd fd_kvmcpu, cmd const[KVM_DIRTY_TLB], arg ptr[in, kvm_dirt
ioctl$KVM_NMI(fd fd_kvmcpu, cmd const[KVM_NMI])
# NEED: arch constraints for syscalls. These are s390-specific, but consts are p
ioctl$KVM_S390_UCAS_MAP(fd fd_kvmcpu, cmd const[KVM_S390_UCAS_MAP], arg ptr[in,
ioctl$KVM_S390_UCAS_UNMAP(fd fd_kvmcpu, cmd const[KVM_S390_UCAS_UNMAP], arg ptr[
ioctl$KVM_S390_VCPU_FAULT(fd fd_kvmcpu, cmd const[KVM_S390_VCPU_FAULT], arg ptr[
ioctl$KVM_SET_ONE_REG(fd fd_kvmcpu, cmd const[KVM_SET_ONE_REG], arg ptr[in, kvm_
ioctl$KVM_GET_ONE_REG(fd fd_kvmcpu, cmd const[KVM_GET_ONE_REG], arg ptr[in, kvm_
ioctl$KVM_KVMCLOCK_CTRL(fd fd_kvmcpu, cmd const[KVM_KVMCLOCK_CTRL])
/tmp/desc.txt Mon Jan 17 05:46:11 2022 3
ioctl$KVM_S390_INTERRUPT_CPU(fd fd_kvmcpu, cmd const[KVM_S390_INTERRUPT], arg pt
ioctl$KVM_GET_REG_LIST(fd fd_kvmcpu, cmd const[KVM_GET_REG_LIST], arg ptr[in, kv
ioctl$KVM_SET_GUEST_DEBUG(fd fd_kvmcpu, cmd const[KVM_SET_GUEST_DEBUG], arg ptr[
ioctl$KVM_SMI(fd fd_kvmcpu, cmd const[KVM_SMI])
ioctl$KVM_TPR_ACCESS_REPORTING(fd fd_kvmcpu, cmd const[KVM_TPR_ACCESS_REPORTING]
ioctl$KVM_SET_VAPIC_ADDR(fd fd_kvmcpu, cmd const[KVM_SET_VAPIC_ADDR], arg ptr[in
ioctl$KVM_X86_SETUP_MCE(fd fd_kvmcpu, cmd const[KVM_X86_SETUP_MCE], arg ptr[in,
ioctl$KVM_X86_SET_MCE(fd fd_kvmcpu, cmd const[KVM_X86_SET_MCE], arg ptr[in, kvm_
ioctl$KVM_ARM_VCPU_INIT(fd fd_kvmcpu, cmd const[KVM_ARM_VCPU_INIT], arg ptr[in,
ioctl$KVM_ARM_SET_DEVICE_ADDR(fd fd_kvmcpu, cmd const[KVM_ARM_SET_DEVICE_ADDR],
ioctl$KVM_GET_NESTED_STATE(fd fd_kvmcpu, cmd const[KVM_GET_NESTED_STATE], arg pt
ioctl$KVM_SET_NESTED_STATE(fd fd_kvmcpu, cmd const[KVM_SET_NESTED_STATE], arg pt
# NEED: arch constraints for syscalls. These are amd64/386-specific, but consts
ioctl$KVM_CAP_HYPERV_SYNIC(fd fd_kvmcpu, cmd const[KVM_ENABLE_CAP], arg ptr[in,
ioctl$KVM_CAP_HYPERV_SYNIC2(fd fd_kvmcpu, cmd const[KVM_ENABLE_CAP], arg ptr[in,
ioctl$KVM_CAP_HYPERV_ENLIGHTENED_VMCS(fd fd_kvmcpu, cmd const[KVM_ENABLE_CAP], a
ioctl$KVM_CAP_HYPERV_DIRECT_TLBFLUSH(fd fd_kvmcpu, cmd const[KVM_ENABLE_CAP], ar
ioctl$KVM_CAP_HYPERV_ENFORCE_CPUID(fd fd_kvmcpu, cmd const[KVM_ENABLE_CAP], arg
ioctl$KVM_CAP_ENFORCE_PV_FEATURE_CPUID(fd fd_kvmcpu, cmd const[KVM_ENABLE_CAP],
ioctl$KVM_SET_DEVICE_ATTR(fd fd_kvmdev, cmd const[KVM_SET_DEVICE_ATTR], arg ptr[
ioctl$KVM_GET_DEVICE_ATTR(fd fd_kvmdev, cmd const[KVM_GET_DEVICE_ATTR], arg ptr[
ioctl$KVM_HAS_DEVICE_ATTR(fd fd_kvmdev, cmd const[KVM_HAS_DEVICE_ATTR], arg ptr[
kvm_mem_region_flags = KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY
kvm_mp_state = KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_UNINITIALIZED, KVM_MP_STATE_I
kvm_assigned_irq_flags = KVM_DEV_IRQ_HOST_INTX, KVM_DEV_IRQ_HOST_MSI, KVM_DEV_IR
kvm_irq_routing_entry_type = KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI, KVM_I
kvm_ioeventfd_flags = KVM_IOEVENTFD_FLAG_DATAMATCH, KVM_IOEVENTFD_FLAG_PIO, KVM_
kvm_ioeventfd_len = 0, 1, 2, 4, 8
kvm_device_type = KVM_DEV_TYPE_FSL_MPIC_20, KVM_DEV_TYPE_FSL_MPIC_42, KVM_DEV_TY
kvm_device_flags = 0, KVM_CREATE_DEVICE_TEST
kvm_guest_debug_flags = KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_GUESTD
kvm_chip_id = KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE, KVM_IRQCHIP_IOAPIC
kvm_cpu_function = 0, 1, 2, 4, 6, 7, 10, 11, 13, KVM_CPUID_SIGNATURE, KVM_CPUID_
kvm_guest_selector = 0, 3, 4, 8, 9, 10, 11, 12, 13, 14, 15, 16
kvm_mce_flags = 1, 2, 4
kvm_mcg_status = MCG_STATUS_RIPV, MCG_STATUS_EIPV, MCG_STATUS_MCIP, MCG_STATUS_L
kvm_mce_status = MCI_STATUS_VAL, MCI_STATUS_OVER, MCI_STATUS_UC, MCI_STATUS_EN,
kvm_cpuid_flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, KVM_CPUID_FLAG_STATEFUL_FUNC,
kvm_dev_flags = KVM_DEV_ASSIGN_ENABLE_IOMMU, KVM_DEV_ASSIGN_PCI_2_3, KVM_DEV_ASS
kvm_vcpu_target = KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARG
kvm_vcpu_features_arm64 = KVM_ARM_VCPU_POWER_OFF, KVM_ARM_VCPU_EL1_32BIT, KVM_AR
kvm_dirty_log_protect = KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE, KVM_DIRTY_LOG_INITI
kvm_dirty_log_sizes = 4096, 8192, 16384, 32768, 65536
kvm_x86_quirks = KVM_X86_QUIRK_LINT0_REENABLED, KVM_X86_QUIRK_CD_NW_CLEARED, KVM
kvm_x2apic_apis = KVM_X2APIC_API_USE_32BIT_IDS, KVM_X2APIC_API_DISABLE_BROADCAST
kvm_x86_exits = KVM_X86_DISABLE_EXITS_MWAIT, KVM_X86_DISABLE_EXITS_HLT, KVM_X86_
kvm_msr_exit_reasons = KVM_MSR_EXIT_REASON_INVAL, KVM_MSR_EXIT_REASON_UNKNOWN, K
kvm_bus_lock_exits = KVM_BUS_LOCK_DETECTION_OFF, KVM_BUS_LOCK_DETECTION_EXIT
kvm_hypercall_exits = KVM_HC_MAP_GPA_RANGE
kvm_mem_slots = 0, 1, 2, 3, 4, 5, 509, 510, 511, 10000, 65536, 65537, 65538, 655
kvm_guest_addr_size = 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x100000
kvm_x86_tss_addr = 0xd000
kvm_x86_cr0 = 1, 2, 4, 8, 16, 32, 65536, 262144, 536870912, 1073741824, 21474836
kvm_x86_cr4 = 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 8192, 16384, 65536, 1
kvm_x86_efer = 1, 256, 1024, 2048, 4096, 8192, 16384, 32768
kvm_x86_dr7 = 1, 2, 4, 8, 16, 32, 64, 128
kvm_x86_rflags = 1, 2, 4, 16, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
/tmp/desc.txt Mon Jan 17 05:46:11 2022 4
# Pseudo call that setups VCPU into a reasonable interesting state for execution
# The interface is designed for extensibility so that addition of new options do
syz_kvm_setup_cpu$x86(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in
syz_kvm_setup_cpu$arm64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[
syz_kvm_setup_cpu$ppc64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[
resource kvm_run_ptr[int64]
define KVM_RUN_SIZE sizeof(struct kvm_run)
mmap$KVM_VCPU(addr vma, len const[KVM_RUN_SIZE], prot flags[mmap_prot], flags fl
_ = __NR_mmap2
define KVM_EXIT_MMIO_OFFSET offsetof(struct kvm_run, mmio)
define KVM_EXIT_MMIO_SIZE sizeof_field(struct kvm_run, mmio)
syz_memcpy_off$KVM_EXIT_MMIO(dst kvm_run_ptr, off const[KVM_EXIT_MMIO_OFFSET], s
define KVM_EXIT_HYPERCALL_OFFSET offsetof(struct kvm_run, hypercall)
define KVM_EXIT_HYPERCALL_SIZE sizeof_field(struct kvm_run, hypercall)
syz_memcpy_off$KVM_EXIT_HYPERCALL(dst kvm_run_ptr, off const[KVM_EXIT_HYPERCALL_
kvm_text_x86 [
 textreal kvm_text_x86_real
 text16 kvm_text_x86_16
 text32 kvm_text_x86_32
 text64 kvm_text_x86_64
]
kvm_text_x86_real {
 typ const[8, intptr]
 text ptr[in, text[x86_real]]
 size len[text, intptr]
}
kvm_text_x86_16 {
 typ const[16, intptr]
 text ptr[in, text[x86_16]]
 size len[text, intptr]
}
kvm_text_x86_32 {
 typ const[32, intptr]
 text ptr[in, text[x86_32]]
 size len[text, intptr]
}
kvm_text_x86_64 {
 typ const[64, intptr]
 text ptr[in, text[x86_64]]
 size len[text, intptr]
}
kvm_text_arm64 {
 typ const[0, intptr]
 text ptr[in, text[arm64]]
 size len[text, intptr]
}
kvm_text_ppc64 {
 typ const[0, intptr]
 text ptr[in, text[ppc64]]
 size len[text, intptr]
}
kvm_setup_opt_x86 [
/tmp/desc.txt Mon Jan 17 05:46:11 2022 5
 cr0 kvm_setup_opt_cr0
 cr4 kvm_setup_opt_cr4
 efer kvm_setup_opt_efer
 flags kvm_setup_opt_flags
 cstype0 kvm_setup_opt_cstype0
 cstype3 kvm_setup_opt_cstype3
 dstype0 kvm_setup_opt_dstype0
 dstype3 kvm_setup_opt_dstype3
 vmwrite kvm_setup_opt_vmwrite
]
kvm_setup_opt_cr0 {
 typ const[0, int64]
 val flags[kvm_x86_cr0, int64]
}
kvm_setup_opt_cr4 {
 typ const[1, int64]
 val flags[kvm_x86_cr4, int64]
}
kvm_setup_opt_efer {
 typ const[2, int64]
 val flags[kvm_x86_efer, int64]
}
kvm_setup_opt_flags {
 typ const[3, int64]
 val flags[kvm_x86_rflags, int64]
}
kvm_setup_opt_cstype0 {
 typ const[4, int64]
 val int64[0:15]
}
kvm_setup_opt_cstype3 {
 typ const[5, int64]
 val int64[0:15]
}
kvm_setup_opt_dstype0 {
 typ const[6, int64]
 val int64[0:15]
}
kvm_setup_opt_dstype3 {
 typ const[7, int64]
 val int64[0:15]
}
kvm_setup_opt_vmwrite {
 typ const[8, int64]
# Low 16 bits are field index, high 48 bits are value.
 sz const[0, int64:1]
 fld int64:5
 pad0 const[0, int64:4]
 ftyp int64:2
 pad1 const[0, int64:1]
 fsz int64:2
 pad2 const[0, int64:1]
 val int64:48
}
/tmp/desc.txt Mon Jan 17 05:46:11 2022 6
kvm_setup_opt_arm64 [
# unions need at least 2 fields, but we have only 1 now, but we want to have it
 featur1 kvm_setup_opt_feature
 featur2 kvm_setup_opt_feature
]
kvm_setup_opt_feature {
 typ const[1, int64]
 val flags[kvm_vcpu_features_arm64, int64]
}
kvm_setup_opt_ppc64 [
# unions need at least 2 fields, but we have only 1 now, but we want to have it
 featur1 kvm_setup_opt_ppc64_feature
 featur2 kvm_setup_opt_ppc64_feature
]
kvm_setup_opt_ppc64_feature {
 typ const[1, int64]
 val int64
}
kvm_setup_flags = KVM_SETUP_PAGING, KVM_SETUP_PAE, KVM_SETUP_PROTECTED, KVM_SETU
define KVM_SETUP_PAGING (1<<0)
define KVM_SETUP_PAE (1<<1)
define KVM_SETUP_PROTECTED (1<<2)
define KVM_SETUP_CPL3 (1<<3)
define KVM_SETUP_VIRT86 (1<<4)
define KVM_SETUP_SMM (1<<5)
define KVM_SETUP_VM (1<<6)
kvm_setup_flags_ppc64 = KVM_SETUP_PPC64_LE, KVM_SETUP_PPC64_IR, KVM_SETUP_PPC64_
# Little endian
define KVM_SETUP_PPC64_LE (1<<0)
# Paging for instructions
define KVM_SETUP_PPC64_IR (1<<1)
# Paging for data
define KVM_SETUP_PPC64_DR (1<<2)
# Run with MSR_PR (==usermode)
define KVM_SETUP_PPC64_PR (1<<3)
# Set PID=1 i.e. not kernel’s PID
define KVM_SETUP_PPC64_PID1 (1<<4)
kvm_guest_debug {
 ctrl flags[kvm_guest_debug_flags, int32]
 pad const[0, int32]
 reg array[int64, 8]
}
kvm_arm_device_addr {
 id int64
 addr flags[kvm_guest_addrs, int64]
}
kvm_reg_list {
 n len[reg, int64]
 reg array[int64]
}
kvm_device_attr {
/tmp/desc.txt Mon Jan 17 05:46:11 2022 7
 flags const[0, int32]
 group int32
 attr int64
 addr ptr64[in, int64]
}
kvm_create_device {
 type flags[kvm_device_type, int32] (in)
 fd fd_kvmdev (out)
 flags flags[kvm_device_flags, int32] (in)
}
kvm_s390_interrupt {
 type int32
 parm int32
 parm64 int64
}
kvm_irqfd {
 fd fd_event
 gsi int32
 flags int32
 rfd fd_event
 pad array[const[0, int8], 16]
}
kvm_pit_state2 {
 chans array[kvm_pit_channel_state, 3]
 flags int32
 pad array[const[0, int32], 9]
}
kvm_pit_channel_state {
 count int32
 lcount int16
 latched int8
 lstatus int8
 status int8
 rstate int8
 wstate int8
 wlatch int8
 rw int8
 mode int8
 bcd int8
 gate int8
 ltime int64
}
kvm_pit_config {
 flags int32
 pad array[const[0, int32], 15]
}
kvm_msi {
 addrlo flags[kvm_guest_addrs, int32]
 addrhi flags[kvm_guest_addrs, int32]
 data int32
 flags int32
 devid int32
 pad array[const[0, int8], 12]
}
kvm_one_reg {
/tmp/desc.txt Mon Jan 17 05:46:11 2022 8
 id int64
 addr int64
}
kvm_s390_ucas_mapping {
 uaddr int64
 vaddr int64
 len int64
}
kvm_dirty_tlb {
 bitmap int64
 n int32
}
kvm_ioeventfd {
 datam flags[kvm_guest_addrs, int64]
 addr ptr64[out, int64]
 len flags[kvm_ioeventfd_len, int32]
 fd fd_event
 flags flags[kvm_ioeventfd_flags, int32]
 pad array[const[0, int8], 36]
}
kvm_lapic_state {
 regs array[int8, 1024]
}
kvm_assigned_msix_entry {
 devid int32
 gsi int32
 entry int16
 padding array[const[0, int16], 3]
}
kvm_assigned_msix_nr {
 devid int32
 entnr int16
}
kvm_irq_routing {
 nr len[entries, int32]
 flags const[0, int32]
 entries array[kvm_irq_routing_entry]
}
kvm_irq_routing_entry {
 gsi int32
 type flags[kvm_irq_routing_entry_type, int32]
 flags const[0, int32]
 pad const[0, int32]
 u kvm_irq_routing_entry_u
}
kvm_irq_routing_entry_u [
 irqchip kvm_irq_routing_irqchip
 msi kvm_irq_routing_msi
 adapter kvm_irq_routing_s390_adapter
 sint kvm_irq_routing_hv_sint
]
kvm_irq_routing_irqchip {
 irqchip int32
/tmp/desc.txt Mon Jan 17 05:46:11 2022 9
 pin int32
}
kvm_irq_routing_msi {
 address_lo int32
 address_hi int32
 data int32
 devid int32
}
kvm_irq_routing_s390_adapter {
 indaddr int64
 saddr int64
 indoff int64
 soff int32
 aid int32
}
kvm_irq_routing_hv_sint {
 vcpu int32
 sint int32
}
kvm_assigned_irq {
 assigned_dev_id int32
 host_irq const[0, int32]
 guest_irq int32
 flags flags[kvm_assigned_irq_flags, int32]
 reserved array[const[0, int32], 12]
}
kvm_assigned_pci_dev {
 devid int32
 busnr int32
 devfn int32
 flags flags[kvm_dev_flags, int32]
 segnr int32
}
kvm_xcr {
 xcr int32
 reserv const[0, int32]
 val int64
}
kvm_xcrs {
 nr len[xcrs, int32]
 flags int32
 xcrs array[kvm_xcr]
}
kvm_xsave {
 region array[int32, 1024]
}
type kvm_enable_cap[CAP, ARGS] {
 cap const[CAP, int32]
 flags const[0, int32]
 args ARGS
} [align[8], size[KVM_ENABLE_CAP_SIZE]]
define KVM_ENABLE_CAP_SIZE sizeof(struct kvm_enable_cap)
/tmp/desc.txt Mon Jan 17 05:46:11 2022 10
kvm_userspace_memory_region {
 slot flags[kvm_mem_slots, int32]
 flags flags[kvm_mem_region_flags, int32]
 paddr flags[kvm_guest_addrs, int64]
 size len[addr, int64]
 addr vma64[1:2]
}
kvm_vcpu_events {
 exinjec int8
 exnr int8
 exhec int8
 pad1 const[0, int8]
 exec int32
 ininjec int8
 innr int8
 insoft int8
 inshad int8
 nmiinj int8
 nmipend int8
 nmimask int8
 pad2 const[0, int8]
 sipi_vector int32
 flags int32
 smismm int8
 smipend int8
 smiinsi int8
 smilatc int8
 reserved array[const[0, int8], 27]
 exception_has_payload int8
 exception_payload int64
}
kvm_clock_data {
 clock int64
 flags int32
 pad0 int32
 realtime int64
 host_tsc int64
 pad array[const[0, int32], 4]
}
kvm_xen_hvm_config {
 flags int32
 msr flags[msr_index, int32]
 addr32 ptr64[in, array[int8]]
 addr64 ptr64[in, array[int8]]
 size32 len[addr32, int8]
 size64 len[addr64, int8]
 pad array[const[0, int8], 30]
}
kvm_irq_level {
 irq int32
 level int32
}
kvm_signal_mask {
/tmp/desc.txt Mon Jan 17 05:46:11 2022 11
 len len[sigset, int32]
 sigset array[int8]
}
kvm_cpuid_entry {
 func flags[kvm_cpu_function, int32]
 eax int32
 ebx int32
 ecx int32
 edx int32
 pad const[0, int32]
}
kvm_cpuid {
 n len[entries, int32]
 pad const[0, int32]
 entries array[kvm_cpuid_entry]
}
kvm_cpuid_entry2 {
 func flags[kvm_cpu_function, int32]
 index int32
 flags flags[kvm_cpuid_flags, int32]
 eax int32
 ebx int32
 ecx int32
 edx int32
 pad array[const[0, int32], 3]
}
kvm_cpuid2 {
 n len[entries, int32]
 pad const[0, int32]
 entries array[kvm_cpuid_entry2]
}
kvm_translation {
 laddr flags[kvm_guest_addrs, int64]
 paddr flags[kvm_guest_addrs, int64]
 valid int8
 writeable int8
 usermode int8
 pad array[const[0, int8], 5]
}
kvm_dirty_log {
 slot flags[kvm_mem_slots, int32]
 pad const[0, int32]
 bitmap vma64
}
kvm_msr_list {
 n len[indices, int32]
 indices array[const[0, int32]]
}
kvm_regs {
 gp array[int64, 16]
 rip flags[kvm_guest_addrs, int64]
 rflags flags[kvm_x86_rflags, int64]
}
kvm_sregs {
/tmp/desc.txt Mon Jan 17 05:46:11 2022 12
 cs kvm_segment
 ds kvm_segment
 es kvm_segment
 fs kvm_segment
 gs kvm_segment
 ss kvm_segment
 tr kvm_segment
 ldt kvm_segment
 gdt kvm_dtable
 idt kvm_dtable
 cr0 flags[kvm_x86_cr0, int64]
 cr2 const[0, int64]
# TODO: this should point to page table
 cr3 flags[kvm_guest_addrs, int64]
 cr4 flags[kvm_x86_cr4, int64]
 cr8 int64[0:15]
 efer flags[kvm_x86_efer, int64]
 apic flags[kvm_guest_addrs, int64]
 intr array[int64, 4]
}
kvm_segment {
 base flags[kvm_guest_addrs, int64]
 limit flags[kvm_guest_addrs, int32]
 select flags[kvm_guest_selector, int16]
 type int8
 present int8
 dpl int8
 db int8
 s int8
 l int8
 g int8
 avl int8
 unusabl int8
 padding const[0, int8]
}
kvm_dtable {
 base flags[kvm_guest_addrs, int64]
 limit int16
 pad array[const[0, int16], 3]
}
kvm_fpu {
 fpr array[const[0, int64], 16]
 fcw int16
 fsw int16
 ftws int8
 pad1 const[0, int8]
 last_opcode int16
 last_ip flags[kvm_guest_addrs, int64]
 last_dp flags[kvm_guest_addrs, int64]
 xmm array[const[0, int64], 32]
 mxcsr int32
 pad2 const[0, int32]
}
kvm_debugregs {
 db array[flags[kvm_guest_addrs, int64], 4]
 dr6 int64
 dr7 flags[kvm_x86_dr7, int64]
 flags int64
 reserv array[const[0, int64], 9]
/tmp/desc.txt Mon Jan 17 05:46:11 2022 13
}
kvm_msrs {
 nmsrs len[entries, int32]
 pad const[0, int32]
 entries array[kvm_msr_entry]
} [packed]
kvm_msr_entry {
 index flags[msr_index, int32]
 reserv const[0, int32]
 data int64
}
kvm_irqchip {
 chipid flags[kvm_chip_id, int32]
 pad const[0, int32]
 chip kvm_irq_chip
}
kvm_irq_chip [
 pic kvm_pic_state
 ioapic kvm_ioapic_state
] [size[512]]
kvm_pic_state {
 lastirr int8
 irr int8
 imr int8
 isr int8
 padd int8
 irqbase int8
 readreg int8
 poll int8
 special int8
 initst int8
 autoeoi int8
 rotate int8
 nestedm int8
 init4 int8
 elcr int8
 elcrmas int8
}
kvm_ioapic_state {
 base flags[kvm_guest_addrs, int64]
 ioregs int32
 id int32
 irr int32
 pad const[0, int32]
 redir array[kvm_ioapic_redir, 24]
}
kvm_ioapic_redir {
 vector int8
 f0 int8
 f1 int8
 reserv array[const[0, int8], 4]
 destid int8
}
kvm_tpr_access_ctl {
 enabled int32
/tmp/desc.txt Mon Jan 17 05:46:11 2022 14
 flags int32
 reserv array[const[0, int32], 8]
}
kvm_mce_cap {
 banks int8[0:32]
 flags flags[kvm_mce_flags, int8]
 count int8
 pad const[0, int8]
}
kvm_x86_mce {
 status flags[kvm_mce_status, int64]
 addr flags[kvm_guest_addrs, int64]
 misc int64
 mcg flags[kvm_mcg_status, int64]
 bank int8[0:32]
 pad1 array[const[0, int8], 7]
 pad2 array[const[0, int64], 3]
}
kvm_reinject_control {
 reinjec int8
 reserv array[const[0, int8], 31]
}
kvm_coalesced_mmio_zone {
 addr flags[kvm_guest_addrs, int64]
 size flags[kvm_guest_addr_size, int32]
 pad const[0, int32]
}
kvm_vcpu_init {
 target flags[kvm_vcpu_target, int32]
 feature flags[kvm_vcpu_features_arm64, int32]
 pad array[const[0, int32], 6]
}
kvm_hyperv_eventfd {
 conn_id int32[0:4]
 fd fd_event
 flags bool32
 padding array[const[0, int32], 3]
}
kvm_nested_state {
 flags flags[kvm_nested_state_flags, int16]
 format const[0, int16]
 size bytesize[parent, int32]
 hdr kvm_vmx_nested_state
 data void
}
kvm_nested_state_arg {
 state kvm_nested_state
 current_vmcs array[int8, VMCS12_SIZE]
 shadow_vmcs array[int8, VMCS12_SIZE]
}
kvm_vmx_nested_state {
 vmxon_pa flags[kvm_guest_addrs, int64]
 vmcs_pa flags[kvm_guest_addrs, int64]
 smm_flags flags[kvm_nested_smm_flags, int16]
/tmp/desc.txt Mon Jan 17 05:46:11 2022 15
} [size[120]]
kvm_nested_state_flags = KVM_STATE_NESTED_GUEST_MODE, KVM_STATE_NESTED_RUN_PENDI
kvm_nested_smm_flags = KVM_STATE_NESTED_SMM_GUEST_MODE, KVM_STATE_NESTED_SMM_VMX
# pkg/ifuzz/pseudo.go also knows this list
msr_index = 0x0, 0x1, 0x10, 0x11, 0x12, 0x13, 0x17, 0x1b, 0x20, 0x21, 0x28, 0x29
define VMCS12_SIZE 0x1000
# Code generated by syz-sysgen. DO NOT EDIT.
arches = 386, amd64, arm, arm64, mips64le, ppc64le, riscv64, s390x
AT_FDCWD = 18446744073709551516, arm:riscv64:???
KVM_ARM_SET_DEVICE_ADDR = 1074835115, arm:riscv64:???, mips64le:ppc64le:21485769
KVM_ARM_TARGET_AEM_V8 = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, arm64:
KVM_ARM_TARGET_CORTEX_A53 = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, ar
KVM_ARM_TARGET_CORTEX_A57 = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, ar
KVM_ARM_TARGET_FOUNDATION_V8 = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???,
KVM_ARM_TARGET_GENERIC_V8 = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, ar
KVM_ARM_TARGET_XGENE_POTENZA = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???,
KVM_ARM_VCPU_EL1_32BIT = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, arm64
KVM_ARM_VCPU_INIT = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, arm64:1075
KVM_ARM_VCPU_PMU_V3 = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, arm64:3
KVM_ARM_VCPU_POWER_OFF = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, arm64
KVM_ARM_VCPU_PSCI_0_2 = 386:amd64:arm:mips64le:ppc64le:riscv64:s390x:???, arm64:
KVM_ASSIGN_DEV_IRQ = 1077980784, arm:riscv64:???, mips64le:ppc64le:2151722608
KVM_ASSIGN_PCI_DEVICE = 2151722601, arm:riscv64:???, mips64le:ppc64le:1077980777
KVM_ASSIGN_SET_INTX_MASK = 1077980836, arm:riscv64:???, mips64le:ppc64le:2151722
KVM_ASSIGN_SET_MSIX_ENTRY = 1074835060, arm:riscv64:???, mips64le:ppc64le:214857
KVM_ASSIGN_SET_MSIX_NR = 1074310771, arm:riscv64:???, mips64le:ppc64le:214805259
KVM_BUS_LOCK_DETECTION_EXIT = 2, arm:riscv64:???
KVM_BUS_LOCK_DETECTION_OFF = 1, arm:riscv64:???
KVM_CAP_DIRTY_LOG_RING = 192, arm:riscv64:???
KVM_CAP_DISABLE_QUIRKS = 116, arm:riscv64:???
KVM_CAP_ENFORCE_PV_FEATURE_CPUID = 190, arm:riscv64:???
KVM_CAP_EXCEPTION_PAYLOAD = 164, arm:riscv64:???
KVM_CAP_EXIT_HYPERCALL = 201, arm:riscv64:???
KVM_CAP_EXIT_ON_EMULATION_FAILURE = 204, arm:riscv64:???
KVM_CAP_HALT_POLL = 182, arm:riscv64:???
KVM_CAP_HYPERV_DIRECT_TLBFLUSH = 175, arm:riscv64:???
KVM_CAP_HYPERV_ENFORCE_CPUID = 199, arm:riscv64:???
KVM_CAP_HYPERV_ENLIGHTENED_VMCS = 163, arm:riscv64:???
KVM_CAP_HYPERV_SYNIC = 123, arm:riscv64:???
KVM_CAP_HYPERV_SYNIC2 = 148, arm:riscv64:???
KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 = 168, arm:riscv64:???
KVM_CAP_MSR_PLATFORM_INFO = 159, arm:riscv64:???
KVM_CAP_SGX_ATTRIBUTE = 196, arm:riscv64:???
KVM_CAP_SPLIT_IRQCHIP = 121, arm:riscv64:???
KVM_CAP_VM_COPY_ENC_CONTEXT_FROM = 197, arm:riscv64:???
KVM_CAP_X2APIC_API = 129, arm:riscv64:???
KVM_CAP_X86_BUS_LOCK_EXIT = 193, arm:riscv64:???
KVM_CAP_X86_DISABLE_EXITS = 143, arm:riscv64:???
KVM_CAP_X86_USER_SPACE_MSR = 188, arm:riscv64:???
KVM_CHECK_EXTENSION = 44547, arm:riscv64:???, mips64le:ppc64le:536915459
KVM_CPUID_FEATURES = 1073741825, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_CPUID_FLAG_SIGNIFCANT_INDEX = 1, arm:arm64:mips64le:ppc64le:riscv64:s390x:??
KVM_CPUID_FLAG_STATEFUL_FUNC = 2, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_CPUID_FLAG_STATE_READ_NEXT = 4, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_CPUID_SIGNATURE = 1073741824, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_CREATE_DEVICE = 3222056672, arm:riscv64:???
KVM_CREATE_DEVICE_TEST = 1, arm:riscv64:???
KVM_CREATE_IRQCHIP = 44640, arm:riscv64:???, mips64le:ppc64le:536915552
KVM_CREATE_PIT2 = 1077980791, arm:riscv64:???, mips64le:ppc64le:2151722615
KVM_CREATE_VCPU = 44609, arm:riscv64:???, mips64le:ppc64le:536915521
/tmp/desc.txt Mon Jan 17 05:46:11 2022 16
KVM_CREATE_VM = 44545, arm:riscv64:???, mips64le:ppc64le:536915457
KVM_DEASSIGN_DEV_IRQ = 1077980789, arm:riscv64:???, mips64le:ppc64le:2151722613
KVM_DEASSIGN_PCI_DEVICE = 1077980786, arm:riscv64:???, mips64le:ppc64le:21517226
KVM_DEV_ASSIGN_ENABLE_IOMMU = 1, arm:riscv64:???
KVM_DEV_ASSIGN_MASK_INTX = 4, arm:riscv64:???
KVM_DEV_ASSIGN_PCI_2_3 = 2, arm:riscv64:???
KVM_DEV_IRQ_GUEST_INTX = 256, arm:riscv64:???
KVM_DEV_IRQ_GUEST_MSI = 512, arm:riscv64:???
KVM_DEV_IRQ_GUEST_MSIX = 1024, arm:riscv64:???
KVM_DEV_IRQ_HOST_INTX = 1, arm:riscv64:???
KVM_DEV_IRQ_HOST_MSI = 2, arm:riscv64:???
KVM_DEV_IRQ_HOST_MSIX = 4, arm:riscv64:???
KVM_DEV_TYPE_FLIC = 6, arm:riscv64:???
KVM_DEV_TYPE_FSL_MPIC_20 = 1, arm:riscv64:???
KVM_DEV_TYPE_FSL_MPIC_42 = 2, arm:riscv64:???
KVM_DEV_TYPE_VFIO = 4, arm:riscv64:???
KVM_DEV_TYPE_XICS = 3, arm:riscv64:???
KVM_DIRTY_LOG_INITIALLY_SET = 2, arm:riscv64:???
KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE = 1, arm:riscv64:???
KVM_DIRTY_TLB = 1074835114, 386:1074572970, arm:riscv64:???, mips64le:ppc64le:21
KVM_ENABLE_CAP = 1080602275, arm:riscv64:???, mips64le:ppc64le:2154344099
KVM_ENABLE_CAP_SIZE = 104, arm:riscv64:???
KVM_EXIT_HYPERCALL_OFFSET = 32, arm:riscv64:???, s390x:48
KVM_EXIT_HYPERCALL_SIZE = 72, arm:riscv64:???
KVM_EXIT_MMIO_OFFSET = 32, arm:riscv64:???, s390x:48
KVM_EXIT_MMIO_SIZE = 24, arm:riscv64:???
KVM_GET_API_VERSION = 44544, arm:riscv64:???, mips64le:ppc64le:536915456
KVM_GET_CLOCK = 2150674044, arm:riscv64:???, mips64le:ppc64le:1076932220
KVM_GET_CPUID2 = 3221794449, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_DEBUGREGS = 2155916961, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_DEVICE_ATTR = 1075359458, arm:riscv64:???, mips64le:ppc64le:2149101282
KVM_GET_DIRTY_LOG = 1074835010, arm:riscv64:???, mips64le:ppc64le:2148576834
KVM_GET_EMULATED_CPUID = 3221794313, arm:arm64:mips64le:ppc64le:riscv64:s390x:??
KVM_GET_FPU = 2174791308, arm:riscv64:???, arm64:2147528332, mips64le:1073786508
KVM_GET_IRQCHIP = 3255348834, arm:riscv64:???
KVM_GET_LAPIC = 2214637198, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_MP_STATE = 2147790488, arm:riscv64:???, mips64le:ppc64le:1074048664
KVM_GET_MSRS = 3221794440, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_MSR_INDEX_LIST = 3221532162, arm:arm64:mips64le:ppc64le:riscv64:s390x:??
KVM_GET_NESTED_STATE = 3229658814, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_NR_MMU_PAGES = 44613, arm:riscv64:???, mips64le:ppc64le:536915525
KVM_GET_ONE_REG = 1074835115, arm:riscv64:???, mips64le:ppc64le:2148576939
KVM_GET_PIT = 3225988709, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_PIT2 = 2154868383, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_REGS = 2156965505, arm:riscv64:???, arm64:2204151425, mips64le:109213657
KVM_GET_REG_LIST = 3221794480, arm:riscv64:???
KVM_GET_SREGS = 2167975555, arm:riscv64:???, arm64:2147528323, mips64le:10737864
KVM_GET_SUPPORTED_CPUID = 3221794309, arm:arm64:mips64le:ppc64le:riscv64:s390x:?
KVM_GET_TSC_KHZ = 44707, arm:riscv64:???, mips64le:ppc64le:536915619
KVM_GET_VCPU_EVENTS = 2151722655, arm:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_VCPU_MMAP_SIZE = 44548, arm:riscv64:???, mips64le:ppc64le:536915460
KVM_GET_XCRS = 2173218470, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GET_XSAVE = 2415963812, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GUESTDBG_ENABLE = 1, arm:riscv64:???
KVM_GUESTDBG_INJECT_BP = 524288, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GUESTDBG_INJECT_DB = 262144, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_GUESTDBG_SINGLESTEP = 2, arm:riscv64:???
KVM_GUESTDBG_USE_HW_BP = 131072, arm:arm64:mips64le:riscv64:???, s390x:65536
KVM_GUESTDBG_USE_SW_BP = 65536, arm:mips64le:riscv64:s390x:???
KVM_HAS_DEVICE_ATTR = 1075359459, arm:riscv64:???, mips64le:ppc64le:2149101283
KVM_HC_MAP_GPA_RANGE = 12, arm:riscv64:???
KVM_HYPERV_EVENTFD = 1075359421, arm:riscv64:???, mips64le:ppc64le:2149101245
KVM_INTERRUPT = 1074048646, arm:riscv64:???, mips64le:ppc64le:2147790470
/tmp/desc.txt Mon Jan 17 05:46:11 2022 17
KVM_IOEVENTFD = 1077980793, arm:riscv64:???, mips64le:ppc64le:2151722617
KVM_IOEVENTFD_FLAG_DATAMATCH = 1, arm:riscv64:???
KVM_IOEVENTFD_FLAG_DEASSIGN = 4, arm:riscv64:???
KVM_IOEVENTFD_FLAG_PIO = 2, arm:riscv64:???
KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY = 8, arm:riscv64:???
KVM_IRQCHIP_IOAPIC = 2, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_IRQCHIP_PIC_MASTER = 0, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_IRQCHIP_PIC_SLAVE = 1, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_IRQFD = 1075883638, arm:riscv64:???, mips64le:ppc64le:2149625462
KVM_IRQ_LINE = 1074310753, arm:riscv64:???, mips64le:ppc64le:2148052577
KVM_IRQ_LINE_STATUS = 3221794407, arm:riscv64:???
KVM_IRQ_ROUTING_HV_SINT = 4, arm:riscv64:???
KVM_IRQ_ROUTING_IRQCHIP = 1, arm:riscv64:???
KVM_IRQ_ROUTING_MSI = 2, arm:riscv64:???
KVM_IRQ_ROUTING_S390_ADAPTER = 3, arm:riscv64:???
KVM_KVMCLOCK_CTRL = 44717, arm:riscv64:???, mips64le:ppc64le:536915629
KVM_MAX_IRQ_ROUTES = 4096, 386:amd64:arm:mips64le:ppc64le:riscv64:???
KVM_MEM_LOG_DIRTY_PAGES = 1, arm:riscv64:???
KVM_MEM_READONLY = 2, arm:riscv64:???
KVM_MP_STATE_CHECK_STOP = 6, arm:riscv64:???
KVM_MP_STATE_HALTED = 3, arm:riscv64:???
KVM_MP_STATE_INIT_RECEIVED = 2, arm:riscv64:???
KVM_MP_STATE_LOAD = 8, arm:riscv64:???
KVM_MP_STATE_OPERATING = 7, arm:riscv64:???
KVM_MP_STATE_RUNNABLE = 0, arm:riscv64:???
KVM_MP_STATE_SIPI_RECEIVED = 4, arm:riscv64:???
KVM_MP_STATE_STOPPED = 5, arm:riscv64:???
KVM_MP_STATE_UNINITIALIZED = 1, arm:riscv64:???
KVM_MSR_EXIT_REASON_FILTER = 4, arm:riscv64:???
KVM_MSR_EXIT_REASON_INVAL = 1, arm:riscv64:???
KVM_MSR_EXIT_REASON_UNKNOWN = 2, arm:riscv64:???
KVM_NMI = 44698, arm:riscv64:???, mips64le:ppc64le:536915610
KVM_PPC_ALLOCATE_HTAB = 3221532327, arm:riscv64:???
KVM_PPC_GET_PVINFO = 1082175137, arm:riscv64:???, mips64le:ppc64le:2155916961
KVM_PPC_GET_SMMU_INFO = 2186325670, arm:riscv64:???, mips64le:ppc64le:1112583846
KVM_REGISTER_COALESCED_MMIO = 1074835047, arm:riscv64:???, mips64le:ppc64le:2148
KVM_REINJECT_CONTROL = 44657, arm:riscv64:???, mips64le:ppc64le:536915569
KVM_RUN = 44672, arm:riscv64:???, mips64le:ppc64le:536915584
KVM_RUN_SIZE = 2352, arm:riscv64:???, s390x:2368
KVM_S390_INTERRUPT = 1074835092, arm:riscv64:???, mips64le:ppc64le:2148576916
KVM_S390_UCAS_MAP = 1075359312, arm:riscv64:???, mips64le:ppc64le:2149101136
KVM_S390_UCAS_UNMAP = 1075359313, arm:riscv64:???, mips64le:ppc64le:2149101137
KVM_S390_VCPU_FAULT = 1074310738, 386:1074048594, arm:riscv64:???, mips64le:ppc6
KVM_SETUP_CPL3 = 8, arm:riscv64:???
KVM_SETUP_PAE = 2, arm:riscv64:???
KVM_SETUP_PAGING = 1, arm:riscv64:???
KVM_SETUP_PPC64_DR = 4, arm:riscv64:???
KVM_SETUP_PPC64_IR = 2, arm:riscv64:???
KVM_SETUP_PPC64_LE = 1, arm:riscv64:???
KVM_SETUP_PPC64_PID1 = 16, arm:riscv64:???
KVM_SETUP_PPC64_PR = 8, arm:riscv64:???
KVM_SETUP_PROTECTED = 4, arm:riscv64:???
KVM_SETUP_SMM = 32, arm:riscv64:???
KVM_SETUP_VIRT86 = 16, arm:riscv64:???
KVM_SETUP_VM = 64, arm:riscv64:???
KVM_SET_BOOT_CPU_ID = 44664, arm:riscv64:???, mips64le:ppc64le:536915576
KVM_SET_CLOCK = 1076932219, arm:riscv64:???, mips64le:ppc64le:2150674043
KVM_SET_CPUID = 1074310794, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_CPUID2 = 1074310800, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_DEBUGREGS = 1082175138, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_DEVICE_ATTR = 1075359457, arm:riscv64:???, mips64le:ppc64le:2149101281
KVM_SET_FPU = 1101049485, arm:riscv64:???, arm64:1073786509, mips64le:2147528333
KVM_SET_GSI_ROUTING = 1074310762, arm:riscv64:???, mips64le:ppc64le:2148052586
/tmp/desc.txt Mon Jan 17 05:46:11 2022 18
KVM_SET_GUEST_DEBUG = 1078505115, arm:riscv64:???, arm64:1107865243, mips64le:21
KVM_SET_IDENTITY_MAP_ADDR = 1074310728, arm:riscv64:???, mips64le:ppc64le:214805
KVM_SET_IRQCHIP = 2181607011, arm:riscv64:???, mips64le:ppc64le:1107865187
KVM_SET_LAPIC = 1140895375, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_MP_STATE = 1074048665, arm:riscv64:???, mips64le:ppc64le:2147790489
KVM_SET_MSRS = 1074310793, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_NESTED_STATE = 1082175167, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_NR_MMU_PAGES = 44612, arm:riscv64:???, mips64le:ppc64le:536915524
KVM_SET_ONE_REG = 1074835116, arm:riscv64:???, mips64le:ppc64le:2148576940
KVM_SET_PIT = 2152246886, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_PIT2 = 1081126560, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_REGS = 1083223682, arm:riscv64:???, arm64:1130409602, mips64le:216587840
KVM_SET_SIGNAL_MASK = 1074048651, arm:riscv64:???, mips64le:ppc64le:2147790475
KVM_SET_SREGS = 1094233732, arm:riscv64:???, arm64:1073786500, mips64le:21475283
KVM_SET_TSC_KHZ = 44706, arm:riscv64:???, mips64le:ppc64le:536915618
KVM_SET_TSS_ADDR = 44615, arm:riscv64:???, mips64le:ppc64le:536915527
KVM_SET_USER_MEMORY_REGION = 1075883590, arm:riscv64:???, mips64le:ppc64le:21496
KVM_SET_VAPIC_ADDR = 1074310803, arm:riscv64:???, mips64le:ppc64le:2148052627
KVM_SET_VCPU_EVENTS = 1077980832, arm:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_XCRS = 1099476647, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SET_XSAVE = 1342221989, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_SIGNAL_MSI = 1075883685, arm:riscv64:???, mips64le:ppc64le:2149625509
KVM_SMI = 44727, arm:riscv64:???, mips64le:ppc64le:536915639
KVM_STATE_NESTED_GUEST_MODE = 1, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_STATE_NESTED_RUN_PENDING = 2, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_STATE_NESTED_SMM_GUEST_MODE = 1, arm:arm64:mips64le:ppc64le:riscv64:s390x:??
KVM_STATE_NESTED_SMM_VMXON = 2, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_TPR_ACCESS_REPORTING = 3223891602, arm:riscv64:???
KVM_TRANSLATE = 3222843013, arm:riscv64:???
KVM_UNREGISTER_COALESCED_MMIO = 1074835048, arm:riscv64:???, mips64le:ppc64le:21
KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK = 2, arm:riscv64:???
KVM_X2APIC_API_USE_32BIT_IDS = 1, arm:riscv64:???
KVM_X86_DISABLE_EXITS_CSTATE = 8, arm:riscv64:???
KVM_X86_DISABLE_EXITS_HLT = 2, arm:riscv64:???
KVM_X86_DISABLE_EXITS_MWAIT = 1, arm:riscv64:???
KVM_X86_DISABLE_EXITS_PAUSE = 4, arm:riscv64:???
KVM_X86_GET_MCE_CAP_SUPPORTED = 2148052637, arm:riscv64:???, mips64le:ppc64le:10
KVM_X86_QUIRK_CD_NW_CLEARED = 2, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_X86_QUIRK_LAPIC_MMIO_HOLE = 4, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_X86_QUIRK_LINT0_REENABLED = 1, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT = 16, arm:arm64:mips64le:ppc64le:riscv64:s390
KVM_X86_QUIRK_OUT_7E_INC_RIP = 8, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_X86_SETUP_MCE = 1074310812, arm:riscv64:???, mips64le:ppc64le:2148052636
KVM_X86_SET_MCE = 1077980830, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
KVM_XEN_HVM_CONFIG = 1077456506, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
MCG_STATUS_EIPV = 2, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
MCG_STATUS_LMCES = 8, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
MCG_STATUS_MCIP = 4, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
MCG_STATUS_RIPV = 1, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
MCI_STATUS_ADDRV = 288230376151711744, arm:arm64:mips64le:ppc64le:riscv64:s390x:
MCI_STATUS_AR = 36028797018963968, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
MCI_STATUS_EN = 1152921504606846976, arm:arm64:mips64le:ppc64le:riscv64:s390x:??
MCI_STATUS_MISCV = 576460752303423488, arm:arm64:mips64le:ppc64le:riscv64:s390x:
MCI_STATUS_OVER = 4611686018427387904, arm:arm64:mips64le:ppc64le:riscv64:s390x:
MCI_STATUS_PCC = 144115188075855872, arm:arm64:mips64le:ppc64le:riscv64:s390x:??
MCI_STATUS_S = 72057594037927936, arm:arm64:mips64le:ppc64le:riscv64:s390x:???
MCI_STATUS_UC = 2305843009213693952, arm:arm64:mips64le:ppc64le:riscv64:s390x:??
MCI_STATUS_VAL = 9223372036854775808, arm:arm64:mips64le:ppc64le:riscv64:s390x:?
VMCS12_SIZE = 4096, arm:riscv64:???
__NR_ioctl = 54, amd64:16, arm:riscv64:???, arm64:29, mips64le:5015
__NR_mmap = 90, 386:192, amd64:9, arm:riscv64:???, arm64:222, mips64le:5009
__NR_mmap2 = 386:192, amd64:arm:arm64:mips64le:ppc64le:riscv64:s390x:???
__NR_openat = 386:295, amd64:257, arm:riscv64:???, arm64:56, mips64le:5247, ppc6
/tmp/desc.txt Mon Jan 17 05:46:11 2022 19
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in
#define ADDR_TEXT 0x0000
#define ADDR_GDT 0x1000
#define ADDR_LDT 0x1800
#define ADDR_PML4 0x2000
#define ADDR_PDP 0x3000
#define ADDR_PD 0x4000
#define ADDR_STACK0 0x0f80
#define ADDR_VAR_HLT 0x2800
#define ADDR_VAR_SYSRET 0x2808
#define ADDR_VAR_SYSEXIT 0x2810
#define ADDR_VAR_IDT 0x3800
#define ADDR_VAR_TSS64 0x3a00
#define ADDR_VAR_TSS64_CPL3 0x3c00
#define ADDR_VAR_TSS16 0x3d00
#define ADDR_VAR_TSS16_2 0x3e00
#define ADDR_VAR_TSS16_CPL3 0x3f00
#define ADDR_VAR_TSS32 0x4800
#define ADDR_VAR_TSS32_2 0x4a00
#define ADDR_VAR_TSS32_CPL3 0x4c00
#define ADDR_VAR_TSS32_VM86 0x4e00
#define ADDR_VAR_VMXON_PTR 0x5f00
#define ADDR_VAR_VMCS_PTR 0x5f08
#define ADDR_VAR_VMEXIT_PTR 0x5f10
#define ADDR_VAR_VMWRITE_FLD 0x5f18
#define ADDR_VAR_VMWRITE_VAL 0x5f20
#define ADDR_VAR_VMXON 0x6000
#define ADDR_VAR_VMCS 0x7000
#define ADDR_VAR_VMEXIT_CODE 0x9000
#define ADDR_VAR_USER_CODE 0x9100
#define ADDR_VAR_USER_CODE2 0x9120
#define SEL_LDT (1 << 3)
#define SEL_CS16 (2 << 3)
#define SEL_DS16 (3 << 3)
#define SEL_CS16_CPL3 ((4 << 3) + 3)
#define SEL_DS16_CPL3 ((5 << 3) + 3)
#define SEL_CS32 (6 << 3)
#define SEL_DS32 (7 << 3)
#define SEL_CS32_CPL3 ((8 << 3) + 3)
#define SEL_DS32_CPL3 ((9 << 3) + 3)
#define SEL_CS64 (10 << 3)
#define SEL_DS64 (11 << 3)
#define SEL_CS64_CPL3 ((12 << 3) + 3)
#define SEL_DS64_CPL3 ((13 << 3) + 3)
#define SEL_CGATE16 (14 << 3)
#define SEL_TGATE16 (15 << 3)
#define SEL_CGATE32 (16 << 3)
#define SEL_TGATE32 (17 << 3)
#define SEL_CGATE64 (18 << 3)
#define SEL_CGATE64_HI (19 << 3)
#define SEL_TSS16 (20 << 3)
#define SEL_TSS16_2 (21 << 3)
#define SEL_TSS16_CPL3 ((22 << 3) + 3)
#define SEL_TSS32 (23 << 3)
#define SEL_TSS32_2 (24 << 3)
#define SEL_TSS32_CPL3 ((25 << 3) + 3)
#define SEL_TSS32_VM86 (26 << 3)
#define SEL_TSS64 (27 << 3)
#define SEL_TSS64_HI (28 << 3)
#define SEL_TSS64_CPL3 ((29 << 3) + 3)
/tmp/desc.txt Mon Jan 17 05:46:11 2022 20
#define SEL_TSS64_CPL3_HI (30 << 3)
#define MSR_IA32_FEATURE_CONTROL 0x3a
#define MSR_IA32_VMX_BASIC 0x480
#define MSR_IA32_SMBASE 0x9e
#define MSR_IA32_SYSENTER_CS 0x174
#define MSR_IA32_SYSENTER_ESP 0x175
#define MSR_IA32_SYSENTER_EIP 0x176
#define MSR_IA32_STAR 0xC0000081
#define MSR_IA32_LSTAR 0xC0000082
#define MSR_IA32_VMX_PROCBASED_CTLS2 0x48B
#define NEXT_INSN $0xbadc0de
#define PREFIX_SIZE 0xba1d
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in
// This file is shared between executor and csource package.
// Implementation of syz_kvm_setup_cpu pseudo-syscall.
// See Intel Software Developerâ\200\231s Manual Volume 3: System Programming Guide
// for details on what happens here.
#include "kvm.h"
#include "kvm_amd64.S.h"
#ifndef KVM_SMI
#define KVM_SMI _IO(KVMIO, 0xb7)
#endif
#define CR0_PE 1
#define CR0_MP (1 << 1)
#define CR0_EM (1 << 2)
#define CR0_TS (1 << 3)
#define CR0_ET (1 << 4)
#define CR0_NE (1 << 5)
#define CR0_WP (1 << 16)
#define CR0_AM (1 << 18)
#define CR0_NW (1 << 29)
#define CR0_CD (1 << 30)
#define CR0_PG (1 << 31)
#define CR4_VME 1
#define CR4_PVI (1 << 1)
#define CR4_TSD (1 << 2)
#define CR4_DE (1 << 3)
#define CR4_PSE (1 << 4)
#define CR4_PAE (1 << 5)
#define CR4_MCE (1 << 6)
#define CR4_PGE (1 << 7)
#define CR4_PCE (1 << 8)
#define CR4_OSFXSR (1 << 8)
#define CR4_OSXMMEXCPT (1 << 10)
#define CR4_UMIP (1 << 11)
#define CR4_VMXE (1 << 13)
#define CR4_SMXE (1 << 14)
#define CR4_FSGSBASE (1 << 16)
#define CR4_PCIDE (1 << 17)
#define CR4_OSXSAVE (1 << 18)
#define CR4_SMEP (1 << 20)
#define CR4_SMAP (1 << 21)
#define CR4_PKE (1 << 22)
/tmp/desc.txt Mon Jan 17 05:46:11 2022 21
#define EFER_SCE 1
#define EFER_LME (1 << 8)
#define EFER_LMA (1 << 10)
#define EFER_NXE (1 << 11)
#define EFER_SVME (1 << 12)
#define EFER_LMSLE (1 << 13)
#define EFER_FFXSR (1 << 14)
#define EFER_TCE (1 << 15)
// 32-bit page directory entry bits
#define PDE32_PRESENT 1
#define PDE32_RW (1 << 1)
#define PDE32_USER (1 << 2)
#define PDE32_PS (1 << 7)
// 64-bit page * entry bits
#define PDE64_PRESENT 1
#define PDE64_RW (1 << 1)
#define PDE64_USER (1 << 2)
#define PDE64_ACCESSED (1 << 5)
#define PDE64_DIRTY (1 << 6)
#define PDE64_PS (1 << 7)
#define PDE64_G (1 << 8)
struct tss16 {
 uint16 prev;
 uint16 sp0;
 uint16 ss0;
 uint16 sp1;
 uint16 ss1;
 uint16 sp2;
 uint16 ss2;
 uint16 ip;
 uint16 flags;
 uint16 ax;
 uint16 cx;
 uint16 dx;
 uint16 bx;
 uint16 sp;
 uint16 bp;
 uint16 si;
 uint16 di;
 uint16 es;
 uint16 cs;
 uint16 ss;
 uint16 ds;
 uint16 ldt;
} __attribute__((packed));
struct tss32 {
 uint16 prev, prevh;
 uint32 sp0;
 uint16 ss0, ss0h;
 uint32 sp1;
 uint16 ss1, ss1h;
 uint32 sp2;
 uint16 ss2, ss2h;
 uint32 cr3;
 uint32 ip;
 uint32 flags;
 uint32 ax;
 uint32 cx;
 uint32 dx;
/tmp/desc.txt Mon Jan 17 05:46:11 2022 22
 uint32 bx;
 uint32 sp;
 uint32 bp;
 uint32 si;
 uint32 di;
 uint16 es, esh;
 uint16 cs, csh;
 uint16 ss, ssh;
 uint16 ds, dsh;
 uint16 fs, fsh;
 uint16 gs, gsh;
 uint16 ldt, ldth;
 uint16 trace;
 uint16 io_bitmap;
} __attribute__((packed));
struct tss64 {
 uint32 reserved0;
 uint64 rsp[3];
 uint64 reserved1;
 uint64 ist[7];
 uint64 reserved2;
 uint32 reserved3;
 uint32 io_bitmap;
} __attribute__((packed));
static void fill_segment_descriptor(uint64* dt, uint64* lt, struct kvm_segment*
{
 uint16 index = seg->selector >> 3;
 uint64 limit = seg->g ? seg->limit >> 12 : seg->limit;
 uint64 sd = (limit & 0xffff) | (seg->base & 0xffffff) << 16 | (uint64)seg->type
 dt[index] = sd;
 lt[index] = sd;
}
static void fill_segment_descriptor_dword(uint64* dt, uint64* lt, struct kvm_seg
{
 fill_segment_descriptor(dt, lt, seg);
 uint16 index = seg->selector >> 3;
 dt[index + 1] = 0;
 lt[index + 1] = 0;
}
static void setup_syscall_msrs(int cpufd, uint16 sel_cs, uint16 sel_cs_cpl3)
{
 char buf[sizeof(struct kvm_msrs) + 5 * sizeof(struct kvm_msr_entry)];
 memset(buf, 0, sizeof(buf));
 struct kvm_msrs* msrs = (struct kvm_msrs*)buf;
 struct kvm_msr_entry* entries = msrs->entries;
 msrs->nmsrs = 5;
 entries[0].index = MSR_IA32_SYSENTER_CS;
 entries[0].data = sel_cs;
 entries[1].index = MSR_IA32_SYSENTER_ESP;
 entries[1].data = ADDR_STACK0;
 entries[2].index = MSR_IA32_SYSENTER_EIP;
 entries[2].data = ADDR_VAR_SYSEXIT;
 entries[3].index = MSR_IA32_STAR;
 entries[3].data = ((uint64)sel_cs << 32) | ((uint64)sel_cs_cpl3 << 48);
 entries[4].index = MSR_IA32_LSTAR;
 entries[4].data = ADDR_VAR_SYSRET;
 ioctl(cpufd, KVM_SET_MSRS, msrs);
}
/tmp/desc.txt Mon Jan 17 05:46:11 2022 23
static void setup_32bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t g
{
 sregs->idt.base = guest_mem + ADDR_VAR_IDT;
 sregs->idt.limit = 0x1ff;
 uint64* idt = (uint64*)(host_mem + sregs->idt.base);
 for (int i = 0; i < 32; i++) {
 struct kvm_segment gate;
 gate.selector = i << 3;
 switch (i % 6) {
 case 0:
 // 16-bit interrupt gate
 gate.type = 6;
 gate.base = SEL_CS16;
 break;
 case 1:
 // 16-bit trap gate
 gate.type = 7;
 gate.base = SEL_CS16;
 break;
 case 2:
 // 16-bit task gate
 gate.type = 3;
 gate.base = SEL_TGATE16;
 break;
 case 3:
 // 32-bit interrupt gate
 gate.type = 14;
 gate.base = SEL_CS32;
 break;
 case 4:
 // 32-bit trap gate
 gate.type = 15;
 gate.base = SEL_CS32;
 break;
 case 5:
 // 32-bit task gate
 gate.type = 11;
 gate.base = SEL_TGATE32;
 break;
 }
 gate.limit = guest_mem + ADDR_VAR_USER_CODE2; // entry offset
 gate.present = 1;
 gate.dpl = 0;
 gate.s = 0;
 gate.g = 0;
 gate.db = 0;
 gate.l = 0;
 gate.avl = 0;
 fill_segment_descriptor(idt, idt, &gate);
 }
}
static void setup_64bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t g
{
 sregs->idt.base = guest_mem + ADDR_VAR_IDT;
 sregs->idt.limit = 0x1ff;
 uint64* idt = (uint64*)(host_mem + sregs->idt.base);
 for (int i = 0; i < 32; i++) {
 struct kvm_segment gate;
 gate.selector = (i * 2) << 3;
 gate.type = (i & 1) ? 14 : 15; // interrupt or trap gate
 gate.base = SEL_CS64;
 gate.limit = guest_mem + ADDR_VAR_USER_CODE2; // entry offset
/tmp/desc.txt Mon Jan 17 05:46:11 2022 24
 gate.present = 1;
 gate.dpl = 0;
 gate.s = 0;
 gate.g = 0;
 gate.db = 0;
 gate.l = 0;
 gate.avl = 0;
 fill_segment_descriptor_dword(idt, idt, &gate);
 }
}
struct kvm_text {
 uintptr_t typ;
 const void* text;
 uintptr_t size;
};
struct kvm_opt {
 uint64 typ;
 uint64 val;
};
#define KVM_SETUP_PAGING (1 << 0)
#define KVM_SETUP_PAE (1 << 1)
#define KVM_SETUP_PROTECTED (1 << 2)
#define KVM_SETUP_CPL3 (1 << 3)
#define KVM_SETUP_VIRT86 (1 << 4)
#define KVM_SETUP_SMM (1 << 5)
#define KVM_SETUP_VM (1 << 6)
// syz_kvm_setup_cpu(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in,
static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat
{
 const int vmfd = a0;
 const int cpufd = a1;
 char* const host_mem = (char*)a2;
 const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3;
 const uintptr_t text_count = a4;
 const uintptr_t flags = a5;
 const struct kvm_opt* const opt_array_ptr = (struct kvm_opt*)a6;
 uintptr_t opt_count = a7;
 const uintptr_t page_size = 4 << 10;
 const uintptr_t ioapic_page = 10;
 const uintptr_t guest_mem_size = 24 * page_size;
 const uintptr_t guest_mem = 0;
 (void)text_count; // fuzzer can spoof count and we need just 1 text, so ignore
 int text_type = text_array_ptr[0].typ;
 const void* text = text_array_ptr[0].text;
 uintptr_t text_size = text_array_ptr[0].size;
 for (uintptr_t i = 0; i < guest_mem_size / page_size; i++) {
 struct kvm_userspace_memory_region memreg;
 memreg.slot = i;
 memreg.flags = 0; // can be KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY
 memreg.guest_phys_addr = guest_mem + i * page_size;
 if (i == ioapic_page)
 memreg.guest_phys_addr = 0xfec00000;
 memreg.memory_size = page_size;
 memreg.userspace_addr = (uintptr_t)host_mem + i * page_size;
 ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
 }
/tmp/desc.txt Mon Jan 17 05:46:11 2022 25
 // SMRAM
 struct kvm_userspace_memory_region memreg;
 memreg.slot = 1 + (1 << 16);
 memreg.flags = 0;
 memreg.guest_phys_addr = 0x30000;
 memreg.memory_size = 64 << 10;
 memreg.userspace_addr = (uintptr_t)host_mem;
 ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg);
 struct kvm_sregs sregs;
 if (ioctl(cpufd, KVM_GET_SREGS, &sregs))
 return -1;
 struct kvm_regs regs;
 memset(&regs, 0, sizeof(regs));
 regs.rip = guest_mem + ADDR_TEXT;
 regs.rsp = ADDR_STACK0;
 sregs.gdt.base = guest_mem + ADDR_GDT;
 sregs.gdt.limit = 256 * sizeof(uint64) - 1;
 uint64* gdt = (uint64*)(host_mem + sregs.gdt.base);
 struct kvm_segment seg_ldt;
 seg_ldt.selector = SEL_LDT;
 seg_ldt.type = 2;
 seg_ldt.base = guest_mem + ADDR_LDT;
 seg_ldt.limit = 256 * sizeof(uint64) - 1;
 seg_ldt.present = 1;
 seg_ldt.dpl = 0;
 seg_ldt.s = 0;
 seg_ldt.g = 0;
 seg_ldt.db = 1;
 seg_ldt.l = 0;
 sregs.ldt = seg_ldt;
 uint64* ldt = (uint64*)(host_mem + sregs.ldt.base);
 struct kvm_segment seg_cs16;
 seg_cs16.selector = SEL_CS16;
 seg_cs16.type = 11;
 seg_cs16.base = 0;
 seg_cs16.limit = 0xfffff;
 seg_cs16.present = 1;
 seg_cs16.dpl = 0;
 seg_cs16.s = 1;
 seg_cs16.g = 0;
 seg_cs16.db = 0;
 seg_cs16.l = 0;
 struct kvm_segment seg_ds16 = seg_cs16;
 seg_ds16.selector = SEL_DS16;
 seg_ds16.type = 3;
 struct kvm_segment seg_cs16_cpl3 = seg_cs16;
 seg_cs16_cpl3.selector = SEL_CS16_CPL3;
 seg_cs16_cpl3.dpl = 3;
 struct kvm_segment seg_ds16_cpl3 = seg_ds16;
 seg_ds16_cpl3.selector = SEL_DS16_CPL3;
 seg_ds16_cpl3.dpl = 3;
 struct kvm_segment seg_cs32 = seg_cs16;
 seg_cs32.selector = SEL_CS32;
 seg_cs32.db = 1;
/tmp/desc.txt Mon Jan 17 05:46:11 2022 26
 struct kvm_segment seg_ds32 = seg_ds16;
 seg_ds32.selector = SEL_DS32;
 seg_ds32.db = 1;
 struct kvm_segment seg_cs32_cpl3 = seg_cs32;
 seg_cs32_cpl3.selector = SEL_CS32_CPL3;
 seg_cs32_cpl3.dpl = 3;
 struct kvm_segment seg_ds32_cpl3 = seg_ds32;
 seg_ds32_cpl3.selector = SEL_DS32_CPL3;
 seg_ds32_cpl3.dpl = 3;
 struct kvm_segment seg_cs64 = seg_cs16;
 seg_cs64.selector = SEL_CS64;
 seg_cs64.l = 1;
 struct kvm_segment seg_ds64 = seg_ds32;
 seg_ds64.selector = SEL_DS64;
 struct kvm_segment seg_cs64_cpl3 = seg_cs64;
 seg_cs64_cpl3.selector = SEL_CS64_CPL3;
 seg_cs64_cpl3.dpl = 3;
 struct kvm_segment seg_ds64_cpl3 = seg_ds64;
 seg_ds64_cpl3.selector = SEL_DS64_CPL3;
 seg_ds64_cpl3.dpl = 3;
 struct kvm_segment seg_tss32;
 seg_tss32.selector = SEL_TSS32;
 seg_tss32.type = 9;
 seg_tss32.base = ADDR_VAR_TSS32;
 seg_tss32.limit = 0x1ff;
 seg_tss32.present = 1;
 seg_tss32.dpl = 0;
 seg_tss32.s = 0;
 seg_tss32.g = 0;
 seg_tss32.db = 0;
 seg_tss32.l = 0;
 struct kvm_segment seg_tss32_2 = seg_tss32;
 seg_tss32_2.selector = SEL_TSS32_2;
 seg_tss32_2.base = ADDR_VAR_TSS32_2;
 struct kvm_segment seg_tss32_cpl3 = seg_tss32;
 seg_tss32_cpl3.selector = SEL_TSS32_CPL3;
 seg_tss32_cpl3.base = ADDR_VAR_TSS32_CPL3;
 struct kvm_segment seg_tss32_vm86 = seg_tss32;
 seg_tss32_vm86.selector = SEL_TSS32_VM86;
 seg_tss32_vm86.base = ADDR_VAR_TSS32_VM86;
 struct kvm_segment seg_tss16 = seg_tss32;
 seg_tss16.selector = SEL_TSS16;
 seg_tss16.base = ADDR_VAR_TSS16;
 seg_tss16.limit = 0xff;
 seg_tss16.type = 1;
 struct kvm_segment seg_tss16_2 = seg_tss16;
 seg_tss16_2.selector = SEL_TSS16_2;
 seg_tss16_2.base = ADDR_VAR_TSS16_2;
 seg_tss16_2.dpl = 0;
/tmp/desc.txt Mon Jan 17 05:46:11 2022 27
 struct kvm_segment seg_tss16_cpl3 = seg_tss16;
 seg_tss16_cpl3.selector = SEL_TSS16_CPL3;
 seg_tss16_cpl3.base = ADDR_VAR_TSS16_CPL3;
 seg_tss16_cpl3.dpl = 3;
 struct kvm_segment seg_tss64 = seg_tss32;
 seg_tss64.selector = SEL_TSS64;
 seg_tss64.base = ADDR_VAR_TSS64;
 seg_tss64.limit = 0x1ff;
 struct kvm_segment seg_tss64_cpl3 = seg_tss64;
 seg_tss64_cpl3.selector = SEL_TSS64_CPL3;
 seg_tss64_cpl3.base = ADDR_VAR_TSS64_CPL3;
 seg_tss64_cpl3.dpl = 3;
 struct kvm_segment seg_cgate16;
 seg_cgate16.selector = SEL_CGATE16;
 seg_cgate16.type = 4;
 seg_cgate16.base = SEL_CS16 | (2 << 16); // selector + param count
 seg_cgate16.limit = ADDR_VAR_USER_CODE2; // entry offset
 seg_cgate16.present = 1;
 seg_cgate16.dpl = 0;
 seg_cgate16.s = 0;
 seg_cgate16.g = 0;
 seg_cgate16.db = 0;
 seg_cgate16.l = 0;
 seg_cgate16.avl = 0;
 struct kvm_segment seg_tgate16 = seg_cgate16;
 seg_tgate16.selector = SEL_TGATE16;
 seg_tgate16.type = 3;
 seg_cgate16.base = SEL_TSS16_2;
 seg_tgate16.limit = 0;
 struct kvm_segment seg_cgate32 = seg_cgate16;
 seg_cgate32.selector = SEL_CGATE32;
 seg_cgate32.type = 12;
 seg_cgate32.base = SEL_CS32 | (2 << 16); // selector + param count
 struct kvm_segment seg_tgate32 = seg_cgate32;
 seg_tgate32.selector = SEL_TGATE32;
 seg_tgate32.type = 11;
 seg_tgate32.base = SEL_TSS32_2;
 seg_tgate32.limit = 0;
 struct kvm_segment seg_cgate64 = seg_cgate16;
 seg_cgate64.selector = SEL_CGATE64;
 seg_cgate64.type = 12;
 seg_cgate64.base = SEL_CS64;
 int kvmfd = open("/dev/kvm", O_RDWR);
 char buf[sizeof(struct kvm_cpuid2) + 128 * sizeof(struct kvm_cpuid_entry2)];
 memset(buf, 0, sizeof(buf));
 struct kvm_cpuid2* cpuid = (struct kvm_cpuid2*)buf;
 cpuid->nent = 128;
 ioctl(kvmfd, KVM_GET_SUPPORTED_CPUID, cpuid);
 ioctl(cpufd, KVM_SET_CPUID2, cpuid);
 close(kvmfd);
 const char* text_prefix = 0;
 int text_prefix_size = 0;
 char* host_text = host_mem + ADDR_TEXT;
/tmp/desc.txt Mon Jan 17 05:46:11 2022 28
 if (text_type == 8) {
 if (flags & KVM_SETUP_SMM) {
 if (flags & KVM_SETUP_PROTECTED) {
 sregs.cs = seg_cs16;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds1
6;
 sregs.cr0 |= CR0_PE;
 } else {
 sregs.cs.selector = 0;
 sregs.cs.base = 0;
 }
 *(host_mem + ADDR_TEXT) = 0xf4; // hlt for rsm
 host_text = host_mem + 0x8000;
 ioctl(cpufd, KVM_SMI, 0);
 } else if (flags & KVM_SETUP_VIRT86) {
 sregs.cs = seg_cs32;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
 sregs.cr0 |= CR0_PE;
 sregs.efer |= EFER_SCE;
 setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3);
 setup_32bit_idt(&sregs, host_mem, guest_mem);
 if (flags & KVM_SETUP_PAGING) {
 uint64 pd_addr = guest_mem + ADDR_PD;
 uint64* pd = (uint64*)(host_mem + ADDR_PD);
 // A single 4MB page to cover the memory region
 pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS;
 sregs.cr3 = pd_addr;
 sregs.cr4 |= CR4_PSE;
 text_prefix = kvm_asm32_paged_vm86;
 text_prefix_size = sizeof(kvm_asm32_paged_vm86) - 1;
 } else {
 text_prefix = kvm_asm32_vm86;
 text_prefix_size = sizeof(kvm_asm32_vm86) - 1;
 }
 } else {
 sregs.cs.selector = 0;
 sregs.cs.base = 0;
 }
 } else if (text_type == 16) {
 if (flags & KVM_SETUP_CPL3) {
 sregs.cs = seg_cs16;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
 text_prefix = kvm_asm16_cpl3;
 text_prefix_size = sizeof(kvm_asm16_cpl3) - 1;
 } else {
 sregs.cr0 |= CR0_PE;
 sregs.cs = seg_cs16;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds16;
 }
 } else if (text_type == 32) {
 sregs.cr0 |= CR0_PE;
 sregs.efer |= EFER_SCE;
 setup_syscall_msrs(cpufd, SEL_CS32, SEL_CS32_CPL3);
 setup_32bit_idt(&sregs, host_mem, guest_mem);
 if (flags & KVM_SETUP_SMM) {
/tmp/desc.txt Mon Jan 17 05:46:11 2022 29
 sregs.cs = seg_cs32;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
 *(host_mem + ADDR_TEXT) = 0xf4; // hlt for rsm
 host_text = host_mem + 0x8000;
 ioctl(cpufd, KVM_SMI, 0);
 } else if (flags & KVM_SETUP_PAGING) {
 sregs.cs = seg_cs32;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
 uint64 pd_addr = guest_mem + ADDR_PD;
 uint64* pd = (uint64*)(host_mem + ADDR_PD);
 // A single 4MB page to cover the memory region
 pd[0] = PDE32_PRESENT | PDE32_RW | PDE32_USER | PDE32_PS;
 sregs.cr3 = pd_addr;
 sregs.cr4 |= CR4_PSE;
 text_prefix = kvm_asm32_paged;
 text_prefix_size = sizeof(kvm_asm32_paged) - 1;
 } else if (flags & KVM_SETUP_CPL3) {
 sregs.cs = seg_cs32_cpl3;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32_cpl3;
 } else {
 sregs.cs = seg_cs32;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
 }
 } else {
 sregs.efer |= EFER_LME | EFER_SCE;
 sregs.cr0 |= CR0_PE;
 setup_syscall_msrs(cpufd, SEL_CS64, SEL_CS64_CPL3);
 setup_64bit_idt(&sregs, host_mem, guest_mem);
 sregs.cs = seg_cs32;
 sregs.ds = sregs.es = sregs.fs = sregs.gs = sregs.ss = seg_ds32;
 uint64 pml4_addr = guest_mem + ADDR_PML4;
 uint64* pml4 = (uint64*)(host_mem + ADDR_PML4);
 uint64 pdpt_addr = guest_mem + ADDR_PDP;
 uint64* pdpt = (uint64*)(host_mem + ADDR_PDP);
 uint64 pd_addr = guest_mem + ADDR_PD;
 uint64* pd = (uint64*)(host_mem + ADDR_PD);
 pml4[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pdpt_addr;
 pdpt[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | pd_addr;
 pd[0] = PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_PS;
 sregs.cr3 = pml4_addr;
 sregs.cr4 |= CR4_PAE;
 if (flags & KVM_SETUP_VM) {
 sregs.cr0 |= CR0_NE;
 *((uint64*)(host_mem + ADDR_VAR_VMXON_PTR)) = ADDR_VAR_VMXON;
 *((uint64*)(host_mem + ADDR_VAR_VMCS_PTR)) = ADDR_VAR_VMCS;
 memcpy(host_mem + ADDR_VAR_VMEXIT_CODE, kvm_asm64_vm_exit, sizeof(kvm_
asm64_v
 *((uint64*)(host_mem + ADDR_VAR_VMEXIT_PTR)) = ADDR_VAR_VMEXIT_CODE;
 text_prefix = kvm_asm64_init_vm;
 text_prefix_size = sizeof(kvm_asm64_init_vm) - 1;
 } else if (flags & KVM_SETUP_CPL3) {
 text_prefix = kvm_asm64_cpl3;
 text_prefix_size = sizeof(kvm_asm64_cpl3) - 1;
/tmp/desc.txt Mon Jan 17 05:46:11 2022 30
 } else {
 text_prefix = kvm_asm64_enable_long;
 text_prefix_size = sizeof(kvm_asm64_enable_long) - 1;
 }
 }
 struct tss16 tss16;
 memset(&tss16, 0, sizeof(tss16));
 tss16.ss0 = tss16.ss1 = tss16.ss2 = SEL_DS16;
 tss16.sp0 = tss16.sp1 = tss16.sp2 = ADDR_STACK0;
 tss16.ip = ADDR_VAR_USER_CODE2;
 tss16.flags = (1 << 1);
 tss16.cs = SEL_CS16;
 tss16.es = tss16.ds = tss16.ss = SEL_DS16;
 tss16.ldt = SEL_LDT;
 struct tss16* tss16_addr = (struct tss16*)(host_mem + seg_tss16_2.base);
 memcpy(tss16_addr, &tss16, sizeof(tss16));
 memset(&tss16, 0, sizeof(tss16));
 tss16.ss0 = tss16.ss1 = tss16.ss2 = SEL_DS16;
 tss16.sp0 = tss16.sp1 = tss16.sp2 = ADDR_STACK0;
 tss16.ip = ADDR_VAR_USER_CODE2;
 tss16.flags = (1 << 1);
 tss16.cs = SEL_CS16_CPL3;
 tss16.es = tss16.ds = tss16.ss = SEL_DS16_CPL3;
 tss16.ldt = SEL_LDT;
 struct tss16* tss16_cpl3_addr = (struct tss16*)(host_mem + seg_tss16_cpl3.base)
 memcpy(tss16_cpl3_addr, &tss16, sizeof(tss16));
 struct tss32 tss32;
 memset(&tss32, 0, sizeof(tss32));
 tss32.ss0 = tss32.ss1 = tss32.ss2 = SEL_DS32;
 tss32.sp0 = tss32.sp1 = tss32.sp2 = ADDR_STACK0;
 tss32.ip = ADDR_VAR_USER_CODE;
 tss32.flags = (1 << 1) | (1 << 17);
 tss32.ldt = SEL_LDT;
 tss32.cr3 = sregs.cr3;
 tss32.io_bitmap = offsetof(struct tss32, io_bitmap);
 struct tss32* tss32_addr = (struct tss32*)(host_mem + seg_tss32_vm86.base);
 memcpy(tss32_addr, &tss32, sizeof(tss32));
 memset(&tss32, 0, sizeof(tss32));
 tss32.ss0 = tss32.ss1 = tss32.ss2 = SEL_DS32;
 tss32.sp0 = tss32.sp1 = tss32.sp2 = ADDR_STACK0;
 tss32.ip = ADDR_VAR_USER_CODE;
 tss32.flags = (1 << 1);
 tss32.cr3 = sregs.cr3;
 tss32.es = tss32.ds = tss32.ss = tss32.gs = tss32.fs = SEL_DS32;
 tss32.cs = SEL_CS32;
 tss32.ldt = SEL_LDT;
 tss32.cr3 = sregs.cr3;
 tss32.io_bitmap = offsetof(struct tss32, io_bitmap);
 struct tss32* tss32_cpl3_addr = (struct tss32*)(host_mem + seg_tss32_2.base);
 memcpy(tss32_cpl3_addr, &tss32, sizeof(tss32));
 struct tss64 tss64;
 memset(&tss64, 0, sizeof(tss64));
 tss64.rsp[0] = ADDR_STACK0;
 tss64.rsp[1] = ADDR_STACK0;
 tss64.rsp[2] = ADDR_STACK0;
 tss64.io_bitmap = offsetof(struct tss64, io_bitmap);
 struct tss64* tss64_addr = (struct tss64*)(host_mem + seg_tss64.base);
 memcpy(tss64_addr, &tss64, sizeof(tss64));
/tmp/desc.txt Mon Jan 17 05:46:11 2022 31
 memset(&tss64, 0, sizeof(tss64));
 tss64.rsp[0] = ADDR_STACK0;
 tss64.rsp[1] = ADDR_STACK0;
 tss64.rsp[2] = ADDR_STACK0;
 tss64.io_bitmap = offsetof(struct tss64, io_bitmap);
 struct tss64* tss64_cpl3_addr = (struct tss64*)(host_mem + seg_tss64_cpl3.base)
 memcpy(tss64_cpl3_addr, &tss64, sizeof(tss64));
 if (text_size > 1000)
 text_size = 1000;
 if (text_prefix) {
 memcpy(host_text, text_prefix, text_prefix_size);
 // Replace 0xbadc0de in LJMP with offset of a next instruction.
 void* patch = memmem(host_text, text_prefix_size, "\xde\xc0\xad\x0b", 4);
 if (patch)
 *((uint32*)patch) = guest_mem + ADDR_TEXT + ((char*)patch - host_text)
 + 6;
 uint16 magic = PREFIX_SIZE;
 patch = memmem(host_text, text_prefix_size, &magic, sizeof(magic));
 if (patch)
 *((uint16*)patch) = guest_mem + ADDR_TEXT + text_prefix_size;
 }
 memcpy((void*)(host_text + text_prefix_size), text, text_size);
 *(host_text + text_prefix_size + text_size) = 0xf4; // hlt
 memcpy(host_mem + ADDR_VAR_USER_CODE, text, text_size);
 *(host_mem + ADDR_VAR_USER_CODE + text_size) = 0xf4; // hlt
 *(host_mem + ADDR_VAR_HLT) = 0xf4; // hlt
 memcpy(host_mem + ADDR_VAR_SYSRET, "\x0f\x07\xf4", 3);
 memcpy(host_mem + ADDR_VAR_SYSEXIT, "\x0f\x35\xf4", 3);
 *(uint64*)(host_mem + ADDR_VAR_VMWRITE_FLD) = 0;
 *(uint64*)(host_mem + ADDR_VAR_VMWRITE_VAL) = 0;
 if (opt_count > 2)
 opt_count = 2;
 for (uintptr_t i = 0; i < opt_count; i++) {
 uint64 typ = opt_array_ptr[i].typ;
 uint64 val = opt_array_ptr[i].val;
 switch (typ % 9) {
 case 0:
 sregs.cr0 ^= val & (CR0_MP | CR0_EM | CR0_ET | CR0_NE | CR0_WP | CR0_A
M | CR0
 break;
 case 1:
 sregs.cr4 ^= val & (CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_MCE | C
R4_PGE
 CR4_OSFXSR | CR4_OSXMMEXCPT | CR4_UMIP | CR4_VMXE
| CR4_SMXE | CR4_FSGS
 CR4_OSXSAVE | CR4_SMEP | CR4_SMAP | CR4_PKE);
 break;
 case 2:
 sregs.efer ^= val & (EFER_SCE | EFER_NXE | EFER_SVME | EFER_LMSLE | EF
ER_FFXS
 break;
 case 3:
 val &= ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13) | (1 <
< 14) |
 (1 << 15) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21));
 regs.rflags ^= val;
 tss16_addr->flags ^= val;
/tmp/desc.txt Mon Jan 17 05:46:11 2022 32
 tss16_cpl3_addr->flags ^= val;
 tss32_addr->flags ^= val;
 tss32_cpl3_addr->flags ^= val;
 break;
 case 4:
 seg_cs16.type = val & 0xf;
 seg_cs32.type = val & 0xf;
 seg_cs64.type = val & 0xf;
 break;
 case 5:
 seg_cs16_cpl3.type = val & 0xf;
 seg_cs32_cpl3.type = val & 0xf;
 seg_cs64_cpl3.type = val & 0xf;
 break;
 case 6:
 seg_ds16.type = val & 0xf;
 seg_ds32.type = val & 0xf;
 seg_ds64.type = val & 0xf;
 break;
 case 7:
 seg_ds16_cpl3.type = val & 0xf;
 seg_ds32_cpl3.type = val & 0xf;
 seg_ds64_cpl3.type = val & 0xf;
 break;
 case 8:
 *(uint64*)(host_mem + ADDR_VAR_VMWRITE_FLD) = (val & 0xffff);
 *(uint64*)(host_mem + ADDR_VAR_VMWRITE_VAL) = (val >> 16);
 break;
 default:
 fail("bad kvm setup opt");
 }
 }
 regs.rflags |= 2; // bit 1 is always set
 fill_segment_descriptor(gdt, ldt, &seg_ldt);
 fill_segment_descriptor(gdt, ldt, &seg_cs16);
 fill_segment_descriptor(gdt, ldt, &seg_ds16);
 fill_segment_descriptor(gdt, ldt, &seg_cs16_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_ds16_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_cs32);
 fill_segment_descriptor(gdt, ldt, &seg_ds32);
 fill_segment_descriptor(gdt, ldt, &seg_cs32_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_ds32_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_cs64);
 fill_segment_descriptor(gdt, ldt, &seg_ds64);
 fill_segment_descriptor(gdt, ldt, &seg_cs64_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_ds64_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_tss32);
 fill_segment_descriptor(gdt, ldt, &seg_tss32_2);
 fill_segment_descriptor(gdt, ldt, &seg_tss32_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_tss32_vm86);
 fill_segment_descriptor(gdt, ldt, &seg_tss16);
 fill_segment_descriptor(gdt, ldt, &seg_tss16_2);
 fill_segment_descriptor(gdt, ldt, &seg_tss16_cpl3);
 fill_segment_descriptor_dword(gdt, ldt, &seg_tss64);
 fill_segment_descriptor_dword(gdt, ldt, &seg_tss64_cpl3);
 fill_segment_descriptor(gdt, ldt, &seg_cgate16);
 fill_segment_descriptor(gdt, ldt, &seg_tgate16);
 fill_segment_descriptor(gdt, ldt, &seg_cgate32);
 fill_segment_descriptor(gdt, ldt, &seg_tgate32);
 fill_segment_descriptor_dword(gdt, ldt, &seg_cgate64);
 if (ioctl(cpufd, KVM_SET_SREGS, &sregs))
/tmp/desc.txt Mon Jan 17 05:46:11 2022 33
 return -1;
 if (ioctl(cpufd, KVM_SET_REGS, &regs))
 return -1;
 return 0;
}
1
2
3
4
5
6
7
8
files = "/dev/kvm", O_RDWR
ioctl[-1, -1, -1]
mmap[0, 0xF000, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_POPULATE,
0xFFFF, -1]
close[-1]
fstat[-1, -1]
read[-1, -1, 0xFFFF]
write[-1, -1, 0xFFFF]

上面是syzkaller对kvm做测试需要写的描述;下面是FUZZHG对kvm做测试所需要的配置

其他内核模糊测试的研究工作集中在改进 Syzkaller 的输入生成 [52][57]、改进对复杂内核接口的模糊测试支持 [59]、模糊测试特定的设备-内核接口 [40][49]、使用快照提高内核模糊测试性能 [50]、开发针对操作系统内核的覆盖引导灰盒模糊测试工具 [47],或将模糊测试与符号执行结合以发现更多漏洞 [27]。Moonshine [38] 指出了维护准确系统调用描述的高昂成本。然而,其提出的替代方法需要收集和分析大量来源于真实程序的系统调用跟踪数据。找到并执行与复杂内核接口进行大量交互的程序本身就是一个复杂且存在扩展性问题的任务。基于手工编写描述或真实跟踪数据的模糊测试工具可以快速覆盖内核代码的深层部分。然而,手工收集系统调用跟踪数据或编写描述的过程容易出现人为错误:描述和跟踪带来的直接覆盖率提升可能掩盖模糊测试工具无法有效探索的部分代码。证明这一点的是,FUZZNG 在 Syzkaller 标记为已覆盖的代码中发现了漏洞。

在本文中,我们提出了 FUZZNG,一个无需详细描述接口即可对系统调用进行模糊测试的系统。默认情况下,内核通过系统调用接口暴露了一个巨大的输入空间,包括进程的所有虚拟内存(例如,通过指向缓冲区的指针)以及整个文件描述符表。因此,尽管表面上系统调用最多接受六个数值型参数,但如果采用简单的模糊测试策略,仅向系统调用传递随机参数并不能取得满意的结果,因为大多数参数在语义上是无效的(例如,指向未映射内存的指针或不存在的文件描述符编号)。现有的系统调用模糊测试工具依赖于详细的系统调用描述和真实世界的调用轨迹来生成有效的系统调用参数,从而避免模糊测试整个由内核提供给用户空间应用程序的进程内存和文件描述符相关的输入空间。然而,这些基于语法的方式实际上将生成有效系统调用的负担转移到了开发者身上。

FUZZNG 的核心见解在于:与其依赖广泛的系统调用描述,不如重新整形系统调用的输入空间,从而(1)消除对系统调用特定语法的需求;(2)使系统调用模糊测试适用于已被验证的现成模糊测试工具。为了实现这种重整,FUZZNG 利用了内核代码在正常操作中已使用的 API,尤其是访问用户空间内存和管理文件描述符的 API。通过应用输入空间重整,FUZZNG 本质上依赖于一种变体的“简单”模糊测试策略,直接从模糊测试工具(例如,libFuzzer)传递系统调用参数。通过这种技术,FUZZNG 消除了对每个系统调用进行详细预分析和生成硬件的需求。简单来说,FUZZNG 不依赖于模糊测试前对系统调用的任何详细分析。

我们为 Linux 内核实现了 FUZZNG,并对 10 个 Syzkaller 提供了详细描述的 Linux 接口进行了评估。我们发现,尽管输入空间重整使 FUZZNG 避免了详细的系统调用描述,但它在覆盖率和错误发现能力上与 Syzkaller 相比是有竞争力的。例如,FUZZNG 能够设置基于 KVM 的虚拟机,配置它的虚拟内存槽位,填充指令,运行它,引发 KVM 指令仿真代码中的退出,并触发一个错误,而无需对 KVM 接口或其 100 多个 ioctl 命令有任何特别的了解。同样,FUZZNG 能够自动创建并执行复杂的 BPF 程序以及 io_uring 序列。所有能够针对这些复杂接口的现有系统都需要人工编写的系统调用描述。此外,FUZZNG 在 Syzkaller 已覆盖的代码中发现了新问题(详见第 V-D 节),这表明手动编写的描述往往可能对接口产生不充分或过度拟合的情况。

综上所述,我们的贡献包括:

  • 我们描述了 Linux 系统调用接口所暴露的输入空间的主要特征。 然后,我们介绍了当前模糊测试工具中常用的系统调用语法,并讨论了这些手工语法的微妙陷阱。
  • 我们提出了 FUZZNG,这是一种无需专家编写语法的系统调用模糊测试工具。 不同于之前的方法,FUZZNG 通过重整内核的输入空间,而不是试图自动生成系统调用描述。我们展示了 FUZZNG 在与 Syzkaller 的比较中表现出竞争力,在 Syzkaller 提供广泛描述的组件上平均实现了 102.5% 的覆盖率。我们的结果揭示了基于语法方法的局限性,同时在 Syzkaller 已覆盖的代码中发现了错误。
  • 我们描述了 FUZZNG 的基于快照的模糊测试方法, 以及我们为无语法内核模糊测试问题量身定制的变异和执行策略。我们展示了 FUZZNG 的一种新颖输入“优化”方法,利用执行过程中学到的信息来提高输入语料库的质量。
  • 我们报告了 FUZZNG 发现的 9 个新漏洞,其中 5 个位于 Syzkaller 已覆盖的组件中。 为了推动无语法内核模糊测试的进一步研究,我们将开源 FUZZNG 的所有组件。

为了践行开放科学的精神,我们将在 6d6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6n7g2i4y4W2j5$3I4S2j5W2)9J5c8V1k6#2P5Y4A6z5c8H3`.`. 上发布 FuzzNG 的源代码。

背景

在本节中,作为我们工作的背景,我们描述了操作系统、系统调用和操作系统模糊测试工具的相关内容。在 Linux 系统中,系统调用由一个整数“ID”识别。系统调用支持最多六个字大小的参数(在大多数架构中通过寄存器传递)。系统调用可以返回一个值,该值同样通过寄存器提供。由于传递给系统调用的数值参数大小有限,一些系统调用参数语义上表示更大的数据结构。特别是,Linux 内核文档强调了用于间接向内核传递任意大小数据的两种系统调用参数类型:指针文件描述符 [1][2]。系统调用使用文件描述符参数允许“用户空间引用内核对象” [1]。对于涉及大量参数的系统调用,这些参数被放置在一个结构中,并通过指针传递给内核 [2]。

例如,alarm 系统调用安排向进程发送一个信号。唯一的参数——直到警报到期的秒数——直接作为整数传递给内核。然而,write 系统调用需要三个参数:一个文件描述符、一个将被写入文件的缓冲区,以及缓冲区的长度。虽然文件描述符和缓冲区的长度可以用寄存器表示为整数,但缓冲区可以有任意长度。因此,内核期望缓冲区参数的寄存器中包含实际缓冲区的地址。

Linux 拥有数百个系统调用,这些调用是用户空间应用程序向内核请求服务的主要机制。然而,在内核内部,同一个系统调用可能有多种实现。例如,ioctl 系统调用用于控制设备,是许多内核驱动程序的主要配置机制。应用程序通过打开与设备关联的所谓特殊文件(通常位于 /dev 目录中)获取驱动程序的引用(即文件描述符)。然后,应用程序可以通过将文件描述符作为第一个参数调用 ioctl 来配置驱动程序。在内核内部,内核将每个文件描述符与一个 file_operations 结构体相关联,该结构体指向用于处理文件读取、写入和关闭等操作的函数。通过 file_operations,内核根据特殊文件的类型(例如字符设备或图形加速器)将 ioctl 请求路由到设备特定的系统调用实现。因此,由于文件描述符和指针参数的存在,Linux 数百个系统调用实际上是通向数千万行内核代码的一个狭窄入口。

系统调用模糊测试器与语法规则

SyzkallerTrinity 这样的系统调用模糊测试器依赖语法规则或规则库来生成有效的系统调用及其关联的文件描述符和指针参数。如果一个不具备语法规则的模糊测试器只是简单地将生成的整数作为系统调用的参数,那么由于系统调用输入空间的巨大范围(由指针和文件描述符参数所引起),这样的模糊测试器将很快变得毫无用处(详见第一节讨论)。因此,当前的系统调用模糊测试器依赖于对内核接口的广泛语法描述。例如,Syzkaller 需要对结构体类型、标志字段、枚举和作为系统调用参数的常量进行注释。此外,Syzkaller 还依赖于对每个系统调用生成的资源(如文件描述符)的手动注释,以确定有效的系统调用序列。这些注释可以防止 Syzkaller 在没有先调用 open 打开 /dev 中的相应文件之前,尝试使用 ioctl 系统调用与驱动程序交互。

尽管 Linux 中有数百个系统调用,但系统调用的行为可能会因参数的不同而有很大差异。例如,write 系统调用的代码路径因写入的文件类型不同(如磁盘文件、套接字或管道)而大相径庭。每种不同类型的系统调用都需要其自己的 Syzkaller 描述。因此,Syzkaller 包含了数万行的描述,这些描述由几十名开发者贡献。这些描述是用一种称为 “syzlang” 的领域特定语言编写的,专门用于描述系统调用接口。尽管社区付出了巨大的努力,但内核接口系统调用描述的不完整性仍然是当前系统调用模糊测试器的已知限制因素 [53]。

在本节的其余部分,我们将描述系统调用模糊测试器面临的主要困难,以及现有模糊测试器使用的语法规则如何试图缓解这些问题。

指针

如上所述,系统调用通常期望一个或多个参数是指向用户空间进程内存中数据结构的指针。像 Syzkaller 这样的有效模糊测试器包含这些数据结构的描述,从而能够识别指针参数,并创建和变异相应的数据结构。然而,对于简单地将变异值作为参数传递的朴素模糊测试器来说,指针是一个难题;即使某个值指向了一个有效的虚拟地址,朴素模糊测试器也无法知道应该在相应的内存位置放置什么样的变异数据。

通过语法规则,Syzkaller 能够识别系统调用何时需要指针参数。这些语法规则编码了指针应该引用的数据的长度和类型(例如平坦的缓冲区或具有单独字段的结构体)。例如,图 2 显示了 Syzkaller 对 VHOST_SET_VRING_ADDR ioctl 调用的描述(与 vhost VIRTIO 硬件卸载子系统相关)。该系统调用的最后一个参数被标记为指向 vhost_vring_addr 类型结构体的“指针”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ioctl$VHOST_SET_VRING_ADDR(fd fd_vhost,
                            cmd const[VHOST_SET_VRING_ADDR],
                            arg ptr[in, vhost_vring_addr])
 
VHOST_SET_VRING_ADDR = 1076408081
 
vhost_vring_addr {
    index flags[vhost_vring_index, int32]
    flags int32[0:1]
    desc_user_addr ptr64[out, array[int8]]
    used_user_addr ptr64[out, array[int8]]
    avail_user_addr ptr64[out, array[int8]]
    log_guest_addrs flags[kvm_guest_addrs, int64]
}

syzkaller对vhost ioctl的部分描述示例

基于描述的模糊测试器(如 Syzkaller)需要对指针参数以及内核在用户空间进程中访问的每个结构体进行详细注释。

文件描述符

在 Linux 中,文件是由内核管理的特权资源。因此,文件操作是通过系统调用发起的。首先,用户空间进程使用一个系统调用(如 opensocketpipe)打开一个文件。在内核内部,内核为每个进程维护一个已打开文件的表。由于一个进程通常会打开多个文件,因此 open 系统调用返回一个整数文件描述符(或 fd),进程可以将其视为打开文件的句柄。对于每个新打开的文件,内核分配从 0 开始的最低可用整数文件描述符。对于后续的文件相关系统调用(如 readwritemmapioctl),进程将整数文件描述符作为参数传递给内核。在内核内部,内核使用 fdget() API 在进程的文件描述符表中查找对应的文件对象,而该表包含正确处理系统调用所需的所有信息。

整数文件描述符(file-descriptors)对模糊测试工具(fuzzers)来说是一个挑战。一个典型的进程通常只会打开少量的文件。因此,模糊测试工具的随机变异很难生成与有效(即已打开)文件关联的整数系统调用参数。如前所述,文件相关系统调用的行为高度依赖于文件的类型。即使模糊测试工具猜测出了一个有效的文件描述符整数,也必须同时选择一个适用于该类型文件的系统调用(以及参数)。为了解决这些问题,系统调用模糊测试工具所使用的语法依赖于文件描述符的特殊注释。例如,在图2(第1行)中,syzkaller的描述表明,VHOST_SET_VRING_ADDR ioctl调用的第一个参数必须是一个文件描述符。描述还进一步指定,该描述符必须是fd_vhost类型。此外,Syzkaller还描述了open("/dev/vhost..")系统调用,其返回值被标注为fd_vhost类型。通过这样的丰富描述,Syzkaller确保它向VHOST_SET_VRING_ADDR ioctl调用传递了有效的文件描述符,并且这些文件描述符与之前打开的vhost文件相关联。

因此,文件描述符和指针参数关联着大的抽象输入空间(用户空间内存和内核对象)。虽然系统调用依赖于其他看似复杂的参数类型,例如“magic整数”(其中只有少数特定值是有效的)和flags字段(单个整数可以表示多个状态/设置),模糊测试工具已经通过强大的机制(例如redqueen/cmplog[5]value-profile[48])有效地识别magic值并测试通过整数传递的flags。这些挑战并非操作系统内核所独有。然而,模糊测试指针和文件描述符本质上需要考虑相应的更大的输入空间。

文件描述符的管理

Linux实现了一组用于管理文件描述符的系统调用。如上所述,opensocket等系统调用创建新的文件描述符。close系统调用则用于销毁文件描述符。此外,dup家族的系统调用可以用来复制文件描述符。例如,dup2系统调用允许进程将对文件的现有引用复制到某个特定的整数文件描述符上:

1
int dup2(int oldfd, int newfd);

dup2调用之后,与旧文件描述符oldfd关联的文件也会与新的文件描述符newfd关联。在这种情况下,内核会将用户空间提供的文件描述符号与文件相关联,而不是遵循上述默认的“最低可用”策略。

Linux中的指针API

在现代Linux系统中,内核和用户空间内存被放置在虚拟内存的两个“半区”中。如上所述,内核经常将系统调用参数解释为指针。然而,内核必须小心处理这些指针,因为它们可能来源于恶意进程。恶意指针可能指向内核地址,而不是用户地址,或者它可能指向另一个线程正在写入的位置(可能导致数据竞争)。由于在用户空间内存中访问数据是一种常见模式,Linux内核实现了特殊的API,例如copy_{from,to}_userget_user,它们必须用于访问用户空间内存中的数据。对用户空间的访问被如此严谨地处理,以至于CPU架构专门实现了防止意外用户空间指针解引用的功能:对于运行在现代Intel架构上并启用了Supervisor Mode Access Prevention(SMAP)功能的Linux系统,必须清除CR4寄存器中的一个特殊位,才能访问用户空间内存。这进一步确保了对用户空间内存的访问不会是意外的,并且应该通过集中管理的API来完成。

自动语法生成

多个研究尝试自动或半自动地收集系统调用语法。可以通过静态或动态分析技术自动收集语法。这些方法通常依赖于内核交互的种子跟踪(seed-traces),但由于缺乏能够完全覆盖内核组件的用户空间应用程序,这些种子跟踪很难收集。白盒静态和动态分析主要关注相对浅显的启发式方法,例如用于定义系统调用和特定ioctl参数类型的源代码模式。然而,自动推断的语法存在一个缺点,即分析中的一个小失误可能会对生成的语法产生严重影响。例如,如果一个ioctl依赖于多个嵌套的结构体(即一个结构体指向另一个结构体等),但分析未能将第二级结构体与第一级结构体中的指针字段关联起来,那么语法将无法描述第二级以后的任何层次。

我们将在以下部分中展示,FUZZNG并不受此限制,因为它并不是试图注释单个参数和结构体字段,而是通过重新设计并模糊测试用户空间暴露的内核输入空间来解决问题。

方法

在本节中,我们介绍了 FUZZNG 的特性,这些特性能够在无需详细注解的情况下实现高效的系统调用模糊测试。FUZZNG 通过挂钩与指针和文件相关的关键内核 API,在需要时将从现成的模糊测试工具中获得的半随机参数动态转换为每个系统调用的有效值,从而重新塑造输入空间。在第四节中,我们将解释这些解决方案如何融入 FUZZNG 的整体设计。

输入空间的重新塑造

FUZZNG 方法的核心是“重新塑造”系统调用的输入空间。一个向系统调用提供随机参数的模糊测试工具表现较差,因为它操作的是一个不完整的输入空间模型(它没有机制来有意义地模糊测试文件描述符(fd)和基于指针的参数)。然而,简单地扩展模糊测试工具可访问的输入空间,例如允许模糊测试工具将数据写入进程内存中的任意位置,并不能提高性能,因为模糊测试工具不太可能猜到系统调用实现将从中读取用户空间地址的确切位置。没有反馈机制的情况下,模糊测试工具仅仅向系统调用传递随机整数参数,并不能知道应将数据放置在何处以供指针读取,或者哪些文件描述符需要打开并传递给后续的系统调用。

“重新塑造”指的是通过 FUZZNG 为模糊测试工具提供一个动态的、参与的输入空间视图的机制。也就是说,在任何时候,FUZZNG 都知道内核正在主动访问的内存和文件描述符。通过 API 挂钩,FUZZNG 会在内核引用指针或文件描述符时暂停模糊测试输入的执行,并在继续输入执行之前采取行动填充相应的用户空间区域或文件描述符编号。因此,模糊测试工具无需详细描述文件描述符或指针参数,因为 FUZZNG 会按需拦截相关的内核访问。

实现输入空间重新塑造的一种机制是直接用模糊测试数据替换内存访问和文件描述符 API 的返回值。然而,FUZZNG 采用了一种替代方法:将模糊测试数据放置在内存访问和文件描述符 API 引用的位置。这种技术的优势在于避免了在没有挂钩的原生内核中无法实现的行为。例如,这种方法避免了 FUZZNG 在不良指针地址(例如空指针或内核指针)上提供模糊测试数据。

这种设计的一个重要优势是,当 FUZZNG 发现一个漏洞时,可以很容易地生成一个“重现器”,并将其与漏洞报告一起提供,以便在未修改的内核中(独立于 FUZZNG)触发该漏洞。为此,在模糊测试完成后,FUZZNG 将崩溃输入转换为简单的系统调用序列(类似于 Syzkaller),并省略挂钩。

通过内核 API 挂钩重新塑造输入空间,FUZZNG 使系统调用接口在无需详细系统调用描述或种子输入的情况下变得可操作。由于通过 API 挂钩提供的动态反馈,FUZZNG 自动获取了其他模糊测试工具需要离线编码在系统调用描述中的指针和文件描述符参数的详细信息。因为 FUZZNG 重新塑造了内核的基础文件描述符和内存输入空间,所以即使是复杂的接口(如 KVMio_uring),其方法也同样适用,而对于这些复杂接口,静态分析 ioctl 参数类型等自动化技术生成的语法往往细节不足。

图 3 展示了 FUZZNG 如何通过重新塑造输入空间,使内核不再因无效指针和文件描述符而拒绝大多数输入。在本节的其余部分,我们将描述 FUZZNG 针对系统调用输入空间中指针和文件描述符两个特性的具体方法。

指针

如前所述,系统调用通常依赖用户空间应用程序提供的指针。在提交系统调用后,内核会启动对指针的访问,访问的大小取决于系统调用的类型和参数。普通用户空间应用程序的开发者基于对系统调用如何处理参数的专业知识(例如文档和手册页),在调用系统调用之前会提前将指针填充为对应的数据。然而,FUZZNG 对内核如何处理单个系统调用参数并不了解。相反,FUZZNG 利用了这样一个事实:一般来说,内核代码访问用户空间内存必须通过集中化的 API,

默认情况下,随机生成的系统调用无法深入到内核代码路径,因为参数会很快触发错误条件。FUZZNG的钩子有效地“重塑”了输入空间,将无意义的指针和文件描述符参数转换为有效的参数,而不会引入不可能的行为。通过FUZZNG的内核钩子,通常会导致简单错误条件的系统调用能够产生有趣的行为。在此图和图4中,“虫洞”符号代表了FUZZNG中用于重塑输入空间的部分。

这主要是出于安全措施(例如 SMAP)。例如,内核提供了一个 copy_from_user 函数,其语义类似于 memcpy,但专门用于允许从用户空间内存复制数据到内核内存。

FUZZNGcopy_from_userget_user API 应用了轻量级的挂钩,以按需获取用户空间内存访问的动态信息。当内核组件使用这些 API 之一进行访问时,FUZZNG 会暂停该访问,并用模糊测试工具提供的数据填充相应的用户空间内存范围。因此,一旦 FUZZNG 恢复访问,内核组件自然会访问由模糊测试工具控制的数据。通过按需填充用户空间内存访问,FUZZNG 避免了识别进程内存中哪些位置应包含模糊测试生成数据的猜测工作。

用户空间访问安全机制:绝大多数内核对用户空间内存的访问是通过集中的内核API进行的。然而,在少数情况下,内核组件可能会实现自己的API版本,或禁用某些保护。例如,Kernel Virtual Machine (KVM) 子系统期望虚拟机的内存被映射到用户空间。当启动一个KVM虚拟CPU(通过KVM_RUN ioctl命令)时,内核直接指示CPU从用户空间的虚拟机内存中执行指令。在这种情况下,虚拟机的指令在非根模式下执行,并且分配给虚拟机的用户空间内存在没有SMAP保护的情况下被访问。这种行为会绕过FUZZNG对内存访问的重新塑造机制,因此必须妥善处理。

为了捕获绕过集中API的用户空间访问行为,FUZZNG采用了一种额外的机制来挂钩用户空间内存访问。为此,FUZZNG通过映射尽可能多的页面来“膨胀”模糊测试进程的地址空间(请注意,这些页面不消耗物理内存,因为操作系统仅在页面首次被访问时分配物理页)。通过映射尽可能多的虚拟页面,可以确保对用户空间内存的访问极有可能对应到一个有效的虚拟页面映射。然而,这些用户空间页面被标记为“不存在”,任何访问都会触发页面故障。

然后,使用userfaultfd(一种可以在用户空间处理页面故障的机制),FUZZNG在继续执行内核组件之前,为每个被访问的页面填充模糊测试数据。与API挂钩方法不同,基于userfaultfd的挂钩不依赖于任何内核代码模式(例如copy_from_user的使用)。因此,它能够捕获所有内核试图访问用户空间内存的情况,即使这些访问未通过集中API完成。默认情况下,该挂钩机制对每个页面仅触发一次,因为后续访问会将页面标记为存在,不会再次触发页面故障。然而,由于FUZZNG会填充整个页面,因此对同一页面的后续读取仍会返回模糊测试数据。

文件描述符

正如第二节所讨论的,内核通常期望系统调用的参数包含文件描述符(file descriptor)编号。然而,通用模糊测试工具通常会为这些参数提供随机数,这些随机数很可能无法引用内核中的有效文件对象。为了解决这一模糊测试的障碍,FUZZNG将模糊测试工具提供的文件描述符编号与进程已打开的现有文件对象相关联。

FUZZNG通过挂钩文件描述符API,确保模糊测试生成的文件描述符编号被重塑为有效的文件对象。内部,FUZZNG利用了内核将文件描述符与底层struct file对象关联的事实。struct file对象包含文件权限、偏移量、支持的操作等信息。为了将文件描述符编号解析为底层的struct file对象,内核开发人员必须使用fdget() API。因此,FUZZNG可以挂钩每次尝试解析文件描述符(无论是有效还是无效)的操作。然后,使用dup2系统调用,FUZZNG可以将模糊测试工具提供的(可能无效的)整数与有效的文件对象关联起来。这确保了任何被内核解释为文件描述符的模糊测试值都被映射到一个有效的已打开文件。

FUZZNG 系统

FUZZNG的核心是在基本的内核API(与系统调用相关)上进行挂钩。在本节中,我们将描述这些挂钩如何融入FUZZNG系统的各个组成部分。

首先,我们描述FUZZNG的模糊测试引擎——QEMU-Fuzz。QEMU-Fuzz主要负责生成输入、解释覆盖率数据以识别“有趣”的输入,以及通过虚拟机快照在每次输入执行之间重置状态。接着,我们详细介绍mod-NG,即为辅助模糊测试而对Linux内核所做的修改。mod-NG挂钩文件描述符和用户内存访问API,以及用于检测内核崩溃的错误报告函数。最后,我们描述NG-Agent,这是用于调用被模糊测试的系统调用的用户空间代理。NG-Agent负责向测试中的内核提供输入、配置kcov以收集内核覆盖率数据,并输出每个执行输入的“规范化”版本(详见第四节-C3c)。图4展示了FUZZNG的系统概览。

QEMU-Fuzz

FUZZNG的模糊测试引擎QEMU-Fuzz是一个基于虚拟机快照的模糊测试工具(图4中的1),构建在修改过的QEMU-KVM虚拟机管理程序和libFuzzer输入变异器之上。系统调用通常会修改用户空间中的寄存器或内存。它们还可能在内核中建立跨系统调用持久的状态(例如,通过read/write/seek系统调用修改的文件描述符偏移量)。这对模糊测试工具提出了挑战,因为模糊测试工具在从完全相同的初始状态执行输入时表现最佳。此外,模糊测试输入可能会超时,或者损坏并导致NG-Agent进程崩溃。

为了解决这些问题,FUZZNG针对运行在虚拟机中的测试内核执行系统调用,并在每次模糊测试输入后,通过快照恢复整个虚拟机的状态。与Syzkaller类似,FUZZNG的每个模糊测试输入表示一系列系统调用。FUZZNG的快照模糊测试确保代理进程和内核状态在每次输入后完全重置为一致的快照。这种方法不同于Syzkaller,后者使用“fork服务”在单独的进程中执行输入。与Syzkaller不同,FUZZNG没有系统调用描述文件来确保输入行为良好,不会在虚拟机中引发性能问题。

QEMU-Fuzz实现了一个专用于模糊测试的虚拟设备,虚拟机可以使用该设备初始化快照并请求重置。此外,虚拟设备接口用于确定NG-Agent(详见第四节-C)在虚拟机中期望接收新模糊测试输入的内存区域,以及虚拟机存储内核覆盖率数据的页面。

我们的FUZZNG实现概述,展示了构成FUZZNG的三个主要组件:1 QEMU-Fuzz是快照模糊测试器,负责生成输入并在每次运行后重置虚拟机状态。2 mod-NG是我们用于挂接到用户内存访问和文件描述符子系统的内核修改集合。3 NG-Agent是用于启动模糊测试系统调用并填充进程访问的进程内存的用户空间进程。

QEMU-Fuzz将每个新输入放置在虚拟机物理内存中约定的位置。当代理执行完输入后,它使用虚拟设备接口向QEMU-Fuzz请求重置。QEMU-Fuzz将虚拟机内存中的覆盖率数据提供给libFuzzer进行输入变异。

为此,我们修改了libFuzzer以支持两种kcov覆盖率数据格式(程序计数器跟踪和比较跟踪)。然后,QEMU-Fuzz将虚拟机的内存、寄存器和设备状态重置为先前初始化的快照。QEMU内置的虚拟机快照/恢复功能针对长期快照存储或实时迁移进行了优化,而非模糊测试。为了适应模糊测试特定的快照工作负载,QEMU-Fuzz实现了自定义的虚拟机重置功能,将所有快照存储在内存中,并使用KVM的脏页跟踪功能,仅重置自上次快照以来写入过的页面,从而减少开销。最后,QEMU-Fuzz使用定时器强制虚拟机在输入执行时间超过配置的超时时进行重置。

mod-NG

第三节 中,我们解释了 FUZZNG 如何通过挂钩与用户内存访问和文件描述符相关的内核代码来实现功能。为此,我们添加了一个名为 mod-NG 的内核模块(图 4 中的模块 2),该模块包含我们用于拦截通过 copy_from_user 类型 API 和文件描述符操作的代码。

copy_from_user API

mod-NG 拦截到用户内存读取的 API 调用时,它会唤醒一个用户空间的“copy-from-user”处理线程(CFU,见图 4),并向线程提供有关读取位置和大小的详细信息。CFU 线程负责用模糊测试生成的数据填充用户空间内存中的相应位置。一旦 CFU 线程通知内核其已处理完成该请求,mod-NG 就会恢复 copy_from_user 调用的执行。

需要注意的是,我们选择使用 CFU 线程和 mod-NG 的组合,而不是直接修改 copy_from_user 将模糊测试输入的字节直接复制到内核内存中,原因有以下几点:

  1. 通过在用户空间内完成读取操作,我们确保进程实际可以写入该区域。这样可以避免向不可能访问的位置(例如,未映射的内存)提供数据。
  2. mod-NG 无需感知模糊测试的输入数据。虚拟机中唯一与模糊测试输入直接交互的组件是 NG-Agent
  3. 用户空间的 userfaultfd 安全机制(图 4 中的 UFFD)用于挂钩用户内存访问(详见 第三节-B),它也是基于用户空间线程的。通过使用类似的用户空间线程处理 CFUuserfaultfd 挂钩,可以确保设计一致性:只有用户空间组件直接读取模糊测试输入。与未修改的系统类似,系统调用的输入(包括内存缓冲区)是在用户空间进程的上下文中填充的。

文件描述符 API

mod-NG 重构了由数值文件描述符引入的输入空间。mod-NG 确保任意模糊测试提供的文件描述符数字都与内核中实际的底层文件对象相关联。否则,内核会拒绝大多数系统调用,因为其中的文件描述符引用了未分配的文件描述符数字。

为了重构文件描述符空间,mod-NG 挂钩了内核分配新文件描述符时使用的 alloc_fd API,以处理诸如 open 等系统调用。通过挂钩 alloc_fdmod-NG 跟踪由代理进程分配的所有文件描述符,并将其存储在一个“文件描述符栈”(FD stack)中。

当内核使用 fdget API 获取与文件描述符数字相关联的底层文件对象时,mod-NG 会检查栈,以确定该文件描述符是否已分配。如果文件描述符已分配,mod-NG 直接恢复 API 的执行,并返回与该文件描述符关联的现有文件对象。然而,如果文件描述符尚未分配,mod-NG 会调用 dup2,将一个已存在的文件描述符(从文件描述符栈中获取)复制到传递给 fdget 的文件描述符数字上。默认情况下,mod-NG 会复制最近分配的文件描述符(栈顶)。但是,mod-NG 还向 NG-Agent 暴露了一个特殊的系统调用 fuzz-set-fd-offset,该调用用于选择从文件描述符栈顶部开始的索引,并将其传递给后续的 dup2 操作。我们将在 第四节-C3b 中详细描述该系统调用的调用方式。

崩溃挂钩

mod-NG 挂钩了内核的 dump_stack API,该 API 会在内核错误发生时被调用。在这种情况下,mod-NG 使用 Port-IO 通知 QEMU-Fuzz 保存输入作为潜在的崩溃数据以供分析。此外,mod-NG 还跟踪 NG-Agent 进程的崩溃(SEGFAULTs),并使用 Port-IO 请求 QEMU-Fuzz 立即恢复虚拟机。

代理进程可能会崩溃,因为没有任何防御机制可以防止模糊测试的系统调用覆盖用户空间进程中的敏感代码、堆等区域。代理进程的崩溃并不表明内核存在问题,但会降低模糊测试的效率,因为代理进程无法通知 QEMU-Fuzz 输入已完成执行(导致超时)。为了避免在 NG-Agent 崩溃的情况下出现超时,SEGFAULT 跟踪机制使 FUZZNG 能够提高模糊测试的性能。

NG-Agent

由于 FUZZNG 是一个系统调用模糊测试工具,而系统调用由用户空间应用程序发起,因此我们依赖一个代理进程(图 4 中的模块 2)来调用一系列模糊测试生成的系统调用。NG-Agent 是负责将 QEMU-Fuzz 生成的二进制模糊测试输入转换为通过系统调用传递的数据的组件,包括系统调用 ID、系统调用参数和由内核访问的用户空间内存内容。

在启动时,代理会读取一个配置文件,该文件标识需要被模糊测试的组件,并与 QEMU-Fuzz 建立通信。由于 QEMU-Fuzz 生成的是原始二进制输入,NG-Agent 包含一个解释器,将原始字节转换为一系列系统调用。此外,代理还启动了负责处理用户空间内存访问挂钩的 CFUUFFD 线程。

当代理从输入中读取字节时,它会组装一个“规范化”的输入版本(详见 第四节-C3c)。在输入执行完成后,代理请求虚拟机重置并从模糊测试引擎获取新的输入。

代理初始化

在启动时,NG-Agent 会初始化覆盖率收集功能、扩展进程的内存,并设置用户内存和快照接口。

配置 kcov

NG-Agent 配置 kcov 以在内核中收集覆盖率 [26]。kcov 是一个 Linux 内核代码覆盖机制,被 Syzkaller 等模糊测试工具广泛使用。通过启用 kcovNG-Agent 进程可以访问包含内核覆盖率数据的 kcov 内存区域。kcov 有两种模式:可以配置为报告被覆盖的内核程序计数器(Program Counters,PCs),或报告被覆盖的比较指令及其对应的操作数(CMP tracing)。这两种模式是互斥的(每个模糊测试进程只能追踪 PCs 或比较指令之一)。CMP tracing 模式的信息对帮助模糊测试工具自动解决代码中的约束(例如 ioctl 请求编号检查)非常有用。因此,像 Syzkaller 这样的现有模糊测试工具在模糊测试过程中会使用这两种模式。NG-Agent 实现了对这两种覆盖模式的支持。

此外,FUZZNG 扩展了 kcovCMP 模式,使其能够报告通过内核 API(如 strncmpmemcmp)执行的比较操作。此更改的灵感来源于现代用户空间模糊测试工具,这些工具可以通过挂钩与字符串和内存比较相关的函数,自动识别目标期望输入中的字符串。在 V-B 节中,我们将展示此功能如何帮助模糊测试输入中填充内核期望的字符串(通过系统调用参数接收)。在模糊测试开始之前,每个在其虚拟机中运行的 NG-Agent 实例都会查询 QEMU-Fuzz,以确定使用 PC 模式还是 CMP 模式。

扩展进程内存

通过挂钩用户空间的内存访问,FUZZNG 可以在不依赖指针参数和结构体布局(如语法描述)的情况下模糊测试复杂接口(见 II-A 节)。然而,Linux 中的普通进程默认仅映射了可用虚拟内存空间的一小部分(在 x86-64 上为 128 TB)。因此,默认情况下,模糊测试工具提供的随机地址很可能不会与任何物理内存映射相关联,从而导致 FUZZNG 无法用模糊测试工具提供的数据填充这些虚拟地址。

为了解决这个问题,NG-Agent 使用 mmap 系统调用尽可能多地映射内存,从而“扩展”其地址空间。NG-Agent 会保留一小部分内存(16MB)未分配,以便模糊测试工具生成的 mmap 系统调用仍能成功。需要注意的是,虽然底层硬件无法用实际物理内存支持约 128 TB 的映射虚拟地址,但物理内存只有在虚拟页面首次被访问时才会分配。在单次模糊测试运行期间,仅有很小一部分“扩展”内存会被访问并分配物理页面。在完成扩展步骤后,几乎所有有效的用户地址(< 0x800000000000)都与一个映射相关联。因此,随机生成的地址很可能击中 NG-Agent 映射的有效用户空间内存,FUZZNG 的挂钩机制可以填充这些内存,当内核响应系统调用时会访问这些地址。

用户内存挂钩服务线程

III-B 节所述,FUZZNG 挂钩了内核对用户空间内存的访问。为此,FUZZNG 依赖两种机制:挂钩 copy_from_user 类型的 API 和 userfaultfd。对于每种机制,NG-Agent 会初始化线程(如 CFUUFFD,见图 4),这些线程负责用模糊测试输入中的字节填充由挂钩的用户内存读取操作引用的内存。我们在 IV-C2 节中进一步描述了这些线程的功能。

与 QEMU-Fuzz 通信

NG-Agent 通过提升其权限级别(使用 Linux 的 iopl)并执行 Port-IO 指令与 QEMU-Fuzz 直接通信。每个 Port-IO 指令都会导致退出到 QEMU-Fuzz,由后者处理请求。当 NG-Agent 完成覆盖率和内存相关的初始化后,它就可以执行模糊测试输入了。为了开始模糊测试过程,NG-Agent 使用 Port-IO 将分配给模糊测试输入和 kcov 覆盖率的内存地址提供给 QEMU-Fuzz。由于 QEMU-Fuzz 使用物理地址与虚拟机内存交互,代理进程会使用 /proc/self/pagemap 接口将虚拟地址转换为物理地址。然后,NG-Agent 请求 QEMU-Fuzz 创建一个虚拟机快照并提供新的模糊测试输入。一旦 NG-Agent 解释了输入,它就会请求虚拟机重置,模糊测试过程会重复进行。

代理配置

NG-AgentQEMU-Fuzz 生成并提供的输入解释为一系列系统调用。FUZZNG 依赖代理配置(agent-config)来针对内核的特定组件。我们将内核功能的逻辑分组称为“组件”。示例包括设备驱动程序(如 console/ptmxrdmavhost)、接口(如 KVMio_uring)以及通用 API(如 bpf)。配置包括两个部分:

  1. 文件(Files):在此指定组件特定文件的路径(位于 /dev/),这些文件在模糊测试之前由代理打开(如果有)。
  2. 系统调用(System-Calls):模糊测试工具可以生成的系统调用列表。对于每个系统调用,我们指定参数数量以及每个参数的可选“掩码”。

例如,在图 1 中的 KVM 配置中,第一行指定 /dev/kvm 文件应被打开,因为它是与 KVM 子系统交互的入口点 [55]。接下来的六行简单列出了可以与 KVM 交互的系统调用。每一行表示模糊测试工具可以调用的一个系统调用及其参数数量。可选地,我们可以为某些参数提供掩码。这些掩码不是严格必要的,但有助于限制低效的系统调用。例如,在图 1 中,我们为 read/write 系统调用指定了掩码,以限制操作的最大大小。此外,我们对 mmap 系统调用应用掩码,以避免可能破坏代理覆盖率/代码区域的映射。

收集与内核组件接口相关的系统调用 ID 很容易,可以通过阅读内核文档或检查接口支持的文件 API(例如,检查 file_operations 结构中的字段)来完成。与 Syzkaller 相比,系统调用参数几乎没有限制(除了用于减少明显无效/浪费参数的掩码)。相反,我们依赖输入空间重塑挂钩和覆盖率反馈来识别具有有效/有趣参数的输入。每个参数(包括文件描述符编号和指针地址)直接从二进制模糊测试输入中获取。

NG-Agent 解释器

QEMU-FuzzNG-Agent 提供的输入只是一个字节缓冲区。为了将这些字节转换为系统调用序列,FUZZNG 实现了一个解释器。该解释器使用一个4字节的值(ASCII编码的“FUZZ”)将输入划分为单独的操作。现代模糊测试工具(例如 libFuzzer)能够自动识别此类“魔法”值,并将它们插入到输入中。解释器支持两种类型的操作:系统调用用户内存模式

系统调用

对于每个操作,NG-Agent 检查第一个字节,并通过该字节的值在 NG-Agent 配置中定义的系统调用表中选择一个系统调用。一旦确定了系统调用,NG-Agent 会读取该调用所需的参数数量(由代理配置指定),并将指定的参数应用掩码。最后,NG-Agent 使用 syscall() 的 libc API 调用系统调用。

在内部,NG-Agent 还向系统调用表添加了 fuzz-set-fd-offset 系统调用,以便模糊测试工具可以指定哪个文件描述符(fd)应用于响应未分配 fd 编号的 fdget 调用。模糊测试工具经常生成与多个文件描述符交互的输入。例如,要运行一个 KVM 虚拟机的输入,需要依次执行以下操作:

  1. /dev/kvm 文件执行 KVM_CREATE_VM ioctl 调用以创建一个 VM 的 fd;
  2. 然后对新创建的 VM fd 执行 KVM_CREATE_VCPU ioctl 调用以创建一个 VCPU fd;
  3. 最后对 VCPU fd 执行 KVM_RUN ioctl 调用。

因为 mod-NG 将新创建的 fd 存储在栈中,所以与文件描述符交互的系统调用(例如 ioctl)将默认操作栈顶的文件描述符。然而,如果在 KVM_RUN 调用后,输入尝试执行 KVM_SET_MEMORY_REGION ioctl(这是针对 VM fd 的特定调用),系统调用会失败,因为此时栈顶的文件描述符是 VCPU 的 fd。

因此,我们为输入提供了调用 fuzz-set-fd-offset 系统调用的能力,以选择栈中的某个文件描述符。例如,VM fd 位于栈的第二个位置(索引为1),模糊测试工具可以在调用 KVM_SET_MEMORY_REGION ioctl 之前调用 fuzz-set-fd-offset(1)

1
2
3
4
5
6
7
8
ioctl(kvm_fd, KVM_CREATE_VM(i.e. 0xae01), 0)
01 [00 0d 00 00][01 ae 00 00][00 00 00 00] 46 55 5a 5a
ioctl(vm_fd, KVM_CREATE_VCPU(i.e. 0xae41), 0)
01 [e9 0c 00 00][41 ae 00 00][00 00 00 00] 46 55 5a 5a
ioctl(vcpu_fd, KVM_SET_MSRS (i.e. 0x4008ae89),
(struct *kvm_msrs)(0xf75afd26) = {0x1a, 0, ��})
01 [e9 09 00 00][89 ae 08 40][26 fd 5a f7] 46 55 5a 5a
1a 00 00 00 00 00 00 00 03 4d 56 4b 46 00 00 5a ���

这是FUZZNG在对KVM进行模糊测试时发现的部分输入。灰色斜体的行是注释,表示后续字节行中代表的系统调用。在输入中,红色字节(ASCII为“FUZZ”)用于分隔操作。最左边的蓝色字节表示系统调用索引。方括号用于分隔传递给系统调用的参数。黄色字节被解释为文件描述符编号。青绿色字节表示“魔术”ioctl请求编号。橙色字节表示由内核访问的地址。底部的紫色字节用于填充内核访问。请注意,我们手动将这些注释添加到图中,仅用于解释目的。FUZZNG本身并不了解参数类型等信息。

由于模糊测试工具“猜测”文件描述符偏移值可能需要时间,在我们评估的一半模糊测试虚拟机中(参见 § V-A),我们配置模糊测试工具在系统调用失败(返回值为-1)时,自动调用 fuzz-set-fd-offset,并遍历所有打开的文件描述符。在这种模式下,模糊测试工具无需猜测栈中的 fd 偏移值,因为代理解释器会自动尝试所有可能的选项。这最大化了系统调用使用正确类型文件描述符成功执行的可能性。

用户内存访问

与系统调用操作不同,用户内存访问由内核发起,而非由 NG-Agent 发起。然而,在输入中,它们仍然表示为操作。当发生用户内存访问时,它由 CFU 线程UFFD 线程 处理(具体取决于是否使用了 copy from user 的 API)。为了用模糊测试数据填充内存中对应的位置,这些线程将解释模糊测试输入中的下一个操作为用户内存模式。

根据访问的大小以及填充该访问所需的模糊测试数据的数量,NG-Agent 使用不同的策略:

  • 小型访问(少于256字节):NG-Agent 直接从模糊测试输入中读取所需的字节数,并用它们填充内核访问的区域。这种大小的数据足以满足传递给内核的大多数结构体。
  • 大型访问:NG-Agent 从操作的第一个字节读取一个长度值,并将其解释为重复模式的长度(从后续字节中提取)。该模式用于填充内核读取的区域。

一旦 CFU / UFFD 线程 用输入中的字节填充了一个内存区域,它们会更新一个全局输入指针,以便主线程中运行的系统调用解释器知道跳过用户内存访问操作。

强制输入结构化

Syzkaller 不同,Syzkaller 的输入包含有关系统调用类型和需要填充的结构体字段的详细信息,FUZZNG 的输入并不包含这些语义信息。因此,FUZZNG 无法预先预测输入对应的系统调用序列或用户空间访问。然而,随着 FUZZNG 执行输入,它会动态获得关于输入所表示的系统调用的有价值信息,例如:

  • 被忽略/未使用的输入部分:例如,多余的系统调用参数或被掩码屏蔽的参数部分。
  • 被归一化为不同值的输入部分:例如,系统调用操作的第一个字节会根据系统调用表的大小进行归一化。
  • 填充用户内存访问操作所需的字节数

为了利用这些信息,我们对 libFuzzer 进行了修改,以支持在模糊测试期间修改输入,从而能够在执行过程中对输入进行规范化。

所有解释器操作都有具体的长度要求。然而,libFuzzer 提供的输入往往无法满足这些要求:操作可能包含太多或太少的字节。为了解决这一问题,NG-Agent 在解释模糊测试输入时动态“调整”操作的大小,以确保每个操作包含恰好所需的字节数。这样,NG-Agent 强制了输入结构化,并确保 libFuzzer 保存的输入中不包含多余字节(参见图5中的示例)。由于存储的输入中的每个字节都被实际用于操作,因此输入变异更有可能实现新的代码覆盖率。

  • 对于系统调用,如果操作没有足够的字节来满足所有系统调用参数的需求,NG-Agent 会从模糊测试输入中删除该操作。
  • 相反,如果操作后存在多余字节,FUZZNG 会从输入中移除多余部分。
  • 同样,FUZZNG 确保用户内存访问操作中的字节数量与访问所需的大小完全匹配。如果字节不足,FUZZNG 会使用伪随机数生成器(从 rdtsc 获取种子)插入随机数据,直到操作的大小与访问匹配。结果输入将包含一个大小完美匹配的用户内存访问操作(如果需要,用随机数据填充),并可用于未来的执行。

此外,NG-Agent 将系统调用参数掩码直接应用于输入。例如,如果代理配置指定参数有一个掩码 0xF000,而模糊测试输入提供了参数 0xDEADBEEF,NG-Agent 会将 0xDEADBEEF 替换为 0x0000B000。NG-Agent 还对用于选择系统调用类型的字节进行归一化(将其限制在模糊测试可访问的系统调用数量范围内)。

在 NG-Agent 执行完输入后,它会将规范化版本返回给 QEMU-Fuzz。默认情况下,libFuzzer 不支持修改模糊测试数据的长度或内容,因此我们对其进行了微小的修改。最终存储在语料库中的输入不包含任何多余字节,并且操作大小保证匹配必要的字节数。

需要注意的是,由于 FUZZNG 存储的是“规范化”输入,使用伪随机数生成器调整操作大小不会导致非确定性问题:任何随机生成的字节都会存储在语料库中。

总之我们的 FUZZNG 实现结合了以下几部分:

  1. 快照模糊测试引擎(QEMU-Fuzz):用于生成输入并重置状态;
  2. 用户空间代理(NG-Agent):用于调用系统调用并用模糊数据填充相关内存;
  3. 内核模块(mod-NG):用于挂接与系统调用相关的内核 API,并向代理提供关于 API 调用的动态反馈。

评估

我们评估了 FUZZNG 的模糊测试能力,以回答以下研究问题:

  • RQ1:与最先进的基于语法的系统调用模糊测试工具相比,FUZZNG 是否能够实现有竞争力的覆盖率?(参见 § V-B)
  • RQ2:与 Syzkaller 的 syzlang 描述文件相比,FUZZNG 的配置文件平均大小是多少?(参见表 I)
  • RQ3FUZZNG 能否在 Linux 内核中发现新漏洞?(参见 § V-D)
  • RQ4FUZZNG 的快照模糊测试性能如何与 Syzkaller 的 fork-server 模式相比?(参见 § V-E)

实验设置

所有实验均在配备 双路 Intel Xeon E5-2600 v3 系列 CPU 和 192 至 256 GB 内存的服务器上进行。所有模糊测试实验均针对 Linux Kernel 5.12 进行。FUZZNG 使用多个 QEMU-Fuzz 虚拟机(VM) 在多核系统上对内核目标进行模糊测试,并为每个 VM 配置不同的模糊测试选项:

覆盖模式:如 § IV-C1a 中所述,KCOV 支持两种覆盖模式:PC 覆盖CMP 覆盖

  • PC 覆盖使 FUZZNG 能够一致地判断何时到达新代码。
  • CMP 覆盖允许 FUZZNG 解决输入约束(如特定的 ioctl 请求号)。
    FUZZNG 将一半的模糊测试 VM 配置为 PC 覆盖模式,另一半配置为 CMP 覆盖模式,实现了在存储新边缘输入和解决复杂比较值之间的平衡。

无错误输入模式FUZZNG 将八分之一的 VM 配置为丢弃导致系统调用失败(返回值为 -1)的输入。这种模式鼓励模糊测试工具发现高质量的系统调用序列,从而深入到内核的深层状态。由于错误路径同样需要被测试,这一功能仅在八分之一的 VM 中启用。

文件描述符“级联”模式:如 § IV-C3a 所述,在此模式下,当涉及文件描述符(fd)的系统调用失败(返回值为 -1)时,FUZZNG 使用 fuzz-set-fd-offset 功能遍历所有可用的文件描述符,重复该系统调用,直到成功或所有打开的文件描述符均尝试完毕。这种模式减少了模糊测试工具正确猜测文件描述符的需求,但由于许多系统调用会返回错误,这会减慢输入执行速度,因此仅对一半的 VM 启用此模式。

需要注意的是,这些选项并不是互斥的(例如,一个 VM 可以同时启用 CMP 覆盖模式和级联模式)。所有并行模糊测试工具会将新发现的有趣输入存储在同一个语料库目录中。因此,即使某个输入仅在单一模式下达到了新覆盖,所有模糊测试实例也会对其进行变异。

覆盖率

FUZZNG 的主要贡献在于其能够以最小的设置成本对复杂的内核接口进行模糊测试,与当前的系统调用模糊测试工具相比,这是一项显著优势。我们将 FUZZNG 的覆盖率表现与 Linux 内核模糊测试工具的事实标准 Syzkaller 以及使用关系学习技术优化 Syzkaller 语法变异效率的 Healer 进行了比较。

组成部分 最大覆盖率 syzkaller healer FuzzNG
边数 syzlang 代码行 边数 边数 配置 代码行
bpf 15359 3623 864 112 3572 1
video4linux 1004 563 381 446 567 4
rdma 4014 562 1474 * 591 5
binder 2506 340 272 56 344 6
cdrom 956 138 351 120 144 5
kvm 34924 9213 891 8755 9468 7
vhost_net 415 218 157 210 225 9
drm 12503 2296 745 1978 2138 7
io_uring 3413 982 343 986 1003 6
vt_ioctl 332 142 381 138 162 9
Geo. Mean与Syzkaller比较平均值 76.52% 102.53% 1.67%
66.95% 102.41% 1.09%

FUZZNG、Syzkaller和Healer的覆盖率比较。请注意,为了一致性,“Syzlang”代码行(LoC)一列不包括Syzkaller依赖的任何额外的测试工具(用C和Go编写)。覆盖率是5次运行的平均值。

所有三种模糊测试工具均针对相同的内核版本进行配置。对于每个内核组件,我们提供了单独的覆盖率允许列表(coverage allowlist),该列表仅将 KCOV 覆盖率插桩应用于实现该组件的源文件(如 KVM、BPF 等)。因此,FUZZNGSyzkallerHealer 仅报告与相关组件交互的系统调用的覆盖率(并存储相关输入)。我们根据以下标准选择了用于模糊测试的组件:

  • Syzkaller(以及由此扩展的 Healer)必须支持该组件(即 Syzkaller 仓库中包含相关的手动编写描述文件)。
  • 该组件必须可用于 x86-64 Linux
  • 该组件必须是一个 系统调用接口
  • 该组件的描述文件必须是全面的。为了确保比较的公平性和通用性,我们根据 syzlang 描述文件的大小对组件进行排序,并选择了描述文件最大的组件,这些组件模糊测试的是定义明确的接口。

我们在 20 个核心上对每个组件进行了为期 168 小时(7 天)的模糊测试。我们的边缘覆盖率结果(取 3 次运行的平均值)如表 I 所示。FUZZNG 在 7 个组件上的覆盖率超过了 Syzkaller。对于剩下的 3 个组件,FUZZNG 的边缘覆盖率与 Syzkaller 相差不到 7%。平均而言,FUZZNG 达到了 Syzkaller 覆盖率的 102.5%。

Healer 并未覆盖 RDMA 代码,因为它依赖于 Syzkaller 的旧版本,而该版本不包含 RDMA 的描述文件。我们发现 Healer 的开源版本(2efbb44c7d)在我们评估的组件上获得的覆盖率低于 SyzkallerHealer 的设计目标是高效地识别系统调用之间的关系。在我们的内核配置中,仅目标组件被插桩以获取覆盖率。因此,仅增加目标组件覆盖率的系统调用会被添加到语料库中。在这种情况下,HealerSyzkaller 的改进受到限制。此外,Healer 的仓库维护者提到,开源版本与 Healer 论文中使用的私有版本相比存在许多限制,这可能是覆盖率差异的主要原因 [52], [3]。

我们比较了 SyzkallerHealerFUZZNG 的独特覆盖率结果。每个组件的结果如图 6 所示。我们手动检查了覆盖率,发现 SyzkallerHealer 覆盖了 FUZZNG 未覆盖的一些边缘,其主要原因如下:

Syzkaller 和 Healer 支持故障注入:这使得模糊测试工具可以强制内核 API 调用失败(例如 SLAB 分配、futex 调用)。由于 FUZZNG 未实现这一功能,因此某些内核的错误处理代码未被覆盖,而 SyzkallerHealer 进行了覆盖。

多线程执行测试用例Syzkaller(以及使用其执行器的 Healer)能够使用多个线程执行测试用例。目前,FUZZNG 仅从单线程运行所有系统调用。因此,与任务引用计数相关的组件代码仅被 Syzkaller 覆盖。

KVM 的部分代码:许多仅被 Syzkaller 覆盖的 KVM 代码与不同 x86 操作模式(实模式、保护模式和长模式)的 VM 指令仿真有关。由于这些模式的 VM 设置差异显著,输入生成的反馈无法引导模糊测试工具进入不同的仿真上下文。Syzkaller 使用显式编码操作模式的虚拟系统调用,这不需要复杂的变异即可触发相关代码。然而,FUZZNG 能够完全覆盖某些指令仿真例程(涵盖所有 x86 模式),因此如果给予更多时间,FUZZNG 可能会覆盖更多此类代码。

BPF 的问题:我们发现 FUZZNG 的基于 libfuzzer 的变异器在生成有效的 BPF 程序方面速度较慢。虽然 FUZZNG 很快生成了最小的有效 16 字节 BPF 程序,但由于生成更长的程序需要同时插入字节并修改描述程序大小的长度字段,FUZZNG 无法生成大的 BPF 程序,从而限制了覆盖率。未来可以通过使变异器对长度字段具有感知能力来解决这一问题,FUZZNG 可以通过关联字节值和用户空间访问长度来识别这些字段。

然而,如图6所示,FUZZNG也覆盖了Syzkaller+Healer未能覆盖的代码部分,除了binder组件。并且,已有研究表明,集成模糊测试(ensemble-fuzzing)优于单个模糊测试工具[11]。因此,使用FUZZNG和基于Syzkaller的技术进行集成模糊测试将是有益的。由于FUZZNG和Syzkaller各自有自己的输入表示,因此协同模糊测试需要一个能够在输入格式之间进行转换的适配器。

此外,我们监控了在模糊测试KVM时,Syzkaller和FUZZNG随着时间推移所实现的覆盖率。结果如图7所示。正如预期的那样,Syzkaller由于其全面的语法套件,最初明显优于FUZZNG,然而其覆盖率很快趋于平稳。值得注意的是,FUZZNG的无语法方法最终在模糊测试进行到第60小时时超过了Syzkaller。使用FUZZNG的潜在好处显而易见,因为其覆盖率的提升不受限于手动编写的语法。FUZZNG在覆盖率上的初始“滞后”是可以预期的。然而,由于FUZZNG会存储语料库输入,后续的模糊测试运行将从高覆盖率开始,使初始运行时的滞后不再成为问题。相反,FUZZNG不依赖语法的特性使其能够在无需人工干预的情况下继续发现新代码。

Syzkaller+Healer和FUZZNG独特覆盖的边缘

在对KVM进行模糊测试时,Syzkaller与FUZZNG之间的覆盖率比较。由于其详尽的描述,Syzkaller能够迅速在KVM上实现高覆盖率。然而,最终FUZZNG超过了Syzkaller

配置大小 (Config-Size)

我们在表 1 中比较了 FUZZNG 的配置文件大小与 Syzkaller 的 syzlang 描述文件大小。这一比较反映了为每个模糊测试工具新增支持组件所需的实现成本。需要注意的是,对于 Syzkaller,我们仅包含 syzlang 描述文件的行数,并未计算任何组件特定的测试挂载代码,因为这些代码与 Syzkaller 的其他无关代码交织在一起。因此,我们低估了为 Syzkaller 添加新组件支持所需的代码量。平均而言,FUZZNG 的配置文件比 Syzkaller 的描述文件小 98.3%

Bug 检测 (Bug-finding)

FUZZNG 的覆盖率评估主要集中于那些 Syzkaller 已有详尽描述并已连续多年使用 Syzkaller 进行模糊测试的组件。因此,我们并不期望从这些代码中发现大量新漏洞。然而,FUZZNG 在 Syzkaller 已经模糊测试的代码中发现了一些未知漏洞。此外,我们选取了 3 个未被 Syzkaller 模糊测试的驱动程序(mmcblkmegaraidnvme),并为其创建了 FUZZNG 配置文件(配置总共仅为 17 行)。FUZZNG 总共在 Linux 内核中发现了 9 个未知漏洞(见附录)。其中,5 个漏洞位于已经有 syzlang 描述并被 Syzkaller 覆盖良好的组件中。FUZZNG 在所有 3 个未被 Syzkaller 描述的组件中都发现了漏洞。这些漏洞均是在 120 小时的测量周期内发现的,目前正在进行负责任的披露。接下来我们讨论三个案例研究。

**KVM 仿真代码中的空指针解引用漏洞:**FUZZNG 在 KVM 的 emulate_int 代码中发现了一个空指针解引用漏洞,该代码负责仿真中断。为了发现此漏洞,FUZZNG 使用了一系列独立的 ioctl 调用来创建虚拟机(VM)、虚拟 CPU(VCPU),为虚拟机创建内存槽并启动虚拟机。当 CPU 访问虚拟机的内存以运行指令时,FUZZNG 用模糊测试数据填充了相应的内存区域。随后,FUZZNG 生成的指令触发了 VMEXIT,到达了 KVM 的仿真代码,此时由于虚拟化上下文的格式错误,发生了空指针解引用。

虽然 Syzkaller 覆盖了 emulate_int 代码,但未能发现此漏洞,因为虚拟机的设置完全由硬编码的挂载代码处理。这些挂载代码设置了寄存器和页表,而虚拟机中执行的指令由一个指令生成器生成,该生成器设计用于创建格式良好的指令序列。而 FUZZNG 不依赖于任何 KVM 特定的挂载代码。因此,尽管 FUZZNG 达到与 Syzkaller 相同覆盖率的时间较长,但 FUZZNG 的变异器对调用的系统调用具有完全控制权,而不受描述文件和挂载代码的限制。这使得 FUZZNG 能够发现其他模糊器由于语法限制无法充分测试的代码中的漏洞。

**io_uring 任务退出代码中的空指针解引用漏洞:**FUZZNG 在 io_uring 的线程管理代码中发现了一个空指针解引用漏洞。如果调用 io_uring 的进程在 io_uring 线程管理代码执行时发送中止信号,则可能会暴露潜在的竞争条件,因为线程管理代码会更新任务的 IO 相关位图(此时触发空指针解引用)。在此场景中,NG-Agent 在用户态线程 userfaultfd 中中止了进程,从而触发了该漏洞。尽管 Syzkaller 对 io_uring 进行了模糊测试,但未能捕获该漏洞。

**MegaRAID 代码中的 Use-after-free 漏洞:**FUZZNG 在 MegaRAID SAS RAID 控制器的驱动程序代码中发现了一个 use-after-free 漏洞。MegaRAID 驱动程序使用实例结构体来跟踪每个 MegaRAID 设备的状态。FUZZNG 生成了一个输入,该输入触发了一个 ioctl 调用,导致实例在尝试发出管理命令之前被释放,而该命令试图将一个与 DMA 相关的物理地址写入已释放的结构体,从而触发了该漏洞。值得注意的是,Syzkaller 并未包含此设备的任何描述,因此未能发现此漏洞。

每秒执行次数 (Executions Per Second)

为了便于测试用例之间的清理工作,FUZZNG 实现了基于虚拟机快照的模糊测试方法。而 Syzkaller 则使用轻量级的 fork-server 方法。我们在 4 核的硬件上使用 FUZZNG 和 Syzkaller 对复杂但大体独立于硬件的 BPF 接口进行了 24 小时的模糊测试。结果发现,FUZZNG 平均每秒每核执行 154 个测试用例,而 Syzkaller 平均每秒每核执行 177 个测试用例。对于所有被模糊测试的组件,FUZZNG 和 Syzkaller 的总执行次数没有显著差异。因此,FUZZNG 的覆盖率优势源于其对输入空间的重新塑造,而非测试执行速率的显著差异。

讨论

尽管 FUZZNG 的实验结果表现良好,我们在此简要讨论其局限性以及未来改进的方向。

其他内核模糊测试工具

内核一直是模糊测试的重要目标。大多数内核模糊测试工具依赖于精细的、手动编写或推断的系统调用语法描述。而 FUZZNG 提出了基于运行时挂钩的技术,避免了对详细语法描述的需求。然而,其他模糊测试工具在内核模糊测试问题空间的不同部分也有研究。以下讨论了 FUZZNG 是否与现有的方法兼容。

Moonshine 收集并提取系统调用跟踪(通过 strace),并将其转化为可用于 Syzkaller 的种子。未来的研究可以将 Moonshine 应用于为 FUZZNG 生成种子(Moonshine 的论文指出为非 Syzkaller 模糊测试工具添加支持是很简单的)[38]。

Healer 使用关系学习来提高 Syzkaller 的变异效率[38]。我们在第 V-B 节的实验表明,当模糊测试单个组件时,Healer 的效果有限。然而,如果未来的研究将 FUZZNG 扩展为对整个内核进行模糊测试,Healer 的技术可以用于学习 FUZZNG 生成和执行的系统调用之间的关系,从而提高模糊测试效率。

其他内核模糊测试工具(如 Difuze 和 SyzGen)旨在为接口自动恢复 Syzlang 描述。这些方法依赖于生成接口语法描述和使用语法进行模糊测试的分阶段过程。而 FUZZNG 则在单个阶段中操作,通过重塑输入空间来进行模糊测试,从而省去了详细语法的需求。

Difuze 对内核代码进行静态分析,以自动推断 ioctl(输入输出控制命令)设备接口的描述[12]。尤其是,Difuze 特别关注恢复 ioctl 命令值和参数类型(通过对 copy_from_user 参数的操作数进行跨过程类型传播分析)。FUZZNG 通过运行时挂钩 copy_from_user 操作并收集 KCOV 的比较覆盖信息,自动推断 ioctl 命令值。然而,未来的研究可以借鉴 Difuze 的静态分析阶段,为 FUZZNG 自动生成配置变体(例如,通过识别所有可以与子系统交互的系统调用)。

SyzGen 针对闭源内核(如 MacOS),应用符号执行技术自动恢复接口语法。SyzGen 能够恢复参数类型(如字符串、字节数组、指针和长度字段),并生成 Syzkaller 描述文件。由于 FUZZNG 通过重塑系统调用输入空间,能够透明地模糊测试指针和数组,因此无需显式语法描述即可实现这些目标。SyzGen 的符号执行技术还可以自动推断整数参数的范围,以及表示标志字段的整数参数。然而,我们发现现成的模糊测试引擎(如 libFuzzer)在没有这些字段注解的情况下表现良好。不过,SyzGen 的技术仍可用于为 FUZZNG 自动提供参数类型的反馈(无需显式语法),从而可能进一步提高模糊测试效率。

全内核模糊测试

默认部署的 Syzkaller 会模糊测试所有具有 Syzlang 描述的系统调用。这种方法能够触及(并发现)需要同时与多个内核组件交互的代码行。目前,FUZZNG 不能随意打开命名文件,我们依赖配置和覆盖过滤器将模糊测试限制在单个组件上。然而,最近的研究表明,可以通过观察覆盖率,利用“关系学习”自动推断与每个文件相关的系统调用和常见系统调用序列[52]。通过将这些技术应用于 FUZZNG,并调整变异器以检测和生成有意义的输入序列,可能实现对整个内核的模糊测试,而无需将模糊测试限制在单个组件上或依赖配置文件。

相关工作

模糊测试在学术界受到广泛关注。本节简要概述了与内核模糊测试相关的工作。模糊测试复兴的主要催化剂是 American Fuzzy Lop (AFL) [62] 的发布,它推广了针对各种软件的覆盖率引导模糊测试。研究人员专注于提高模糊测试性能,包括输入调度[25]、[58]、[43],变异算法[35]、[8]、[42],以及输入反馈[4]、[63]、[17]的改进。其他系统致力于使用符号执行技术[61]、[29]、[28]克服障碍,例如对“魔数”的比较和校验和[41]。模糊测试工具如 AFL 的 laf-intel[30] 和 libFuzzer[48] 已应用源码插桩技术,以识别与魔数比较的操作并生成能够通过这些比较的输入。

其他研究将模糊测试适配于复杂目标,例如代码解释器[60]、[56]、[22]、[19],编译器[31]、[10]、[34],网络协议[6]、[16]、[13],以及虚拟设备[20]、[37]、[46]、[45]、[7]。V-Shuttle[39] 展示了无需语法即可通过挂钩关键直接内存访问(DMA)API,对复杂的虚拟机监控程序进行模糊测试。

最近,基于快照的模糊测试得到了广泛关注,尤其是针对大型、有状态的模糊测试目标。Agamotto 基于 QEMU 引入了高性能快照技术,用于模糊测试[50]。Agamotto 支持在目标的不同执行点创建多个快照,以加速模糊测试。Nyx 基于 QEMU/KVM 构建,实现了快速的寄存器、内存和虚拟设备快照功能,用于模糊测试[45]。类似地,FUZZNG 实现了一个简单的基于 QEMU 的快照模糊测试工具 QEMU-Fuzz,该工具专门用于模糊测试 Linux 内核并接受 KCOV 格式的覆盖率信息。

操作系统内核在学术界受到了广泛关注,已有专门针对内核竞争条件[24]、文件系统[59]和外围接口[49]的模糊测试系统。同样,VIA[21]对操作系统驱动进行模糊测试,以识别可能在机密计算环境中危及安全保证的漏洞,在这种环境中,虚拟设备代码是不被信任的。kAFL引入了基于硬件的覆盖收集机制,以便在不需要源码插桩的情况下对操作系统内核进行覆盖引导模糊测试[47]。与这些工作不同,FUZZNG专注于减少对描述和工具的需求,以进行通用系统调用模糊测试。

系统调用接口在操作系统模糊测试社区中受到了最多的关注。从90年代开始,已经有多个模糊测试工具通过生成随机参数来运行,例如tsys、iknowthis、sysfuzz、xnufuzz和kg crashme[54]。像Trinity这样的系统调用模糊测试工具通过加入系统调用描述改进了简单的系统调用生成算法[54]。随着覆盖引导模糊测试在用户空间应用中获得关注,Syzkaller被创建以结合基于描述的模糊测试工具和覆盖引导来对Linux进行模糊测试。今天,Syzkaller是最受欢迎的系统调用模糊测试工具,已被纳入Linux内核开发周期,并被移植到XNU、FreeBSD和Windows等操作系统[14]。Syzkaller已向Linux内核开发者报告了数千个漏洞,并成为内核开发生命周期中的重要组成部分。与过去的方法不同,FUZZNG利用内核钩子在无需详细系统调用描述的情况下实现了与Syzkaller相当的覆盖。

一些研究旨在自动生成系统调用描述。Difuze通过对内核代码进行静态分析,自动推断设备接口的描述以进行模糊测试[12]。IMF依靠应用程序钩子收集的内核API交互日志,推断macOS系统调用的语法[18]。SyzGen依赖于手动收集的日志追踪的数据挖掘和符号执行,自动创建macOS系统调用语法[9]。值得注意的是,所有这些系统都专注于自动生成系统调用描述,但没有一项工作对具有明确手动规范的接口与Syzkaller进行比较。KSG使用符号执行自动生成能够与Syzkaller实现竞争性覆盖的syzlang描述,但源代码尚未发布[51]。与语法生成技术不同,FUZZNG重塑了内核的输入空间,使其更适合模糊测试,而不是依赖种子追踪和广泛的静态/动态分析阶段。

其他学术研究则专注于提高Syzkaller的性能,而不是直接生成语法。Moonshine依赖于来自现实世界程序的系统调用种子追踪,以改进Syzkaller的描述[38]。Healer应用关系学习来改进Syzkaller的系统调用序列变异算法。SyzVegas利用机器学习技术来提高Syzkaller的覆盖率[57]。Agamotto利用动态虚拟机快照跳过在Syzkaller输入中常见的系统调用,从而提高模糊测试吞吐量。HFL通过符号执行扩展了Syzkaller[27]。与这些方法不同,FUZZNG不依赖于手动或“学习”的系统调用行为描述。相反,FUZZNG将旧的随机参数模糊测试工具的技术与新的覆盖引导技术相结合,并重塑系统调用输入空间,创建了一个在与基于描述的方法相比时实现竞争性覆盖的模糊测试系统。

结论

FUZZNG是第一个能够在没有手动编写的系统调用描述或对源码/种子程序进行先验分析的情况下生成复杂系统调用交互的模糊测试工具。FUZZNG依赖于操作系统内核的基本特性来“重塑”系统调用接口,消除由指针和文件描述符参数造成的模糊测试障碍。FUZZNG的核心功能是简单地将通用模糊测试引擎(libFuzzer)的二进制输入解释为系统调用序列。mod-NG中实现的钩子透明地允许FUZZNG在内核访问它们之前即时填充文件描述符和复杂的数据结构。我们对FUZZNG原型的评估显示,它实现了Syzkaller覆盖率的102.5%,但每个组件的配置代码行数仅为其1.7%。此外,由于FUZZNG不依赖于任何特定组件的工具,这可能导致漏洞无法访问,FUZZNG在Syzkaller覆盖的函数中发现了漏洞。此外,尽管我们的评估集中在经过多年高覆盖模糊测试的Linux内核的测试良好的组件上,我们仍然发现了9个以前未知的漏洞,我们将负责任地披露这些漏洞。我们将开源所有FUZZNG代码,并与上游项目合作以整合FUZZNG,以便它能继续为Linux内核社区带来好处。

致谢

我们感谢匿名评审员的宝贵意见和反馈。我们承认,本文中报道的计算工作是在波士顿大学研究计算服务管理的共享计算集群和CloudLab[15]上进行的。该工作得到了国家科学基金会(NSF)CNS-1942793号资助和Red Hat协作研究孵化奖(2023-01-RH11)的支持。


[培训]科锐逆向工程师培训第53期2025年7月8日开班!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 15661
活跃值: (18963)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
2
本文使用ChatGPT-4o翻译而成,如有错误之处,请斧正
文中表格因为编辑格式原因,和原文有出入,如有学术参考之意图,请从附件下载原文查看正确的表格
感谢原文作者的工作
2025-1-18 00:19
0
游客
登录 | 注册 方可回帖
返回