Skip to content

Commit b19c9d0

Browse files
committed
Implement designated CLI subcommands for fuzzers
* Re-work, re-organize, and centralize the allocators fuzzer code. * Refactor the snapshot fuzzer. * Surface-level refactor for the gossip service fuzzer. * Small refactor for the gossip table fuzzer.
1 parent e7746f6 commit b19c9d0

File tree

11 files changed

+357
-322
lines changed

11 files changed

+357
-322
lines changed

.circleci/config.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ jobs:
221221
at: workspace
222222
- run:
223223
name: Run Gossip Service Fuzzer
224-
command: workspace/zig-out-release/bin/fuzz --seed 19 gossip-service 10000
224+
command: workspace/zig-out-release/bin/fuzz --seed 19 gossip-service --max-messages 10_000
225225

226226
gossip_table_fuzz:
227227
executor: linux-executor
@@ -231,7 +231,7 @@ jobs:
231231
at: workspace
232232
- run:
233233
name: Run Gossip Table Fuzzer
234-
command: workspace/zig-out-release/bin/fuzz --seed 19 gossip-table 100000
234+
command: workspace/zig-out-release/bin/fuzz --seed 19 gossip-table --max-actions 100_000
235235

236236
allocators_fuzz:
237237
executor: linux-executor
@@ -241,7 +241,7 @@ jobs:
241241
at: workspace
242242
- run:
243243
name: Run Allocators Fuzzer
244-
command: workspace/zig-out-release/bin/fuzz --seed 19 allocators 10000
244+
command: workspace/zig-out-release/bin/fuzz --seed 19 allocators --max-actions 10_000
245245

246246
ledger_fuzz:
247247
executor: linux-executor
@@ -251,7 +251,7 @@ jobs:
251251
at: workspace
252252
- run:
253253
name: Run Ledger Fuzzer
254-
command: workspace/zig-out-release/bin/fuzz --seed 19 ledger 10000
254+
command: workspace/zig-out-release/bin/fuzz --seed 19 ledger --max-actions 10_000
255255

256256
solana_conformance:
257257
executor: linux-executor

scripts/kcov_fuzz_allocators.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ kcov \
2323
--include-pattern=src/utils/allocators.zig \
2424
--exclude-pattern=$HOME/.cache \
2525
kcov-output/ \
26-
./zig-out/bin/fuzz --seed 19 allocators 50_000
26+
./zig-out/bin/fuzz --seed 19 allocators --max-actions 50_000
2727

2828
# open report
2929
echo "=> Opening kcov-output/index.html"

scripts/kcov_fuzz_gossip.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ kcov \
2323
--include-pattern=src/gossip/ \
2424
--exclude-pattern=$HOME/.cache \
2525
kcov-output/ \
26-
./zig-out/bin/fuzz --seed 19 gossip-service 50_000
26+
./zig-out/bin/fuzz --seed 19 gossip-service --max-messages 50_000
2727

2828
# open report
2929
echo "=> Opening kcov-output/index.html"

scripts/kcov_fuzz_gossip_table.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ kcov \
2323
--include-pattern=src/gossip/ \
2424
--exclude-pattern=$HOME/.cache \
2525
kcov-output/ \
26-
./zig-out/bin/fuzz --seed 19 gossip-table 50_000
26+
./zig-out/bin/fuzz --seed 19 gossip-table --max-actions 50_000
2727

2828
# open report
2929
echo "=> Opening kcov-output/index.html"

src/accountsdb/fuzz.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub const RunCmd = struct {
5757
.max_slots = .{
5858
.kind = .named,
5959
.name_override = null,
60-
.alias = .none,
60+
.alias = .m,
6161
.default_value = null,
6262
.config = {},
6363
.help = "The number of slots number which, when surpassed, will exit the fuzzer.",

src/accountsdb/fuzz_snapshot.zig

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const std = @import("std");
22
const sig = @import("../sig.zig");
3+
const cli = @import("cli");
34

45
const bincode = sig.bincode;
56

@@ -15,42 +16,40 @@ const SnapshotManifest = sig.accounts_db.Manifest;
1516

1617
const MAX_FUZZ_TIME_NS = std.time.ns_per_s * 100_000;
1718

18-
pub fn run() !void {
19-
const seed = std.crypto.random.int(u64);
20-
21-
var gpa: std.heap.DebugAllocator(.{}) = .init;
22-
defer _ = gpa.deinit();
23-
const allocator = gpa.allocator();
24-
25-
{
26-
// open and append seed
27-
const SEED_FILE_PATH = sig.TEST_DATA_DIR ++ "fuzz_snapshot_seeds.txt";
28-
const seed_file = try std.fs.cwd().createFile(SEED_FILE_PATH, .{ .truncate = false });
29-
defer seed_file.close();
19+
pub const RunCmd = struct {
20+
pub const cmd_info: cli.CommandInfo(RunCmd) = .{
21+
.help = .{
22+
.short = "Fuzz snapshots.",
23+
.long = null,
24+
},
25+
.sub = .{},
26+
};
27+
};
3028

31-
try seed_file.writer().print("{}\n", .{seed});
32-
}
33-
std.debug.print("seed: {}\n", .{seed});
29+
pub fn run(
30+
allocator: std.mem.Allocator,
31+
seed: u64,
32+
run_cmd: RunCmd,
33+
) !void {
34+
_ = run_cmd;
3435

35-
var prng = std.Random.DefaultPrng.init(seed);
36-
const random = prng.random();
36+
var prng_state: std.Random.DefaultPrng = .init(seed);
37+
const prng = prng_state.random();
3738

38-
var bytes_buffer = std.ArrayList(u8).init(allocator);
39-
defer bytes_buffer.deinit();
39+
var bytes_buffer: std.ArrayListUnmanaged(u8) = .empty;
40+
defer bytes_buffer.deinit(allocator);
4041

4142
var i: u64 = 0;
42-
43-
var timer = try std.time.Timer.start();
43+
var timer: std.time.Timer = try .start();
4444
while (timer.read() < MAX_FUZZ_TIME_NS) : (i += 1) {
4545
bytes_buffer.clearRetainingCapacity();
4646

47-
const manifest_original: SnapshotManifest = try randomSnapshotManifest(allocator, random);
47+
const manifest_original: SnapshotManifest = try randomSnapshotManifest(allocator, prng);
4848
defer manifest_original.deinit(allocator);
4949

50-
try bytes_buffer.ensureUnusedCapacity(bincode.sizeOf(manifest_original, .{}) * 2);
51-
50+
try bytes_buffer.ensureUnusedCapacity(allocator, bincode.sizeOf(manifest_original, .{}) * 2);
5251
const original_bytes_start = bytes_buffer.items.len;
53-
try bincode.write(bytes_buffer.writer(), manifest_original, .{});
52+
try bincode.write(bytes_buffer.writer(allocator), manifest_original, .{});
5453
const original_bytes_end = bytes_buffer.items.len;
5554

5655
const snapshot_deserialized = try bincode.readFromSlice(
@@ -62,7 +61,7 @@ pub fn run() !void {
6261
defer snapshot_deserialized.deinit(allocator);
6362

6463
const serialized_bytes_start = bytes_buffer.items.len;
65-
try bincode.write(bytes_buffer.writer(), snapshot_deserialized, .{});
64+
try bincode.write(bytes_buffer.writer(allocator), snapshot_deserialized, .{});
6665
const serialized_bytes_end = bytes_buffer.items.len;
6766

6867
const original_bytes = bytes_buffer.items[original_bytes_start..original_bytes_end];

src/fuzz.zig

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const gossip_service_fuzz = sig.gossip.fuzz_service;
77
const gossip_table_fuzz = sig.gossip.fuzz_table;
88
const snapshot_fuzz = sig.accounts_db.fuzz_snapshot;
99
const ledger_fuzz = sig.ledger.fuzz_ledger;
10+
const allocators_fuzz = sig.utils.allocators.fuzzer;
1011

1112
// Supported fuzz filters.
1213
// NOTE: changing these enum variants will require a change to the fuzz/kcov in `scripts/`
@@ -24,34 +25,13 @@ const Cmd = struct {
2425
seed: ?u64,
2526
fuzzer: ?union(FuzzFilter) {
2627
accountsdb: accountsdb_fuzz.RunCmd,
27-
snapshot: FuzzerTodo,
28-
gossip_service: FuzzerTodo,
29-
gossip_table: FuzzerTodo,
30-
ledger: FuzzerTodo,
31-
allocators: FuzzerTodo,
28+
snapshot: snapshot_fuzz.RunCmd,
29+
gossip_service: gossip_service_fuzz.RunCmd,
30+
gossip_table: gossip_table_fuzz.RunCmd,
31+
ledger: ledger_fuzz.RunCmd,
32+
allocators: allocators_fuzz.RunCmd,
3233
},
3334

34-
const FuzzerTodo = struct {
35-
args: []const []const u8,
36-
37-
pub const cmd_info: cli.CommandInfo(FuzzerTodo) = .{
38-
.help = .{
39-
.short = "TODO: implement bespoke CLI integration for this fuzzer.",
40-
.long = null,
41-
},
42-
.sub = .{
43-
.args = .{
44-
.kind = .positional,
45-
.name_override = null,
46-
.alias = .none,
47-
.default_value = &.{},
48-
.config = .string,
49-
.help = "Args to pass to the specified fuzzer.",
50-
},
51-
},
52-
};
53-
};
54-
5535
const parser = cli.Parser(Cmd, .{
5636
.help = .{
5737
.short = "Fuzz a component of the validator.",
@@ -61,33 +41,33 @@ const Cmd = struct {
6141
.data_dir = .{
6242
.kind = .named,
6343
.name_override = null,
64-
.alias = .none,
44+
.alias = .d,
6545
.default_value = null,
6646
.config = .string,
6747
.help = "Directory for all fuzzers to store their on-disk data relative to.",
6848
},
6949
.seed = .{
7050
.kind = .named,
7151
.name_override = null,
72-
.alias = .none,
52+
.alias = .s,
7353
.default_value = null,
7454
.config = {},
7555
.help = "Seed for the PRNG for all random actions taken during fuzzing.",
7656
},
7757
.fuzzer = .{
7858
.accountsdb = accountsdb_fuzz.RunCmd.cmd_info,
79-
.snapshot = FuzzerTodo.cmd_info,
80-
.gossip_service = FuzzerTodo.cmd_info,
81-
.gossip_table = FuzzerTodo.cmd_info,
82-
.ledger = FuzzerTodo.cmd_info,
83-
.allocators = FuzzerTodo.cmd_info,
59+
.snapshot = snapshot_fuzz.RunCmd.cmd_info,
60+
.gossip_service = gossip_service_fuzz.RunCmd.cmd_info,
61+
.gossip_table = gossip_table_fuzz.RunCmd.cmd_info,
62+
.ledger = ledger_fuzz.RunCmd.cmd_info,
63+
.allocators = allocators_fuzz.RunCmd.cmd_info,
8464
},
8565
},
8666
});
8767
};
8868

8969
pub fn main() !void {
90-
var gpa_state: std.heap.DebugAllocator(.{}) = .init;
70+
var gpa_state: std.heap.DebugAllocator(.{ .safety = true }) = .init;
9171
defer _ = gpa_state.deinit();
9272
const gpa = gpa_state.allocator();
9373

@@ -165,10 +145,19 @@ pub fn main() !void {
165145
run_cmd,
166146
),
167147

168-
.snapshot => try snapshot_fuzz.run(),
169-
.gossip_service => |run_cmd| try gossip_service_fuzz.run(seed, run_cmd.args),
170-
.gossip_table => |run_cmd| try gossip_table_fuzz.run(seed, run_cmd.args),
171-
.ledger => |run_cmd| try ledger_fuzz.run(seed, run_cmd.args),
172-
.allocators => |run_cmd| try sig.utils.allocators.runFuzzer(seed, run_cmd.args),
148+
.snapshot,
149+
=> |run_cmd| try snapshot_fuzz.run(gpa, seed, run_cmd),
150+
151+
.gossip_service,
152+
=> |run_cmd| try gossip_service_fuzz.run(gpa, seed, run_cmd),
153+
154+
.gossip_table,
155+
=> |run_cmd| try gossip_table_fuzz.run(gpa, .from(logger), seed, run_cmd),
156+
157+
.allocators,
158+
=> |run_cmd| try allocators_fuzz.run(gpa, seed, run_cmd),
159+
160+
.ledger,
161+
=> |run_cmd| try ledger_fuzz.run(seed, run_cmd),
173162
}
174163
}

src/gossip/fuzz_service.zig

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! ./zig-out/bin/fuzz <seed> <num_messages>
33
const std = @import("std");
44
const sig = @import("../sig.zig");
5+
const cli = @import("cli");
56

67
const bincode = sig.bincode;
78

@@ -36,31 +37,38 @@ const SLEEP_TIME = Duration.zero();
3637

3738
const Logger = sig.trace.Logger("gossip.fuzz_service");
3839

39-
pub fn run(seed: u64, args: []const []const u8) !void {
40-
var gpa: std.heap.DebugAllocator(.{}) = .init;
41-
defer _ = gpa.deinit();
42-
const allocator = gpa.allocator();
43-
44-
// logs
45-
var std_logger = try sig.trace.ChannelPrintLogger.init(.{
46-
.allocator = std.heap.c_allocator,
47-
.max_level = sig.trace.Level.debug,
48-
.max_buffer = 1 << 20,
49-
}, null);
50-
defer std_logger.deinit();
40+
pub const RunCmd = struct {
41+
max_messages: ?u64,
42+
43+
pub const cmd_info: cli.CommandInfo(@This()) = .{
44+
.help = .{
45+
.short = "Fuzz gossip.",
46+
.long = null,
47+
},
48+
.sub = .{
49+
.max_messages = .{
50+
.kind = .named,
51+
.name_override = null,
52+
.alias = .m,
53+
.default_value = null,
54+
.config = {},
55+
.help = "Maximum number of messages before exiting the fuzzer.",
56+
},
57+
},
58+
};
59+
};
5160

61+
pub fn run(
62+
allocator: std.mem.Allocator,
63+
seed: u64,
64+
args: RunCmd,
65+
) !void {
5266
// setup randomness
53-
var prng = std.Random.DefaultPrng.init(seed);
67+
var prng_state: std.Random.DefaultPrng = .init(seed);
68+
const prng = prng_state.random();
5469

5570
// parse cli args to define where to send packets
56-
const maybe_max_messages_string: ?[]const u8 = if (args.len == 0) null else args[0];
57-
const maybe_max_messages = blk: {
58-
if (maybe_max_messages_string) |max_messages_str| {
59-
break :blk try std.fmt.parseInt(u64, max_messages_str, 10);
60-
} else {
61-
break :blk null;
62-
}
63-
};
71+
const maybe_max_messages = args.max_messages;
6472

6573
// we need two clients:
6674
// 1) fuzz_client: attacker
@@ -113,12 +121,7 @@ pub fn run(seed: u64, args: []const []const u8) !void {
113121
}
114122

115123
// start fuzzing
116-
try fuzz(
117-
dirty_allocator,
118-
maybe_max_messages,
119-
prng.random(),
120-
fuzz_client,
121-
);
124+
try fuzz(dirty_allocator, maybe_max_messages, prng, fuzz_client);
122125
}
123126

124127
pub fn fuzz(

0 commit comments

Comments
 (0)