lib/stabilizer/src/properties/image.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const hypothesis = @import("hypothesis");
3 const stabilizer = @import("stabilizer");
4
5 const Allocator = std.mem.Allocator;
6 const Alignment = std.mem.Alignment;
7
8 const default_shuffle_slots = stabilizer.default_shuffle_slots;
9 const default_region_size = stabilizer.default_region_size;
10 const code_alignment = stabilizer.code_alignment;
11 const stack_alignment = stabilizer.stack_alignment;
12 const rerandomize_interval_ms = stabilizer.rerandomize_interval_ms;
13 const max_stack_pad_unit = stabilizer.max_stack_pad_unit;
14
15 const Reference = stabilizer.Reference;
16 const Marsaglia = stabilizer.Marsaglia;
17 const StackPads = stabilizer.StackPads;
18 const ShuffleAllocator = stabilizer.ShuffleAllocator;
19 const sizeClass = stabilizer.sizeClass;
20 const FunctionId = stabilizer.FunctionId;
21 const LocationId = stabilizer.LocationId;
22 const FunctionOptions = stabilizer.FunctionOptions;
23 const FunctionEntryState = stabilizer.FunctionEntryState;
24 const FunctionLocation = stabilizer.FunctionLocation;
25 const CodeRandomizer = stabilizer.CodeRandomizer;
26 const Runtime = stabilizer.Runtime;
27
28 fn settings() hypothesis.Settings {
29 return hypothesis.Settings.quick()
30 .withSeed(0x57ab_11e5)
31 .withDatabase("zig-out/hypothesis-failures/stabilizer");
32 }
33
34 fn drawUsize(data: *hypothesis.ConjectureData, min: usize, max: usize, shrink_towards: usize) !usize {
35 return @intCast(try data.drawInteger(
36 @intCast(min),
37 @intCast(max),
38 @intCast(shrink_towards),
39 ));
40 }
41
42 fn drawAlignment(data: *hypothesis.ConjectureData) !Alignment {
43 const shift = try drawUsize(data, 0, 8, 0);
44 return .fromByteUnits(@as(usize, 1) << @as(u6, @intCast(shift)));
45 }
46
47 test "code randomizer copies function image bytes through relocation" {
48 var code = CodeRandomizer.init(std.testing.allocator, .{ .shuffle_slots = 4 }, 55);
49 defer code.deinit();
50
51 const function = try code.registerFunctionImage(.{
52 .code = &.{ 0xaa, 0xbb, 0xcc },
53 .table = &.{ 0x10, 0x20 },
54 .table_adjacent = true,
55 });
56 const first = try code.relocate(function);
57 try std.testing.expectEqualSlices(u8, &.{ 0xaa, 0xbb, 0xcc, 0x10, 0x20 }, first.contents);
58
59 first.contents[1] = 0xee;
60 const second = try code.relocate(function);
61 try std.testing.expectEqualSlices(u8, &.{ 0xaa, 0xee, 0xcc, 0x10, 0x20 }, second.contents);
62 }
63
64 test "code randomizer keeps non-adjacent relocation table out of location image" {
65 var code = CodeRandomizer.init(std.testing.allocator, .{ .shuffle_slots = 4 }, 55);
66 defer code.deinit();
67
68 const function = try code.registerFunctionImage(.{
69 .code = &.{ 0xaa, 0xbb, 0xcc },
70 .table = &.{ 0x10, 0x20 },
71 .table_adjacent = false,
72 });
73 const location = try code.relocate(function);
74 try std.testing.expectEqualSlices(u8, &.{ 0xaa, 0xbb, 0xcc }, location.contents);
75 }
76
77 const CodeImageProperty = struct {
78 pub fn property(data: *hypothesis.ConjectureData, allocator: Allocator) !void {
79 var code = CodeRandomizer.init(allocator, .{
80 .shuffle_slots = 16,
81 .alignment = code_alignment,
82 }, 0x494d_4147);
83 defer code.deinit();
84
85 const code_len = try drawUsize(data, 1, 64, 4);
86 const image_code = try allocator.alloc(u8, code_len);
87 defer allocator.free(image_code);
88 for (image_code, 0..) |*byte, index| byte.* = @truncate(index);
89
90 const table_words = try drawUsize(data, 1, 8, 2);
91 const table = try allocator.alloc(u8, table_words * @sizeOf(usize));
92 defer allocator.free(table);
93 @memset(table, 0);
94
95 const slot_count = try drawUsize(data, 0, table_words, 1);
96 var slots_buffer: [8]usize = undefined;
97 var slot_index: usize = 0;
98 while (slot_index < slot_count) : (slot_index += 1) {
99 slots_buffer[slot_index] = slot_index * @sizeOf(usize);
100 }
101
102 const function = try code.registerFunctionImage(.{
103 .code = image_code,
104 .table = table,
105 .table_adjacent = true,
106 .stack_pad_unit = 0,
107 .stack_pad_table_offsets = slots_buffer[0..slot_count],
108 });
109 const location = try code.relocate(function);
110 try std.testing.expectEqual(code_len + table.len, location.contents.len);
111 if (slot_count > 0) {
112 const address = code.stackPadAddress(function).?;
113 slot_index = 0;
114 while (slot_index < slot_count) : (slot_index += 1) {
115 const offset = code_len + slots_buffer[slot_index];
116 try std.testing.expectEqual(
117 address,
118 std.mem.readInt(usize, location.contents[offset..][0..@sizeOf(usize)], .little),
119 );
120 }
121 } else {
122 try std.testing.expect(code.stackPadAddress(function) == null);
123 }
124 }
125 };
126
127 test "pbt: code image relocation patches stack pad tables" {
128 try hypothesis.checkNamed(CodeImageProperty, "stabilizer-code-image", settings());
129 }