lib/machine/src/world/source.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const restore = @import("restore.zig");
  2 const std = @import("std");
  3 
  4 /// Rejections of replay sources, so a caller matches on them to tell an overlapping
  5 /// buffer apart from a miscounted one. `SourceAlias` reports that a reader or a
  6 /// byte range lies on top of memory the world is using.
  7 pub const Error = error{
  8     RestoredShapeMismatch,
  9     SourceAlias,
 10     SourceCountMismatch,
 11 };
 12 
 13 const source_limit: usize = 2;
 14 
 15 /// Rejects a replay reader that overlaps the restored world, its ledger, instance
 16 /// storage, guest RAM, or another reader. Every replay entry point calls this before
 17 /// it reads a byte, because a reader over live memory would see the change it is
 18 /// verifying. The call accepts one or two readers. A reader whose own value overlaps
 19 /// its buffer is rejected too.
 20 pub fn validate(
 21     restored: *restore.Restored,
 22     readers: []const *std.Io.Reader,
 23 ) Error!void {
 24     if (readers.len == 0 or readers.len > source_limit) {
 25         return error.SourceCountMismatch;
 26     }
 27     if (restored.node_count > restored.nodes.len) {
 28         return error.RestoredShapeMismatch;
 29     }
 30     for (readers, 0..) |reader, index| {
 31         if (buffersOverlap(reader.buffer, std.mem.asBytes(reader)) or
 32             readerAliases(reader, std.mem.asBytes(restored)) or
 33             readerAliases(reader, std.mem.asBytes(restored.fabric)))
 34         {
 35             return error.SourceAlias;
 36         }
 37         for (readers[0..index]) |previous| {
 38             if (readerAliases(reader, std.mem.asBytes(previous)) or
 39                 readerAliases(previous, std.mem.asBytes(reader)) or
 40                 buffersOverlap(reader.buffer, previous.buffer))
 41             {
 42                 return error.SourceAlias;
 43             }
 44         }
 45         for (&restored.nodes) |*node| {
 46             const machine = if (node.machine) |*value| value else continue;
 47             if (readerAliases(reader, &machine.storage.bytes) or
 48                 readerAliases(reader, machine.ram))
 49             {
 50                 return error.SourceAlias;
 51             }
 52         }
 53     }
 54 }
 55 
 56 /// Rejects a byte range that overlaps the restored world, its ledger, or any instance
 57 /// storage or guest RAM, so a caller holding a plan or a buffer gets the same protection.
 58 pub fn validateMemory(
 59     restored: *restore.Restored,
 60     bytes: []const u8,
 61 ) Error!void {
 62     if (restored.node_count > restored.nodes.len) {
 63         return error.RestoredShapeMismatch;
 64     }
 65     if (memoryAliases(restored, bytes)) return error.SourceAlias;
 66 }
 67 
 68 fn memoryAliases(
 69     restored: *restore.Restored,
 70     bytes: []const u8,
 71 ) bool {
 72     if (buffersOverlap(bytes, std.mem.asBytes(restored)) or
 73         buffersOverlap(bytes, std.mem.asBytes(restored.fabric)))
 74     {
 75         return true;
 76     }
 77     for (&restored.nodes) |*node| {
 78         const machine = if (node.machine) |*value| value else continue;
 79         if (buffersOverlap(bytes, &machine.storage.bytes) or
 80             buffersOverlap(bytes, machine.ram))
 81         {
 82             return true;
 83         }
 84     }
 85     return false;
 86 }
 87 
 88 fn readerAliases(reader: *std.Io.Reader, bytes: []const u8) bool {
 89     return buffersOverlap(std.mem.asBytes(reader), bytes) or
 90         buffersOverlap(reader.buffer, bytes);
 91 }
 92 
 93 fn buffersOverlap(left: []const u8, right: []const u8) bool {
 94     if (left.len == 0 or right.len == 0) return false;
 95     const left_start = @intFromPtr(left.ptr);
 96     const right_start = @intFromPtr(right.ptr);
 97     const left_end = std.math.add(usize, left_start, left.len) catch return true;
 98     const right_end = std.math.add(usize, right_start, right.len) catch return true;
 99     return left_start < right_end and right_start < left_end;
100 }