lib/deadalloc/src/properties/exterminator.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const hypothesis = @import("hypothesis");
3 const deadalloc = @import("deadalloc");
4
5 const RepairPolicy = deadalloc.RepairPolicy;
6
7 pub fn settings() hypothesis.Settings {
8 return hypothesis.Settings.quick()
9 .withSeed(0xde47_a110)
10 .withDatabase("zig-out/hypothesis-failures/deadalloc");
11 }
12
13 fn drawPropertyUsize(conjecture: *hypothesis.ConjectureData, min: usize, max: usize, shrink_towards: usize) !usize {
14 return @intCast(try conjecture.drawInteger(
15 @intCast(min),
16 @intCast(max),
17 @intCast(shrink_towards),
18 ));
19 }
20
21 pub const RepairPolicyProperty = struct {
22 pub fn property(conjecture: *hypothesis.ConjectureData, allocator: std.mem.Allocator) !void {
23 _ = allocator;
24
25 const red_zone_bytes = try drawPropertyUsize(conjecture, 0, 2048, 16);
26 const quarantine_epochs: u64 = @intCast(try drawPropertyUsize(conjecture, 0, 8192, 16));
27 const buffer_overflow = try drawPropertyUsize(conjecture, 0, 1, 0);
28 const use_after_free = try drawPropertyUsize(conjecture, 0, 1, 0);
29 const double_free = try drawPropertyUsize(conjecture, 0, 1, 0);
30 const alloc_site = try drawPropertyUsize(conjecture, 1, 4096, 1);
31 const free_site = try drawPropertyUsize(conjecture, 4097, 8192, 4097);
32 const requested_len = try drawPropertyUsize(conjecture, 1, 256, 16);
33 const overflow_extra = try drawPropertyUsize(conjecture, 0, 64, 0);
34
35 const current: RepairPolicy = .{
36 .red_zone_bytes = red_zone_bytes,
37 .quarantine_epochs = quarantine_epochs,
38 };
39 var report: deadalloc.Report = .{
40 .counters = .{
41 .buffer_overflow = buffer_overflow,
42 .use_after_free = use_after_free,
43 .double_free = double_free,
44 },
45 };
46 if (buffer_overflow > 0) {
47 report.issues.append(.{
48 .kind = .buffer_overflow,
49 .offset = requested_len + overflow_extra,
50 .requested_len = requested_len,
51 .allocation_return_address = alloc_site,
52 });
53 }
54 if (use_after_free > 0 or double_free > 0) {
55 report.issues.append(.{
56 .kind = if (use_after_free > 0) .use_after_free else .double_free,
57 .allocation_return_address = alloc_site,
58 .free_return_address = free_site,
59 });
60 }
61 const repaired = RepairPolicy.fromReport(report, current);
62
63 if (buffer_overflow == 0) {
64 try std.testing.expectEqual(current.red_zone_bytes, repaired.red_zone_bytes);
65 } else if (current.red_zone_bytes == 0) {
66 try std.testing.expectEqual(@as(usize, 16), repaired.red_zone_bytes);
67 } else if (current.red_zone_bytes < 1024) {
68 try std.testing.expectEqual(@min(current.red_zone_bytes * 2, 1024), repaired.red_zone_bytes);
69 } else {
70 try std.testing.expectEqual(current.red_zone_bytes, repaired.red_zone_bytes);
71 }
72
73 if (use_after_free == 0 and double_free == 0) {
74 try std.testing.expectEqual(current.quarantine_epochs, repaired.quarantine_epochs);
75 } else if (current.quarantine_epochs == 0) {
76 try std.testing.expectEqual(@as(u64, 1), repaired.quarantine_epochs);
77 } else if (current.quarantine_epochs < 4096) {
78 try std.testing.expectEqual(@min(current.quarantine_epochs * 2, 4096), repaired.quarantine_epochs);
79 } else {
80 try std.testing.expectEqual(current.quarantine_epochs, repaired.quarantine_epochs);
81 }
82
83 if (buffer_overflow == 0) {
84 try std.testing.expectEqual(@as(usize, 0), repaired.repairs.overflowPadding(alloc_site));
85 } else {
86 try std.testing.expectEqual(overflow_extra + 1, repaired.repairs.overflowPadding(alloc_site));
87 }
88
89 if (use_after_free == 0 and double_free == 0) {
90 try std.testing.expectEqual(@as(u64, 0), repaired.repairs.lifeExtension(alloc_site, free_site));
91 } else {
92 try std.testing.expectEqual(@as(u64, 1), repaired.repairs.lifeExtension(alloc_site, free_site));
93 }
94 }
95 };
96
97 test "property: repair policy grows diagnostics monotonically" {
98 try hypothesis.checkNamed(RepairPolicyProperty, "deadalloc-repair-policy", settings());
99 }