Skip to content

Commit

Permalink
Merge pull request #7574 from roc-lang/spike-fuzzing
Browse files Browse the repository at this point in the history
Initial Fuzzing Spike
  • Loading branch information
lukewilliamboswell authored Feb 4, 2025
2 parents fb6bf56 + bcb409c commit 59ff9bd
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 9 deletions.
67 changes: 67 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
const std = @import("std");
const afl = @import("zig-afl-kit");
const LazyPath = std.Build.LazyPath;
const ResolvedTarget = std.Build.ResolvedTarget;
const OptimizeMode = std.builtin.OptimizeMode;
const Import = std.Build.Module.Import;

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
Expand Down Expand Up @@ -41,4 +46,66 @@ pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Run all tests included in src/tests.zig");

test_step.dependOn(&run_tests.step);

// Fuzz targets
const fuzz = b.step("fuzz", "Generate all fuzz executables");

// TODO: this just builds the fuzz target. Afterwards, they are still awkward to orchestrate and run.
// Make a script to manage the corpus and run the fuzzers (or at least some good docs)
// Likely will should check in a minimal corpus somewhere so we don't always start from zero.
add_fuzz_target(
b,
fuzz,
target,
optimize,
"cli",
b.path("src/fuzz/cli.zig"),
&[_]Import{
.{ .name = "cli", .module = b.createModule(.{ .root_source_file = b.path("src/cli.zig") }) },
},
);
}

fn add_fuzz_target(
b: *std.Build,
fuzz: *std.Build.Step,
target: ResolvedTarget,
optimize: OptimizeMode,
name: []const u8,
root_source_file: LazyPath,
imports: []const Import,
) void {
var name_obj = std.ArrayList(u8).init(b.allocator);
defer name_obj.deinit();
name_obj.writer().print("{s}_obj", .{name}) catch unreachable;

var name_exe = std.ArrayList(u8).init(b.allocator);
defer name_exe.deinit();
name_exe.writer().print("fuzz-{s}", .{name}) catch unreachable;

var step_msg = std.ArrayList(u8).init(b.allocator);
defer step_msg.deinit();
step_msg.writer().print("Generate fuzz executable for {s}", .{name}) catch unreachable;

const fuzz_obj = b.addObject(.{
.name = name_obj.items,
.root_source_file = root_source_file,
.target = target,
.optimize = .Debug,
});

for (imports) |import| {
fuzz_obj.root_module.addImport(import.name, import.module);
}

// TODO: Once 0.14.0 is released, uncomment this. Will make fuzzing work better.
// fuzz_obj.root_module.fuzz = true;
fuzz_obj.root_module.stack_check = false; // not linking with compiler-rt
fuzz_obj.root_module.link_libc = true; // afl runtime depends on libc

const fuzz_exe = afl.addInstrumentedExe(b, target, optimize, fuzz_obj);
const fuzz_step = b.step(name_exe.items, step_msg.items);
fuzz_step.dependOn(&b.addInstallBinFile(fuzz_exe, name_exe.items).step);

fuzz.dependOn(fuzz_step);
}
7 changes: 6 additions & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
.name = "roc",
.version = "0.0.0",
.minimum_zig_version = "0.13.0",
.dependencies = .{},
.dependencies = .{
.@"zig-afl-kit" = .{
.url = "git+https://github.com/kristoff-it/zig-afl-kit#88c6b71377767c1b8d26979b0adfa12a58d988dd",
.hash = "1220796f7d2d9a2d4d7f8339ee0b14aa4bf133a15ae9ba39c941cc68e08d5c5ce9a2",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
Expand Down
4 changes: 4 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@
shellHook = ''
export LLVM_SYS_180_PREFIX="${llvmPkgs.dev}"
${aliases}
# https://github.com/ziglang/zig/issues/18998
unset NIX_CFLAGS_COMPILE
unset NIX_LDFLAGS
'';
};

Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions src/fuzz/cli-corpus/build.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
roc build --optimize --profiling main.roc
1 change: 1 addition & 0 deletions src/fuzz/cli-corpus/run.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
roc run repo.roc /tmp/input.txt
54 changes: 54 additions & 0 deletions src/fuzz/cli.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/// This is just a silly fuzz test to start getting the infra setup.
/// It shows the basic that other fuzz tests likely should build off of.
///
/// To run:
/// 1. zig build fuzz-cli
/// 2. ./zig-out/AFLplusplus/bin/afl-fuzz -i src/fuzz/cli-corpus/ -o /tmp/cli-out/ zig-out/bin/fuzz-cli
///
/// Other afl commands also avilable in `./zig-out/AFLplusplus/bin`
///
const std = @import("std");
const cli = @import("cli");
const RocCmd = cli.RocCmd;
const RocOpt = cli.RocOpt;

export fn zig_fuzz_init() void {}

export fn zig_fuzz_test(buf: [*]u8, len: isize) void {
// We reinitialize the gpa on every loop of the fuzzer.
// This enables the gpa to do leak checking on each iteration.
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer {
_ = general_purpose_allocator.deinit();
}
const allocator = general_purpose_allocator.allocator();

// Convert the input buffer into what is expected by the test.
const buf_slice = buf[0..@intCast(len)];

var args_list = std.ArrayList([]const u8).init(allocator);
defer args_list.deinit();
var it = std.mem.splitScalar(u8, buf_slice, ' ');
while (it.next()) |param| {
args_list.append(param) catch unreachable;
}

const args = args_list.items;
if (args.len <= 1) {
return;
}

const cmd = args[1];

if (RocCmd.parse(cmd)) |roc_command| {
const parsed_opt = RocOpt.parse(args[2..]) catch return;
const opt = parsed_opt.opt;
const cmd_args = args[(2 + parsed_opt.next_index)..];

// Sadly, there is not really something to verify here.
// We just verify it doesn't crash.
_ = roc_command;
_ = opt;
_ = cmd_args;
}
}
4 changes: 2 additions & 2 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const std = @import("std");
const mem = std.mem;
const Allocator = std.mem.Allocator;
const RocCmd = @import("command.zig").RocCmd;
const RocOpt = @import("command.zig").RocOpt;
const RocCmd = @import("cli.zig").RocCmd;
const RocOpt = @import("cli.zig").RocOpt;

const usage =
\\Usage:
Expand Down
7 changes: 1 addition & 6 deletions src/test.zig
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
const std = @import("std");
const testing = std.testing;

comptime {
testing.refAllDecls(@import("main.zig"));
testing.refAllDecls(@import("command.zig"));
}

test {
std.testing.refAllDecls(@This());
testing.refAllDeclsRecursive(@import("main.zig"));
}

0 comments on commit 59ff9bd

Please sign in to comment.