Skip to content
zit Artifact [9e0d8dbb8c]
Login

Artifact [9e0d8dbb8c]

Artifact 9e0d8dbb8c82ca0ba1a80974c076e74e4f37072b21a10812efd5539875904719:


//! Seeded, parameterized fuzz targets for the delta format
//! (draft-fossil-delta-format-00). Each target mirrors one of the fixed-seed
//! blocks in delta.zig's own test suite, but takes its seed and round count
//! from the caller (`zig build fuzz -- <target> [--seed N] [--rounds N]`)
//! instead of a hardcoded constant, so an out-of-band runner can sweep many
//! seeds at much higher round counts. Registered in fuzz_tests.zig.

const std = @import("std");
const delta = @import("zit").delta;

/// `apply` on arbitrary source+delta bytes never crashes: only a declared
/// `Error`/`Allocator.Error` or a freed value comes back (delta §4
/// robustness; mirrors delta.zig's "apply on arbitrary source+delta never
/// crashes" block).
pub fn applyGarbage(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void {
    var prng = std.Random.DefaultPrng.init(seed);
    const rand = prng.random();
    var round: usize = 0;
    while (round < rounds) : (round += 1) {
        var sbuf: [128]u8 = undefined;
        var dbuf: [160]u8 = undefined;
        const slen = rand.uintLessThan(usize, sbuf.len + 1);
        const dlen = rand.uintLessThan(usize, dbuf.len + 1);
        for (sbuf[0..slen]) |*b| b.* = rand.int(u8);
        // Bias delta bytes toward DIGIT64/opcodes so inputs reach deep into
        // the decoder rather than being rejected at byte 0.
        for (dbuf[0..dlen]) |*b| b.* = if (rand.boolean())
            rand.int(u8)
        else
            delta.digit64[rand.uintLessThan(usize, delta.digit64.len)];
        if (delta.apply(gpa, sbuf[0..slen], dbuf[0..dlen])) |out| gpa.free(out) else |_| {}
    }
}

/// `apply(source, create(source, target)) == target` for arbitrary inputs
/// (delta §5 round-trip; mirrors delta.zig's round-trip block).
pub fn roundTrip(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void {
    var prng = std.Random.DefaultPrng.init(seed);
    const rand = prng.random();
    var round: usize = 0;
    while (round < rounds) : (round += 1) {
        var sbuf: [256]u8 = undefined;
        const slen = rand.uintLessThan(usize, sbuf.len + 1);
        for (sbuf[0..slen]) |*b| b.* = rand.int(u8);
        const source = sbuf[0..slen];

        // Build target from a mix of source runs (exercises copies) and
        // fresh random bytes (exercises literals).
        var tbuf: [384]u8 = undefined;
        const tlen = rand.uintLessThan(usize, tbuf.len + 1);
        var t: usize = 0;
        while (t < tlen) {
            if (slen >= 8 and rand.boolean()) {
                const start = rand.uintLessThan(usize, slen);
                const run = 1 + rand.uintLessThan(usize, @min(slen - start, tlen - t));
                @memcpy(tbuf[t..][0..run], source[start..][0..run]);
                t += run;
            } else {
                tbuf[t] = rand.int(u8);
                t += 1;
            }
        }
        const target = tbuf[0..tlen];
        const d = try delta.create(gpa, source, target);
        defer gpa.free(d);
        const out = try delta.apply(gpa, source, d);
        defer gpa.free(out);
        try std.testing.expectEqualSlices(u8, target, out);
    }
}

/// `decodeInt` on arbitrary digit strings stays bounded: consumes no more
/// than the input, and the only error it ever returns is `MalformedDelta`
/// (delta §3.1; mirrors delta.zig's "decodeInt on arbitrary digit strings
/// stays bounded" block).
pub fn decodeIntBounded(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void {
    _ = gpa;
    var prng = std.Random.DefaultPrng.init(seed);
    const rand = prng.random();
    var round: usize = 0;
    while (round < rounds) : (round += 1) {
        var buf: [40]u8 = undefined;
        const len = rand.uintLessThan(usize, buf.len + 1);
        // Bias toward DIGIT64 so long all-digit runs (overflow candidates) occur.
        for (buf[0..len]) |*b| b.* = if (rand.uintLessThan(u8, 4) == 0)
            rand.int(u8)
        else
            delta.digit64[rand.uintLessThan(usize, delta.digit64.len)];
        if (delta.decodeInt(buf[0..len])) |dec| {
            try std.testing.expect(dec.len <= len); // consumed no more than the input
        } else |err| {
            try std.testing.expect(err == error.MalformedDelta); // rejection is the only error
        }
    }
}

/// `create` rejects rather than panics/wraps on synthetic source/target sizes
/// `>= 2^32` (delta §3.1: every integer the format carries must be < 2^32;
/// mirrors delta.zig's "create rejects synthetic sizes >= 2^32" block). Sizes
/// are drawn only from `[2^32, 2^40]`: the guard in `create` returns before
/// any deref or allocation, so an oversized slice can be built from a single
/// fake base pointer without touching 4+ GiB of real memory. A length below
/// 2^32 is out of scope here (it would proceed into the loop and dereference)
/// and is covered instead by `roundTrip`.
pub fn createLargeSizes(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void {
    if (@bitSizeOf(usize) < 33) return; // a length >= 2^32 is unrepresentable on 32-bit
    var prng = std.Random.DefaultPrng.init(seed);
    const rand = prng.random();
    var byte: u8 = 0;
    const min_len: usize = @as(usize, 1) << 32;
    const max_span: usize = (@as(usize, 1) << 40) - min_len;
    var round: usize = 0;
    while (round < rounds) : (round += 1) {
        const len = min_len + rand.uintLessThan(usize, max_span + 1);
        const huge = @as([*]const u8, @ptrCast(&byte))[0..len]; // never dereferenced: the guard returns first
        const variant = rand.uintLessThan(u8, 3); // 0: source oversized, 1: target oversized, 2: both
        const source: []const u8 = if (variant == 1) "" else huge;
        const target: []const u8 = if (variant == 0) "" else huge;
        try std.testing.expectError(delta.CreateError.TargetTooLarge, delta.create(gpa, source, target));
    }
}

/// `targetSize` on arbitrary bytes never crashes: only `MalformedDelta` or a
/// value within the u32 cap `decodeInt` enforces comes back (delta §4.1).
pub fn targetSizeGarbage(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void {
    _ = gpa;
    var prng = std.Random.DefaultPrng.init(seed);
    const rand = prng.random();
    var round: usize = 0;
    while (round < rounds) : (round += 1) {
        var buf: [64]u8 = undefined;
        const len = rand.uintLessThan(usize, buf.len + 1);
        for (buf[0..len]) |*b| b.* = if (rand.boolean())
            rand.int(u8)
        else
            delta.digit64[rand.uintLessThan(usize, delta.digit64.len)];
        if (delta.targetSize(buf[0..len])) |size| {
            try std.testing.expect(size <= std.math.maxInt(u32));
        } else |err| {
            try std.testing.expect(err == error.MalformedDelta);
        }
    }
}