╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ fuzzer.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1600 } else { 1601 const total_weight = n_seen_pcs; 1602 var n = f.rngLessThan(u64, total_weight); 1603 for (0.., f.tests) |i, *t| { 1604 if (t.limit == 0) continue; 1605 const seen_pc_count = @max(t.seen_pc_count, 1); 1606 if (n < seen_pc_count) return @intCast(i); 1607 n -= seen_pc_count; 1608 } 1609 unreachable; 1610 } 1611 } 1612 1613 fn weightsContain(int: u64, weights: []const abi.Weight) bool { 1614 var contains: bool = false; 1615 for (weights) |w| { 1616 contains |= w.min <= int and int <= w.max; 1617 } 1618 return contains; 1619 } 1620 1621 fn weightsContainBytes(bytes: []const u8, weights: []const abi.Weight) bool { 1622 if (weights[0].min == 0 and weights[0].max == 0xff) { 1623 // Fast path: all bytes are valid 1624 return true; 1625 } 1626 1627 var contains: bool = true; 1628 for (bytes) |b| { 1629 contains &= weightsContain(b, weights); 1630 } 1631 return contains; 1632 } 1633 1634 fn sumWeightsInclusive(weights: []const abi.Weight) u64 { 1635 var sum: u64 = math.maxInt(u64); 1636 for (weights) |w| { 1637 sum +%= (w.max - w.min +% 1) *% w.weight; 1638 } 1639 return sum; 1640 } 1641 1642 fn weightedValue(f: *Fuzzer, weights: []const abi.Weight, incl_sum: u64) u64 { 1643 var incl_n: u64 = f.rngInt(u64); 1644 const limit = incl_sum +% 1; 1645 if (limit != 0) incl_n = std.Random.limitRangeBiased(u64, incl_n, limit); 1646 1647 for (weights) |w| { 1648 // (w.max - w.min + 1) * w.weight - 1 1649 const incl_vals = (w.max - w.min) * w.weight + (w.weight - 1); 1650 if (incl_n > incl_vals) { 1651 incl_n -= incl_vals + 1; 1652 } else { 1653 const val = w.min + incl_n / w.weight; 1654 assert(val <= w.max); 1655 return val; 1656 } 1657 } else unreachable; 1658 } 1659 1660 const Untyped = union { 1661 int: u64, 1662 bytes: []u8, 1663 }; 1664 1665 fn nextUntyped(f: *Fuzzer, uid: Uid, weights: []const abi.Weight) union(enum) { 1666 copy: Untyped, 1667 mutate: Untyped, 1668 fresh: void, 1669 } { 1670 const t = &f.tests[f.test_i]; 1671 const corpus = t.corpus.slice(); 1672 const corpus_i = @backingInt(t.corpus_pos); 1673 const data = &corpus.items(.data)[corpus_i]; 1674 var small_entronopy: SmallEntronopy = .{ .bits = f.rngInt(u64) }; 1675 1676 const uid_i = data.uid_slices.getIndex(uid) orelse { 1677 @branchHint(.unlikely); 1678 return .fresh; 1679 }; 1680 const data_slice = data.uid_slices.values()[uid_i]; 1681 var slice_i = f.uid_data_i.items[uid_i]; 1682 var data_i = data_slice.base + slice_i; 1683 1684 new_data: while (true) { 1685 assert(slice_i == f.uid_data_i.items[uid_i] and data_i == data_slice.base + slice_i); 1686 if (slice_i == data_slice.len) break :new_data; 1687 assert(slice_i < data_slice.len); 1688 1689 f.uid_data_i.items[uid_i] += 1; 1690 const mut_i = std.simd.firstIndexOfValue( 1691 @as(@Vector(4, u32), f.mut_data.i), 1692 data_i + @as(u32, @intCast(data.ints.len)) * @backingInt(uid.kind), 1693 ) orelse { 1694 @branchHint(.likely); 1695 switch (uid.kind) { 1696 .int => { 1697 const int = data.ints[data_i]; 1698 if (weightsContain(int, weights)) { 1699 @branchHint(.likely); 1700 return .{ .copy = .{ .int = int } }; 1701 } 1702 }, 1703 .bytes => { 1704 const entry = data.bytes.entries[data_i]; 1705 const bytes = data.bytes.table[entry.off..][0..entry.len]; 1706 if (weightsContainBytes(bytes, weights)) { 1707 @branchHint(.likely); 1708 return .{ .copy = .{ .bytes = bytes } }; 1709 } 1710 }, 1711 } 1712 break :new_data; 1713 }; 1714 1715 const seq = &f.mut_data.seq[mut_i]; 1716 new_seq: { 1717 if (!seq.kind.none) break :new_seq; 1718 1719 var opts: packed struct(u6) { 1720 // Matches layout as `mut_data.seq.kind` 1721 insert: bool, 1722 copy: bool, 1723 1724 seq: u2, 1725 delete: bool, 1726 splice: bool, 1727 } = @bitCast(small_entronopy.take(u6)); 1728 if (opts.seq != 0) break :new_data; 1729 1730 const max_consume = data_slice.len - slice_i; // inclusive 1731 if (opts.delete) { 1732 f.uid_data_i.items[uid_i] += f.rngLessThan(u32, max_consume); 1733 slice_i = f.uid_data_i.items[uid_i]; 1734 data_i = data_slice.base + slice_i; 1735 continue; 1736 } 1737 opts.insert |= max_consume == 0; 1738 seq.kind = .{ 1739 .class = if (opts.insert) .replace else .insert, 1740 .copy = opts.copy, 1741 .ordered_mutate = false, 1742 .none = false, 1743 }; 1744 1745 if (!seq.kind.copy) { 1746 seq.len = switch (seq.kind.class) { 1747 .replace => f.rngLessThan(u32, max_consume) + 1, 1748 .insert => @clz(f.rngInt(u16)) + 1, 1749 }; 1750 seq.copy = undefined; 1751 } else { 1752 const src: SeqCopy, const src_len: u32 = if (!opts.splice) .{ 1753 switch (uid.kind) { 1754 .int => .{ .ints = data.ints[data_slice.base..][0..data_slice.len] }, 1755 .bytes => .{ .bytes = .{ 1756 .entries = data.bytes.entries[data_slice.base..][0..data_slice.len], 1757 .table = data.bytes.table, 1758 } }, 1759 }, 1760 data_slice.len, 1761 } else src: { 1762 const seen_uid_i = corpus.items(.seen_uid_i)[corpus_i][uid_i]; 1763 const untyped_slices = t.seen_uids.values()[seen_uid_i].slices; 1764 switch (uid.kind) { 1765 .int => { 1766 const slices = untyped_slices.ints.items; 1767 const i = f.rngLessThan(u32, @intCast(slices.len)); 1768 break :src .{ 1769 .{ .ints = slices[i] }, 1770 @intCast(slices[i].len), 1771 }; 1772 }, 1773 .bytes => { 1774 const slices = untyped_slices.bytes.items; 1775 const i = f.rngLessThan(u32, @intCast(slices.len)); 1776 break :src .{ 1777 .{ .bytes = slices[i] }, 1778 @intCast(slices[i].entries.len), 1779 }; 1780 }, 1781 } 1782 }; 1783 1784 const off = f.rngLessThan(u32, src_len); 1785 seq.len = f.rngLessThan(u32, src_len - off) + 1; 1786 if (seq.kind.class == .replace) seq.len = @min(seq.len, max_consume); 1787 seq.copy = switch (uid.kind) { 1788 .int => .{ .ints = src.ints[off..][0..seq.len] }, 1789 .bytes => .{ .bytes = .{ 1790 .entries = src.bytes.entries[off..][0..seq.len], 1791 .table = src.bytes.table, 1792 } }, 1793 }; 1794 } 1795 } 1796 1797 assert(!seq.kind.none); 1798 f.uid_data_i.items[uid_i] -= @intFromBool(seq.kind.class == .insert); 1799 seq.len -= 1; 1800 seq.kind.none |= seq.len == 0; 1801 f.mut_data.i[mut_i] += @intFromBool(seq.kind.class == .replace and seq.len != 0); 1802 1803 if (!seq.kind.copy) { 1804 assert(!seq.kind.ordered_mutate); 1805 break :new_data; 1806 } 1807 if (seq.kind.ordered_mutate) { 1808 assert(seq.kind.class == .replace); 1809 seq.copy.order_i += @intFromBool(seq.len != 0); 1810 f.mut_data.i[mut_i] = data.order[seq.copy.order_i]; 1811 break :new_data; 1812 } 1813 switch (uid.kind) { 1814 .int => { 1815 const int = seq.copy.ints[0]; 1816 seq.copy.ints = seq.copy.ints[1..]; 1817 if (weightsContain(int, weights)) { 1818 @branchHint(.likely); 1819 return .{ .copy = .{ .int = int } }; 1820 } 1821 }, 1822 .bytes => { 1823 const entry = seq.copy.bytes.entries[0]; 1824 const bytes = seq.copy.bytes.table[entry.off..][0..entry.len]; 1825 seq.copy.bytes.entries = seq.copy.bytes.entries[1..]; 1826 if (weightsContainBytes(bytes, weights)) { 1827 @branchHint(.likely); 1828 return .{ .copy = .{ .bytes = bytes } }; 1829 } 1830 }, 1831 } 1832 break; 1833 } 1834 1835 const opts: packed struct(u10) { 1836 copy: u2, 1837 fresh: u2, 1838 splice: bool, 1839 local_far: bool, 1840 local_off: i4, 1841 } = @bitCast(small_entronopy.take(u10)); 1842 1843 if (opts.copy != 0) { 1844 if (opts.fresh == 0 or slice_i == data_slice.len) return .fresh; 1845 switch (uid.kind) { 1846 .int => { 1847 const int = data.ints[data_i]; 1848 if (weightsContain(int, weights)) { 1849 @branchHint(.likely); 1850 return .{ .mutate = .{ .int = int } }; 1851 } 1852 }, 1853 .bytes => { 1854 const entry = data.bytes.entries[data_i]; 1855 const bytes = data.bytes.table[entry.off..][0..entry.len]; 1856 if (weightsContainBytes(bytes, weights)) { 1857 @branchHint(.likely); 1858 return .{ .mutate = .{ .bytes = bytes } }; 1859 } 1860 }, 1861 } 1862 } 1863 1864 if (!opts.splice) { 1865 const src_data_i = data_slice.base + if (!opts.local_far) i: { 1866 const off = opts.local_off; 1867 break :i if (off >= 0) @min( 1868 f.uid_data_i.items[uid_i] +| @as(u4, @intCast(off)), 1869 data_slice.len - 1, 1870 ) else f.uid_data_i.items[uid_i] -| @abs(off); 1871 } else f.rngLessThan(u32, data_slice.len); 1872 switch (uid.kind) { 1873 .int => { 1874 const int = data.ints[src_data_i]; 1875 if (weightsContain(int, weights)) { 1876 @branchHint(.likely); 1877 return .{ .copy = .{ .int = int } }; 1878 } 1879 }, 1880 .bytes => { 1881 const entry = data.bytes.entries[src_data_i]; 1882 const bytes = data.bytes.table[entry.off..][0..entry.len]; 1883 if (weightsContainBytes(bytes, weights)) { 1884 @branchHint(.likely); 1885 return .{ .copy = .{ .bytes = bytes } }; 1886 } 1887 }, 1888 } 1889 } else { 1890 const seen_uid_i = corpus.items(.seen_uid_i)[corpus_i][uid_i]; 1891 const untyped_slices = t.seen_uids.values()[seen_uid_i].slices; 1892 switch (uid.kind) { 1893 .int => { 1894 const slices = untyped_slices.ints.items; 1895 const from = slices[f.rngLessThan(u32, @intCast(slices.len))]; 1896 const int = from[f.rngLessThan(u32, @intCast(from.len))]; 1897 if (weightsContain(int, weights)) { 1898 @branchHint(.likely); 1899 return .{ .copy = .{ .int = int } }; 1900 } 1901 }, 1902 .bytes => { 1903 const slices = untyped_slices.bytes.items; 1904 const from = slices[f.rngLessThan(u32, @intCast(slices.len))]; 1905 const entry_i = f.rngLessThan(u32, @intCast(from.entries.len)); 1906 const entry = from.entries[entry_i]; 1907 const bytes = from.table[entry.off..][0..entry.len]; 1908 if (weightsContainBytes(bytes, weights)) { 1909 @branchHint(.likely); 1910 return .{ .copy = .{ .bytes = bytes } }; 1911 } 1912 }, 1913 } 1914 } 1915 return .fresh; 1916 } 1917 1918 pub fn nextInt(f: *Fuzzer, uid: Uid, weights: []const abi.Weight) u64 { 1919 const t = &f.tests[f.test_i]; 1920 f.req_values += 1; 1921 if (@backingInt(t.corpus_pos) >= @backingInt(Input.Index.reserved_start)) { 1922 @branchHint(.unlikely); 1923 const int = f.bytes_input.valueWeightedWithHash(u64, weights, undefined); 1924 if (t.corpus_pos == .bytes_fresh) { 1925 f.input_builder.checkSmithedLen(8); 1926 f.input_builder.addInt(uid, int); 1927 } 1928 return int; 1929 } 1930 const int = f.nextIntInner(uid, weights); 1931 f.mmap_input.appendLittleInt(u64, int); 1932 return int; 1933 } 1934 1935 fn nextIntInner(f: *Fuzzer, uid: Uid, weights: []const abi.Weight) u64 { 1936 return switch (f.nextUntyped(uid, weights)) { 1937 .copy => |u| u.int, 1938 .mutate, .fresh => f.weightedValue(weights, sumWeightsInclusive(weights)), 1939 }; 1940 } 1941 1942 pub fn nextEos(f: *Fuzzer, uid: Uid, weights: []const abi.Weight) bool { 1943 const t = &f.tests[f.test_i]; 1944 f.req_values += 1; 1945 if (@backingInt(t.corpus_pos) >= @backingInt(Input.Index.reserved_start)) { 1946 @branchHint(.unlikely); 1947 const eos = f.bytes_input.eosWeightedWithHash(weights, undefined); 1948 if (t.corpus_pos == .bytes_fresh) { 1949 f.input_builder.checkSmithedLen(1); 1950 f.input_builder.addInt(uid, @intFromBool(eos)); 1951 } 1952 return eos; 1953 } 1954 // `nextIntInner` is already gauraunteed to eventually return `1` 1955 const eos = @as(u1, @intCast(f.nextIntInner(uid, weights))) != 0; 1956 f.mmap_input.appendLittleInt(u8, @intFromBool(eos)); 1957 return eos; 1958 } 1959 1960 fn mutateBytes(f: *Fuzzer, in: []u8, out: []u8, weights: []const abi.Weight) void { 1961 assert(in.len != 0); 1962 const weights_incl_sum = sumWeightsInclusive(weights); 1963 1964 var small_entronopy: SmallEntronopy = .{ .bits = f.rngInt(u64) }; 1965 var muts = mutCount(small_entronopy.take(u16)); 1966 var rem_out = out; 1967 var rem_copy = in; 1968 while (rem_out.len != 0 and muts != 0) { 1969 muts -= 1; 1970 const opts: packed struct(u4) { 1971 kind: enum(u2) { 1972 random, 1973 stream_copy, 1974 stream_discard, 1975 absolute_copy, 1976 }, 1977 small: u2, 1978 1979 pub fn limitSmall(o: @This(), n: usize) u32 { 1980 return @min( 1981 @as(u32, @intCast(n)), 1982 @as(u32, if (o.small != 0) 8 else math.maxInt(u32)), 1983 ); 1984 } 1985 } = @bitCast(small_entronopy.take(u4)); 1986 s: switch (opts.kind) { 1987 .random => { 1988 const n = f.rngLessThan(u32, opts.limitSmall(rem_out.len)) + 1; 1989 for (rem_out[0..n]) |*o| { 1990 o.* = @intCast(f.weightedValue(weights, weights_incl_sum)); 1991 } 1992 rem_out = rem_out[n..]; 1993 }, 1994 .stream_copy => { 1995 if (rem_copy.len == 0) continue :s .random; 1996 const n = @min( 1997 f.rngLessThan(u32, opts.limitSmall(rem_copy.len)) + 1, 1998 rem_out.len, 1999 ); 2000 @memcpy(rem_out[0..n], rem_copy[0..n]); 2001 rem_out = rem_out[n..]; 2002 rem_copy = rem_copy[n..]; 2003 }, 2004 .stream_discard => { 2005 if (rem_copy.len == 0) continue :s .random; 2006 const n = f.rngLessThan(u32, opts.limitSmall(rem_copy.len)) + 1; 2007 rem_copy = rem_copy[n..]; 2008 }, 2009 .absolute_copy => { 2010 const in_len: u32 = @intCast(in.len); 2011 const off = f.rngLessThan(u32, in_len); 2012 const len = @min( 2013 f.rngLessThan(u32, in_len - off) + 1, 2014 opts.limitSmall(rem_out.len), 2015 ); 2016 @memcpy(rem_out[0..len], in[off..][0..len]); 2017 rem_out = rem_out[len..]; 2018 }, 2019 } 2020 } 2021 2022 const copy = @min(rem_out.len, rem_copy.len); 2023 @memcpy(rem_out[0..copy], rem_copy[0..copy]); 2024 for (rem_out[copy..]) |*o| { 2025 o.* = @intCast(f.weightedValue(weights, weights_incl_sum)); 2026 } 2027 } 2028 2029 fn nextBytesInner(f: *Fuzzer, uid: Uid, out: []u8, weights: []const abi.Weight) void { 2030 so: switch (f.nextUntyped(uid, weights)) { 2031 .copy => |u| { 2032 if (u.bytes.len >= out.len) { 2033 @branchHint(.likely); 2034 @memcpy(out, u.bytes[0..out.len]); 2035 return; 2036 } 2037 2038 @memcpy(out[0..u.bytes.len], u.bytes); 2039 const weights_incl_sum = sumWeightsInclusive(weights); 2040 for (out[u.bytes.len..]) |*o| { 2041 o.* = @intCast(f.weightedValue(weights, weights_incl_sum)); 2042 } 2043 }, 2044 .mutate => |u| { 2045 if (u.bytes.len == 0) continue :so .fresh; 2046 f.mutateBytes(u.bytes, out, weights); 2047 }, 2048 .fresh => { 2049 const weights_incl_sum = sumWeightsInclusive(weights); 2050 for (out) |*o| { 2051 o.* = @intCast(f.weightedValue(weights, weights_incl_sum)); 2052 } 2053 }, 2054 } 2055 } 2056 2057 pub fn nextBytes(f: *Fuzzer, uid: Uid, out: []u8, weights: []const abi.Weight) void { 2058 const t = &f.tests[f.test_i]; 2059 f.req_values += 1; 2060 f.req_bytes +%= @truncate(out.len); // This function should panic since the 32-bit 2061 // data limit is exceeded, so wrapping is fine. 2062 if (@backingInt(t.corpus_pos) >= @backingInt(Input.Index.reserved_start)) { 2063 @branchHint(.unlikely); 2064 f.bytes_input.bytesWeightedWithHash(out, weights, undefined); 2065 if (t.corpus_pos == .bytes_fresh) { 2066 f.input_builder.checkSmithedLen(out.len); 2067 f.input_builder.addBytes(uid, out); 2068 } 2069 return; 2070 } 2071 2072 f.nextBytesInner(uid, out, weights); 2073 f.mmap_input.appendSlice(out); 2074 } 2075 2076 fn nextSliceInner( 2077 f: *Fuzzer, 2078 uid: Uid, 2079 buf: []u8, 2080 len_weights: []const abi.Weight, 2081 byte_weights: []const abi.Weight, 2082 ) u32 { 2083 so: switch (f.nextUntyped(uid, byte_weights)) { 2084 .copy => |u| { 2085 var len: u32 = @intCast(u.bytes.len); 2086 if (!weightsContain(len, len_weights)) { 2087 @branchHint(.unlikely); 2088 len = @intCast(f.weightedValue(len_weights, sumWeightsInclusive(len_weights))); 2089 } 2090 2091 if (u.bytes.len >= len) { 2092 @branchHint(.likely); 2093 @memcpy(buf[0..len], u.bytes[0..len]); 2094 return len; 2095 } 2096 2097 @memcpy(buf[0..u.bytes.len], u.bytes); 2098 const weights_incl_sum = sumWeightsInclusive(byte_weights); 2099 for (buf[u.bytes.len..len]) |*o| { 2100 o.* = @intCast(f.weightedValue(byte_weights, weights_incl_sum)); 2101 } 2102 return len; 2103 }, 2104 .mutate => |u| { 2105 if (u.bytes.len == 0) continue :so .fresh; 2106 const len: u32 = len: { 2107 const offseted: packed struct { 2108 is: u3, 2109 sub: bool, 2110 by: u3, 2111 } = @bitCast(f.rngInt(u7)); 2112 if (offseted.is != 0) { 2113 const len = if (offseted.sub) 2114 @as(u32, @intCast(u.bytes.len)) -| offseted.by 2115 else 2116 @min(u.bytes.len + offseted.by, @as(u32, @intCast(buf.len))); 2117 if (weightsContain(len, len_weights)) { 2118 break :len len; 2119 } 2120 } 2121 break :len @intCast(f.weightedValue( 2122 len_weights, 2123 sumWeightsInclusive(len_weights), 2124 )); 2125 }; 2126 f.mutateBytes(u.bytes, buf[0..len], byte_weights); 2127 return len; 2128 }, 2129 .fresh => { 2130 const len: u32 = @intCast(f.weightedValue( 2131 len_weights, 2132 sumWeightsInclusive(len_weights), 2133 )); 2134 const weights_incl_sum = sumWeightsInclusive(byte_weights); 2135 for (buf[0..len]) |*o| { 2136 o.* = @intCast(f.weightedValue(byte_weights, weights_incl_sum)); 2137 } 2138 return len; 2139 }, 2140 } 2141 } 2142 2143 pub fn nextSlice( 2144 f: *Fuzzer, 2145 uid: Uid, 2146 buf: []u8, 2147 len_weights: []const abi.Weight, 2148 byte_weights: []const abi.Weight, 2149 ) u32 { 2150 const t = &f.tests[f.test_i]; 2151 f.req_values += 1; 2152 if (@backingInt(t.corpus_pos) >= @backingInt(Input.Index.reserved_start)) { 2153 @branchHint(.unlikely); 2154 const n = f.bytes_input.sliceWeightedWithHash( 2155 buf, 2156 len_weights, 2157 byte_weights, 2158 undefined, 2159 ); 2160 if (t.corpus_pos == .bytes_fresh) { 2161 f.input_builder.checkSmithedLen(@as(usize, 4) + n); 2162 f.input_builder.addBytes(uid, buf[0..n]); 2163 } 2164 return n; 2165 } 2166 2167 const n = f.nextSliceInner(uid, buf, len_weights, byte_weights); 2168 f.mmap_input.appendLittleInt(u32, n); 2169 f.mmap_input.appendSlice(buf[0..n]); 2170 f.req_bytes += n; 2171 return n; 2172 } 2173 }; 2174 2175 export fn fuzzer_init(cache_dir_path: abi.Slice) void { 2176 exec = .init(cache_dir_path.toSlice()); 2177 } 2178 2179 export fn fuzzer_coverage() abi.Coverage { 2180 const coverage_id = exec.pc_digest; 2181 const header = @volatileCast(exec.seenPcsHeader()); 2182 2183 var seen_count: usize = 0; 2184 for (header.seenBits()) |chunk| { 2185 seen_count += @popCount(chunk); 2186 } 2187 2188 return .{ 2189 .id = coverage_id, 2190 .runs = header.n_runs, 2191 .unique = header.unique_runs, 2192 .seen = seen_count, 2193 }; 2194 } 2195 2196 export fn fuzzer_main( 2197 n_tests: u32, 2198 seed: u32, 2199 limit_kind: abi.LimitKind, 2200 amount_or_instance: u64, 2201 ) void { 2202 fuzzer = .init( 2203 n_tests, 2204 seed ^ amount_or_instance, // seed is otherwise the same for all instances 2205 if (limit_kind == .forever) @as(u32, @intCast(amount_or_instance)) else 0, 2206 if (limit_kind == .forever) null else amount_or_instance, 2207 ); 2208 defer fuzzer.deinit(); 2209 abi.runner_start_input_poller(); 2210 defer abi.runner_stop_input_poller(); 2211 2212 if (n_tests == 1) { 2213 // no swapping between fuzz tests 2214 runTest(0); 2215 } else { 2216 while (fuzzer.select()) |i| { 2217 runTest(i); 2218 } 2219 } 2220 } 2221 2222 export fn fuzzer_receive_input(test_i: u32, bytes_slice: abi.Slice) bool { 2223 const recv = &fuzzer.tests[test_i].received; 2224 if (recv.state.startWrite()) return true; 2225 defer recv.state.finishWrite(); 2226 2227 const bytes = bytes_slice.toSlice(); 2228 const len: u32 = @intCast(bytes.len); 2229 recv.inputs.ensureUnusedCapacity(gpa, 4 + bytes.len) catch @panic("OOM"); 2230 recv.inputs.appendSliceAssumeCapacity(@ptrCast(&len)); 2231 recv.inputs.appendSliceAssumeCapacity(bytes); 2232 2233 return false; 2234 } 2235 2236 fn runTest(i: u32) void { 2237 fuzzer.test_i = i; 2238 fuzzer.mmap_input.setTest(i); 2239 current_test_name = abi.runner_test_name(i).toSlice(); 2240 abi.runner_test_run(i); 2241 } 2242 2243 export fn fuzzer_set_test(test_one: abi.TestOne) void { 2244 fuzzer.test_one = test_one; 2245 } 2246 2247 export fn fuzzer_new_input(bytes: abi.Slice) void { 2248 if (bytes.len == 0) return; // An entry of length zero is always present 2249 if (fuzzer.tests[fuzzer.test_i].start_mut_corpus != math.maxInt(u32)) return; // Test ran previously 2250 fuzzer.newInputExternal(bytes.toSlice()); 2251 } 2252 2253 export fn fuzzer_start_test() void { 2254 fuzzer.ensureCorpusLoaded(); 2255 fuzzer.batch(); 2256 } 2257 2258 export fn fuzzer_int(uid: Uid, weights: abi.Weights) u64 { 2259 assert(uid.kind == .int); 2260 return fuzzer.nextInt(uid, weights.toSlice()); 2261 } 2262 2263 export fn fuzzer_eos(uid: Uid, weights: abi.Weights) bool { 2264 assert(uid.kind == .int); 2265 return fuzzer.nextEos(uid, weights.toSlice()); 2266 } 2267 2268 export fn fuzzer_bytes(uid: Uid, out: abi.MutSlice, weights: abi.Weights) void { 2269 assert(uid.kind == .bytes); 2270 return fuzzer.nextBytes(uid, out.toSlice(), weights.toSlice()); 2271 } 2272 2273 export fn fuzzer_slice( 2274 uid: Uid, 2275 buf: abi.MutSlice, 2276 len_weights: abi.Weights, 2277 byte_weights: abi.Weights, 2278 ) u32 { 2279 assert(uid.kind == .bytes); 2280 return fuzzer.nextSlice(uid, buf.toSlice(), len_weights.toSlice(), byte_weights.toSlice()); 2281 } 2282 2283 export fn fuzzer_unslide_address(addr: usize) usize { 2284 const si = std.debug.getSelfDebugInfo() catch @compileError("unsupported"); 2285 const slide = si.getModuleSlide(io, addr) catch |err| { 2286 // The LLVM backend seems to insert placeholder values of `1` in __sancov_pcs1 2287 if (addr == 1) return 1; 2288 panic("failed to find virtual address slide for address 0x{x}: {t}", .{ addr, err }); 2289 }; 2290 return addr - slide; 2291 } 2292 2293 /// Helps determine run uniqueness in the face of recursion. 2294 /// Currently not used by the fuzzer. 2295 export threadlocal var __sancov_lowest_stack: usize = 0; 2296 2297 export fn __sanitizer_cov_trace_pc_indir(callee: usize) void { 2298 // Not valuable because we already have pc tracing via 8bit counters. 2299 _ = callee; 2300 } 2301 export fn __sanitizer_cov_8bit_counters_init(start: usize, end: usize) void { 2302 // clang will emit a call to this function when compiling with code coverage instrumentation. 2303 // however, fuzzer_init() does not need this information since it directly reads from the 2304 // symbol table. 2305 _ = start; 2306 _ = end; 2307 } 2308 export fn __sanitizer_cov_pcs_init(start: usize, end: usize) void { 2309 // clang will emit a call to this function when compiling with code coverage instrumentation. 2310 // however, fuzzer_init() does not need this information since it directly reads from the 2311 // symbol table. 2312 _ = start; 2313 _ = end; 2314 } 2315 2316 /// Reusable and recoverable input. 2317 /// 2318 /// Has a 32-bit limit on the input length. This has the nice side effect that `u32` 2319 /// can be used in most placed in `fuzzer` with the last `@sizeOf(abi.MmapInputHeader)` 2320 /// values reserved. 2321 const MemoryMappedInput = struct { 2322 const Header = abi.MmapInputHeader; 2323 2324 len: u32, 2325 /// Directly accessing `memory` is unsafe, use either `inputSlice` or `writeSlice`. 2326 mmap: Io.File.MemoryMap, 2327 in_i: u32, 2328 2329 /// `file` becomes owned by the returned `MemoryMappedInput` 2330 pub fn init(file: Io.File, instance_id: u32, in_i: u32) MemoryMappedInput { 2331 var size = file.length(io) catch |e| 2332 panic("failed to get length of 'in{x}': {t}", .{ in_i, e }); 2333 if (size < std.heap.page_size_max) { 2334 size = std.heap.page_size_max; 2335 file.setLength(io, size) catch |e| 2336 panic("failed to resize 'in{x}': {t}", .{ in_i, e }); 2337 } 2338 const map = file.createMemoryMap(io, .{ .len = size }) catch |e| 2339 panic("failed to memmap input file 'in{x}': {t}", .{ in_i, e }); 2340 @as(*volatile Header, @ptrCast(map.memory)).* = .{ 2341 .pc_digest = mem.nativeToLittle(u64, exec.pc_digest), 2342 .instance_id = mem.nativeToLittle(u32, instance_id), 2343 .test_i = 0, 2344 .len = 0, 2345 }; 2346 return .{ 2347 .len = 0, 2348 .mmap = map, 2349 .in_i = in_i, 2350 }; 2351 } 2352 2353 pub fn deinit(l: *MemoryMappedInput) void { 2354 const f = l.mmap.file; 2355 l.mmap.write(io) catch |e| panic("failed to write memory map of 'in{x}': {t}", .{ l.in_i, e }); 2356 l.mmap.destroy(io); 2357 f.close(io); 2358 l.* = undefined; 2359 } 2360 2361 /// Modify the array so that it can hold at least `additional_count` **more** items. 2362 /// 2363 /// Invalidates element pointers if additional memory is needed. 2364 pub fn ensureUnusedCapacity(l: *MemoryMappedInput, additional_count: usize) void { 2365 return l.ensureSize(@sizeOf(Header) + l.len + additional_count); 2366 } 2367 2368 fn ensureSize(l: *MemoryMappedInput, min_capacity: usize) void { 2369 if (l.mmap.memory.len < min_capacity) { 2370 @branchHint(.unlikely); 2371 2372 const max_capacity = 1 << 32; // The size of the header is not added 2373 // in order to keep the capacity page aligned and to allow those values to 2374 // reserved for other places. 2375 if (min_capacity > max_capacity) @panic("too much smith data requested"); 2376 2377 const new_capacity = @min(growCapacity(min_capacity), max_capacity); 2378 l.mmap.file.setLength(io, new_capacity) catch |e| 2379 panic("failed to resize 'in{x}': {t}", .{ l.in_i, e }); 2380 l.mmap.setLength(io, new_capacity) catch |se| switch (se) { 2381 error.OperationUnsupported => { 2382 const f = l.mmap.file; 2383 l.mmap.destroy(io); 2384 l.mmap = f.createMemoryMap(io, .{ .len = new_capacity }) catch |e| 2385 panic("failed to memory map 'in{x}': {t}", .{ l.in_i, e }); 2386 }, 2387 else => panic("failed to resize memory map of 'in{x}': {t}", .{ l.in_i, se }), 2388 }; 2389 } 2390 } 2391 2392 // Only writing has side effects, so volatile is not needed 2393 pub fn inputSlice(l: *MemoryMappedInput) []const u8 { 2394 return l.mmap.memory[@sizeOf(Header)..][0..l.len]; 2395 } 2396 2397 // Writing has side effectsd, so volatile is necessary 2398 pub fn writeSlice(l: *MemoryMappedInput) []volatile u8 { 2399 return l.mmap.memory; 2400 } 2401 2402 fn writeLen(l: *MemoryMappedInput) void { 2403 l.writeSlice()[@offsetOf(Header, "len")..][0..4].* = 2404 @bitCast(mem.nativeToLittle(u32, l.len)); 2405 } 2406 2407 pub fn setTest(l: *MemoryMappedInput, i: u32) void { 2408 l.writeSlice()[@offsetOf(Header, "test_i")..][0..4].* = 2409 @bitCast(mem.nativeToLittle(u32, i)); 2410 } 2411 2412 /// Invalidates all element pointers. 2413 pub fn clearRetainingCapacity(l: *MemoryMappedInput) void { 2414 l.len = 0; 2415 l.writeLen(); 2416 } 2417 2418 /// Append the slice of items to the list. 2419 /// 2420 /// Invalidates item pointers if more space is required. 2421 pub fn appendSlice(l: *MemoryMappedInput, items: []const u8) void { 2422 l.ensureUnusedCapacity(items.len); 2423 @memcpy(l.writeSlice()[@sizeOf(Header) + l.len ..][0..items.len], items); 2424 l.len += @as(u32, @intCast(items.len)); 2425 l.writeLen(); 2426 } 2427 2428 /// Append the little-endian integer to the list. 2429 /// 2430 /// Invalidates item pointers if more space is required. 2431 pub fn appendLittleInt(l: *MemoryMappedInput, T: type, x: T) void { 2432 l.ensureUnusedCapacity(@sizeOf(T)); 2433 l.writeSlice()[@sizeOf(Header) + l.len ..][0..@sizeOf(T)].* = 2434 @bitCast(mem.nativeToLittle(T, x)); 2435 l.len += @sizeOf(T); 2436 l.writeLen(); 2437 } 2438 2439 /// Called when memory growth is necessary. Returns a capacity larger than 2440 /// minimum that grows super-linearly. 2441 fn growCapacity(minimum: usize) usize { 2442 return mem.alignForward( 2443 usize, 2444 minimum +| (minimum / 2 + std.heap.page_size_max), 2445 std.heap.page_size_max, 2446 ); 2447 } 2448 }; 2449