【外文翻译- 没有语法也没关系:在没有系统调用说明的情况下实现对 Linux 内核的模糊测试】此文章归类为:外文翻译。
整个计算生态系统的完整性取决于操作系统(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 (®s, 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, ®s)) 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 节),这表明手动编写的描述往往可能对接口产生不充分或过度拟合的情况。
综上所述,我们的贡献包括:
为了践行开放科学的精神,我们将在 f8dK9s2c8@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 数百个系统调用实际上是通向数千万行内核代码的一个狭窄入口。
像 Syzkaller 和 Trinity 这样的系统调用模糊测试器依赖语法规则或规则库来生成有效的系统调用及其关联的文件描述符和指针参数。如果一个不具备语法规则的模糊测试器只是简单地将生成的整数作为系统调用的参数,那么由于系统调用输入空间的巨大范围(由指针和文件描述符参数所引起),这样的模糊测试器将很快变得毫无用处(详见第一节讨论)。因此,当前的系统调用模糊测试器依赖于对内核接口的广泛语法描述。例如,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 中,文件是由内核管理的特权资源。因此,文件操作是通过系统调用发起的。首先,用户空间进程使用一个系统调用(如 open
、socket
或 pipe
)打开一个文件。在内核内部,内核为每个进程维护一个已打开文件的表。由于一个进程通常会打开多个文件,因此 open
系统调用返回一个整数文件描述符(或 fd),进程可以将其视为打开文件的句柄。对于每个新打开的文件,内核分配从 0 开始的最低可用整数文件描述符。对于后续的文件相关系统调用(如 read
、write
、mmap
和 ioctl
),进程将整数文件描述符作为参数传递给内核。在内核内部,内核使用 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实现了一组用于管理文件描述符的系统调用。如上所述,open
和socket
等系统调用创建新的文件描述符。close
系统调用则用于销毁文件描述符。此外,dup
家族的系统调用可以用来复制文件描述符。例如,dup2
系统调用允许进程将对文件的现有引用复制到某个特定的整数文件描述符上:
1 | int dup2( int oldfd, int newfd); |
在dup2
调用之后,与旧文件描述符oldfd
关联的文件也会与新的文件描述符newfd
关联。在这种情况下,内核会将用户空间提供的文件描述符号与文件相关联,而不是遵循上述默认的“最低可用”策略。
在现代Linux系统中,内核和用户空间内存被放置在虚拟内存的两个“半区”中。如上所述,内核经常将系统调用参数解释为指针。然而,内核必须小心处理这些指针,因为它们可能来源于恶意进程。恶意指针可能指向内核地址,而不是用户地址,或者它可能指向另一个线程正在写入的位置(可能导致数据竞争)。由于在用户空间内存中访问数据是一种常见模式,Linux内核实现了特殊的API,例如copy_{from,to}_user
和get_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 重新塑造了内核的基础文件描述符和内存输入空间,所以即使是复杂的接口(如 KVM 和 io_uring),其方法也同样适用,而对于这些复杂接口,静态分析 ioctl 参数类型等自动化技术生成的语法往往细节不足。
图 3 展示了 FUZZNG 如何通过重新塑造输入空间,使内核不再因无效指针和文件描述符而拒绝大多数输入。在本节的其余部分,我们将描述 FUZZNG 针对系统调用输入空间中指针和文件描述符两个特性的具体方法。
如前所述,系统调用通常依赖用户空间应用程序提供的指针。在提交系统调用后,内核会启动对指针的访问,访问的大小取决于系统调用的类型和参数。普通用户空间应用程序的开发者基于对系统调用如何处理参数的专业知识(例如文档和手册页),在调用系统调用之前会提前将指针填充为对应的数据。然而,FUZZNG 对内核如何处理单个系统调用参数并不了解。相反,FUZZNG 利用了这样一个事实:一般来说,内核代码访问用户空间内存必须通过集中化的 API,
默认情况下,随机生成的系统调用无法深入到内核代码路径,因为参数会很快触发错误条件。FUZZNG的钩子有效地“重塑”了输入空间,将无意义的指针和文件描述符参数转换为有效的参数,而不会引入不可能的行为。通过FUZZNG的内核钩子,通常会导致简单错误条件的系统调用能够产生有趣的行为。在此图和图4中,“虫洞”符号代表了FUZZNG中用于重塑输入空间的部分。
这主要是出于安全措施(例如 SMAP)。例如,内核提供了一个 copy_from_user 函数,其语义类似于 memcpy,但专门用于允许从用户空间内存复制数据到内核内存。
FUZZNG 对 copy_from_user 和 get_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的核心是在基本的内核API(与系统调用相关)上进行挂钩。在本节中,我们将描述这些挂钩如何融入FUZZNG系统的各个组成部分。
首先,我们描述FUZZNG的模糊测试引擎——QEMU-Fuzz。QEMU-Fuzz主要负责生成输入、解释覆盖率数据以识别“有趣”的输入,以及通过虚拟机快照在每次输入执行之间重置状态。接着,我们详细介绍mod-NG,即为辅助模糊测试而对Linux内核所做的修改。mod-NG挂钩文件描述符和用户内存访问API,以及用于检测内核崩溃的错误报告函数。最后,我们描述NG-Agent,这是用于调用被模糊测试的系统调用的用户空间代理。NG-Agent负责向测试中的内核提供输入、配置kcov以收集内核覆盖率数据,并输出每个执行输入的“规范化”版本(详见第四节-C3c)。图4展示了FUZZNG的系统概览。
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使用定时器强制虚拟机在输入执行时间超过配置的超时时进行重置。
在 第三节 中,我们解释了 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
将模糊测试输入的字节直接复制到内核内存中,原因有以下几点:
userfaultfd
安全机制(图 4 中的 UFFD)用于挂钩用户内存访问(详见 第三节-B),它也是基于用户空间线程的。通过使用类似的用户空间线程处理 CFU 和 userfaultfd
挂钩,可以确保设计一致性:只有用户空间组件直接读取模糊测试输入。与未修改的系统类似,系统调用的输入(包括内存缓冲区)是在用户空间进程的上下文中填充的。mod-NG 重构了由数值文件描述符引入的输入空间。mod-NG 确保任意模糊测试提供的文件描述符数字都与内核中实际的底层文件对象相关联。否则,内核会拒绝大多数系统调用,因为其中的文件描述符引用了未分配的文件描述符数字。
为了重构文件描述符空间,mod-NG 挂钩了内核分配新文件描述符时使用的 alloc_fd
API,以处理诸如 open
等系统调用。通过挂钩 alloc_fd
,mod-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 能够提高模糊测试的性能。
由于 FUZZNG 是一个系统调用模糊测试工具,而系统调用由用户空间应用程序发起,因此我们依赖一个代理进程(图 4 中的模块 2)来调用一系列模糊测试生成的系统调用。NG-Agent 是负责将 QEMU-Fuzz 生成的二进制模糊测试输入转换为通过系统调用传递的数据的组件,包括系统调用 ID、系统调用参数和由内核访问的用户空间内存内容。
在启动时,代理会读取一个配置文件,该文件标识需要被模糊测试的组件,并与 QEMU-Fuzz 建立通信。由于 QEMU-Fuzz 生成的是原始二进制输入,NG-Agent 包含一个解释器,将原始字节转换为一系列系统调用。此外,代理还启动了负责处理用户空间内存访问挂钩的 CFU 和 UFFD 线程。
当代理从输入中读取字节时,它会组装一个“规范化”的输入版本(详见 第四节-C3c)。在输入执行完成后,代理请求虚拟机重置并从模糊测试引擎获取新的输入。
在启动时,NG-Agent 会初始化覆盖率收集功能、扩展进程的内存,并设置用户内存和快照接口。
NG-Agent 配置 kcov 以在内核中收集覆盖率 [26]。kcov 是一个 Linux 内核代码覆盖机制,被 Syzkaller 等模糊测试工具广泛使用。通过启用 kcov,NG-Agent 进程可以访问包含内核覆盖率数据的 kcov 内存区域。kcov 有两种模式:可以配置为报告被覆盖的内核程序计数器(Program Counters,PCs),或报告被覆盖的比较指令及其对应的操作数(CMP tracing)。这两种模式是互斥的(每个模糊测试进程只能追踪 PCs 或比较指令之一)。CMP tracing 模式的信息对帮助模糊测试工具自动解决代码中的约束(例如 ioctl
请求编号检查)非常有用。因此,像 Syzkaller 这样的现有模糊测试工具在模糊测试过程中会使用这两种模式。NG-Agent 实现了对这两种覆盖模式的支持。
此外,FUZZNG 扩展了 kcov 的 CMP 模式,使其能够报告通过内核 API(如 strncmp
和 memcmp
)执行的比较操作。此更改的灵感来源于现代用户空间模糊测试工具,这些工具可以通过挂钩与字符串和内存比较相关的函数,自动识别目标期望输入中的字符串。在 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 会初始化线程(如 CFU 和 UFFD,见图 4),这些线程负责用模糊测试输入中的字节填充由挂钩的用户内存读取操作引用的内存。我们在 IV-C2 节中进一步描述了这些线程的功能。
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-Agent 将 QEMU-Fuzz 生成并提供的输入解释为一系列系统调用。FUZZNG 依赖代理配置(agent-config)来针对内核的特定组件。我们将内核功能的逻辑分组称为“组件”。示例包括设备驱动程序(如 console/ptmx
、rdma
和 vhost
)、接口(如 KVM
和 io_uring
)以及通用 API(如 bpf
)。配置包括两个部分:
/dev/
),这些文件在模糊测试之前由代理打开(如果有)。例如,在图 1 中的 KVM 配置中,第一行指定 /dev/kvm
文件应被打开,因为它是与 KVM 子系统交互的入口点 [55]。接下来的六行简单列出了可以与 KVM 交互的系统调用。每一行表示模糊测试工具可以调用的一个系统调用及其参数数量。可选地,我们可以为某些参数提供掩码。这些掩码不是严格必要的,但有助于限制低效的系统调用。例如,在图 1 中,我们为 read/write
系统调用指定了掩码,以限制操作的最大大小。此外,我们对 mmap
系统调用应用掩码,以避免可能破坏代理覆盖率/代码区域的映射。
收集与内核组件接口相关的系统调用 ID 很容易,可以通过阅读内核文档或检查接口支持的文件 API(例如,检查 file_operations
结构中的字段)来完成。与 Syzkaller 相比,系统调用参数几乎没有限制(除了用于减少明显无效/浪费参数的掩码)。相反,我们依赖输入空间重塑挂钩和覆盖率反馈来识别具有有效/有趣参数的输入。每个参数(包括文件描述符编号和指针地址)直接从二进制模糊测试输入中获取。
QEMU-Fuzz 向 NG-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 虚拟机的输入,需要依次执行以下操作:
/dev/kvm
文件执行 KVM_CREATE_VM ioctl 调用以创建一个 VM 的 fd;因为 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 使用不同的策略:
一旦 CFU / UFFD 线程 用输入中的字节填充了一个内存区域,它们会更新一个全局输入指针,以便主线程中运行的系统调用解释器知道跳过用户内存访问操作。
与 Syzkaller 不同,Syzkaller 的输入包含有关系统调用类型和需要填充的结构体字段的详细信息,FUZZNG 的输入并不包含这些语义信息。因此,FUZZNG 无法预先预测输入对应的系统调用序列或用户空间访问。然而,随着 FUZZNG 执行输入,它会动态获得关于输入所表示的系统调用的有价值信息,例如:
为了利用这些信息,我们对 libFuzzer 进行了修改,以支持在模糊测试期间修改输入,从而能够在执行过程中对输入进行规范化。
所有解释器操作都有具体的长度要求。然而,libFuzzer 提供的输入往往无法满足这些要求:操作可能包含太多或太少的字节。为了解决这一问题,NG-Agent 在解释模糊测试输入时动态“调整”操作的大小,以确保每个操作包含恰好所需的字节数。这样,NG-Agent 强制了输入结构化,并确保 libFuzzer 保存的输入中不包含多余字节(参见图5中的示例)。由于存储的输入中的每个字节都被实际用于操作,因此输入变异更有可能实现新的代码覆盖率。
此外,NG-Agent 将系统调用参数掩码直接应用于输入。例如,如果代理配置指定参数有一个掩码 0xF000,而模糊测试输入提供了参数 0xDEADBEEF,NG-Agent 会将 0xDEADBEEF 替换为 0x0000B000。NG-Agent 还对用于选择系统调用类型的字节进行归一化(将其限制在模糊测试可访问的系统调用数量范围内)。
在 NG-Agent 执行完输入后,它会将规范化版本返回给 QEMU-Fuzz。默认情况下,libFuzzer 不支持修改模糊测试数据的长度或内容,因此我们对其进行了微小的修改。最终存储在语料库中的输入不包含任何多余字节,并且操作大小保证匹配必要的字节数。
需要注意的是,由于 FUZZNG 存储的是“规范化”输入,使用伪随机数生成器调整操作大小不会导致非确定性问题:任何随机生成的字节都会存储在语料库中。
总之我们的 FUZZNG 实现结合了以下几部分:
我们评估了 FUZZNG 的模糊测试能力,以回答以下研究问题:
所有实验均在配备 双路 Intel Xeon E5-2600 v3 系列 CPU 和 192 至 256 GB 内存的服务器上进行。所有模糊测试实验均针对 Linux Kernel 5.12 进行。FUZZNG 使用多个 QEMU-Fuzz 虚拟机(VM) 在多核系统上对内核目标进行模糊测试,并为每个 VM 配置不同的模糊测试选项:
覆盖模式:如 § IV-C1a 中所述,KCOV 支持两种覆盖模式: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 等)。因此,FUZZNG、Syzkaller 和 Healer 仅报告与相关组件交互的系统调用的覆盖率(并存储相关输入)。我们根据以下标准选择了用于模糊测试的组件:
我们在 20 个核心上对每个组件进行了为期 168 小时(7 天)的模糊测试。我们的边缘覆盖率结果(取 3 次运行的平均值)如表 I 所示。FUZZNG 在 7 个组件上的覆盖率超过了 Syzkaller。对于剩下的 3 个组件,FUZZNG 的边缘覆盖率与 Syzkaller 相差不到 7%。平均而言,FUZZNG 达到了 Syzkaller 覆盖率的 102.5%。
Healer 并未覆盖 RDMA 代码,因为它依赖于 Syzkaller 的旧版本,而该版本不包含 RDMA 的描述文件。我们发现 Healer 的开源版本(2efbb44c7d)在我们评估的组件上获得的覆盖率低于 Syzkaller。Healer 的设计目标是高效地识别系统调用之间的关系。在我们的内核配置中,仅目标组件被插桩以获取覆盖率。因此,仅增加目标组件覆盖率的系统调用会被添加到语料库中。在这种情况下,Healer 对 Syzkaller 的改进受到限制。此外,Healer 的仓库维护者提到,开源版本与 Healer 论文中使用的私有版本相比存在许多限制,这可能是覆盖率差异的主要原因 [52], [3]。
我们比较了 Syzkaller 和 Healer 与 FUZZNG 的独特覆盖率结果。每个组件的结果如图 6 所示。我们手动检查了覆盖率,发现 Syzkaller 和 Healer 覆盖了 FUZZNG 未覆盖的一些边缘,其主要原因如下:
Syzkaller 和 Healer 支持故障注入:这使得模糊测试工具可以强制内核 API 调用失败(例如 SLAB 分配、futex 调用)。由于 FUZZNG 未实现这一功能,因此某些内核的错误处理代码未被覆盖,而 Syzkaller 和 Healer 进行了覆盖。
多线程执行测试用例: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
我们在表 1 中比较了 FUZZNG 的配置文件大小与 Syzkaller 的 syzlang 描述文件大小。这一比较反映了为每个模糊测试工具新增支持组件所需的实现成本。需要注意的是,对于 Syzkaller,我们仅包含 syzlang 描述文件的行数,并未计算任何组件特定的测试挂载代码,因为这些代码与 Syzkaller 的其他无关代码交织在一起。因此,我们低估了为 Syzkaller 添加新组件支持所需的代码量。平均而言,FUZZNG 的配置文件比 Syzkaller 的描述文件小 98.3%。
FUZZNG 的覆盖率评估主要集中于那些 Syzkaller 已有详尽描述并已连续多年使用 Syzkaller 进行模糊测试的组件。因此,我们并不期望从这些代码中发现大量新漏洞。然而,FUZZNG 在 Syzkaller 已经模糊测试的代码中发现了一些未知漏洞。此外,我们选取了 3 个未被 Syzkaller 模糊测试的驱动程序(mmcblk、megaraid、nvme),并为其创建了 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 并未包含此设备的任何描述,因此未能发现此漏洞。
为了便于测试用例之间的清理工作,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)的支持。
更多【外文翻译- 没有语法也没关系:在没有系统调用说明的情况下实现对 Linux 内核的模糊测试】相关视频教程:www.yxfzedu.com