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 }