tiny.closure.link.cover
Defined in link.
API (10)
Actions
Public operations.
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
Source
Source: lib/closure/src/link/cover.zig
zig
const binary = @import("../binary/root.zig");const std = @import("std");pub const attributions_max: usize = 64;pub const padding_ranges_max: usize = 64;pub const Kind = enum(u8) { root, primitive,};pub const Attribution = struct { kind: Kind, offset: u64, address: u64, length: u64, source_offset: u64 = 0, relocation_count: u32 = 0, source_sha256: [32]u8,};pub const Padding = struct { offset: u64, address: u64, length: u16,};pub const Policy = struct { padding_bytes_max: u16 = 15, padding_byte: u8 = 0xcc,};pub const Scratch = struct { ordered: []Attribution, padding: []Padding,};pub const Report = struct { attributions: []const Attribution, padding: []const Padding, root_bytes: u64, primitive_bytes: u64, padding_bytes: u64, executable_bytes: u64, executable_sha256: [32]u8, work: u64,};const CoverError = error{ AttributionCapacityExceeded, AttributionDigestMissing, AttributionOutsideExecutable, AttributionOverlap, ExecutableCoverageMissing, PaddingCapacityExceeded, PaddingInvalid,};pub const Error = binary.strict.Error || CoverError;pub fn verify( bytes: []const u8, image: binary.strict.Report, input: []const Attribution, policy: Policy, scratch: Scratch,) Error!Report { if (input.len > scratch.ordered.len or input.len > attributions_max or scratch.padding.len > padding_ranges_max) { return error.AttributionCapacityExceeded; } @memcpy(scratch.ordered[0..input.len], input); const ordered = scratch.ordered[0..input.len]; sort(ordered); var work: u64 = input.len; try validateAttributions(image, ordered, &work); var padding_count: usize = 0; var root_bytes: u64 = 0; var primitive_bytes: u64 = 0; var padding_bytes: u64 = 0; var executable_bytes: u64 = 0; var attributed_count: usize = 0; var hasher = std.crypto.hash.sha2.Sha256.init(.{}); for (image.loads) |load| { if (!load.executable) continue; const start = toOffset(load.offset) catch return error.AttributionOutsideExecutable; const length = toOffset(load.content_bytes) catch return error.AttributionOutsideExecutable; const contents = try take(bytes, start, length); hasher.update(contents); executable_bytes = try added(executable_bytes, load.content_bytes); var cursor = load.offset; const end = try added(load.offset, load.content_bytes); for (ordered) |attribution| { work += 1; if (attribution.offset < load.offset or attribution.offset >= end) { continue; } const gap = attribution.offset - cursor; if (gap != 0) { try appendPadding( bytes, load, cursor, gap, policy, scratch.padding, &padding_count, &padding_bytes, &work, ); } switch (attribution.kind) { .root => root_bytes = try added(root_bytes, attribution.length), .primitive => primitive_bytes = try added(primitive_bytes, attribution.length), } cursor = try added(attribution.offset, attribution.length); attributed_count += 1; } if (cursor < end) { try appendPadding( bytes, load, cursor, end - cursor, policy, scratch.padding, &padding_count, &padding_bytes, &work, ); } if (cursor > end) return error.AttributionOverlap; } if (attributed_count != ordered.len) { return error.AttributionOutsideExecutable; } const accounted = try added( try added(root_bytes, primitive_bytes), padding_bytes, ); if (accounted != executable_bytes) { return error.ExecutableCoverageMissing; } var digest: [32]u8 = undefined; hasher.final(&digest); return .{ .attributions = ordered, .padding = scratch.padding[0..padding_count], .root_bytes = root_bytes, .primitive_bytes = primitive_bytes, .padding_bytes = padding_bytes, .executable_bytes = executable_bytes, .executable_sha256 = digest, .work = work, };}fn validateAttributions( image: binary.strict.Report, ordered: []const Attribution, work: *u64,) Error!void { var previous_end: u64 = 0; for (ordered, 0..) |attribution, index| { work.* += 1; if (attribution.length == 0 or !digestKnown(attribution.source_sha256)) { return error.AttributionDigestMissing; } if (index != 0 and attribution.offset < previous_end) { return error.AttributionOverlap; } const end = try added(attribution.offset, attribution.length); var admitted = false; for (image.loads) |load| { work.* += 1; if (!load.executable) continue; const load_end = try added(load.offset, load.content_bytes); if (attribution.offset < load.offset or end > load_end) continue; const relative = attribution.offset - load.offset; if (attribution.address != try added(load.address, relative)) { return error.AttributionOutsideExecutable; } admitted = true; break; } if (!admitted) return error.AttributionOutsideExecutable; previous_end = end; }}fn appendPadding( bytes: []const u8, load: binary.strict.Load, offset: u64, length: u64, policy: Policy, output: []Padding, count: *usize, total: *u64, work: *u64,) Error!void { if (length == 0 or length > policy.padding_bytes_max) { return error.PaddingInvalid; } if (count.* == output.len) return error.PaddingCapacityExceeded; const start = toOffset(offset) catch return error.PaddingInvalid; const length_usize = toOffset(length) catch return error.PaddingInvalid; const contents = try take(bytes, start, length_usize); for (contents) |byte| { work.* += 1; if (byte != policy.padding_byte) return error.PaddingInvalid; } output[count.*] = .{ .offset = offset, .address = try added(load.address, offset - load.offset), .length = @intCast(length), }; count.* += 1; total.* = try added(total.*, length);}fn sort(values: []Attribution) void { var index: usize = 1; while (index < values.len) : (index += 1) { const value = values[index]; var cursor = index; while (cursor > 0 and values[cursor - 1].offset > value.offset) { values[cursor] = values[cursor - 1]; cursor -= 1; } values[cursor] = value; }}fn digestKnown(digest: [32]u8) bool { for (digest) |byte| { if (byte != 0) return true; } return false;}fn take( bytes: []const u8, offset: usize, length: usize,) Error![]const u8 { const end = std.math.add(usize, offset, length) catch return error.BinaryArithmeticOverflow; if (end > bytes.len) return error.TruncatedBinary; return bytes[offset..end];}fn toOffset(value: u64) Error!usize { return std.math.cast(usize, value) orelse error.BinaryArithmeticOverflow;}fn added(left: anytype, right: anytype) Error!u64 { return std.math.add( u64, @intCast(left), @intCast(right), ) catch error.BinaryArithmeticOverflow;}test "coverage attributes every executable byte with bounded padding" { var bytes: [32]u8 = @splat(0); @memset(bytes[4..8], 0x11); @memset(bytes[8..11], 0xcc); @memset(bytes[11..15], 0x22); const loads = [_]binary.strict.Load{.{ .offset = 4, .address = 0x1000, .file_bytes = 16, .content_bytes = 11, .memory_bytes = 16, .readable = true, .writable = false, .executable = true, }}; const image = binary.strict.Report{ .kind = .elf64, .loads = &loads, .relocations = &.{}, .executable_bytes = 11, .initialized_bytes = 0, .memory_bytes = 16, .stack_bytes = 0, .timestamp = 0, .image_base = 0, .work = 1, }; const digest: [32]u8 = @splat(1); const input = [_]Attribution{ .{ .kind = .primitive, .offset = 11, .address = 0x1007, .length = 4, .source_sha256 = digest, }, .{ .kind = .root, .offset = 4, .address = 0x1000, .length = 4, .source_sha256 = digest, }, }; var ordered: [2]Attribution = undefined; var padding: [1]Padding = undefined; const report = try verify( &bytes, image, &input, .{}, .{ .ordered = &ordered, .padding = &padding }, ); try std.testing.expectEqual(@as(u64, 4), report.root_bytes); try std.testing.expectEqual(@as(u64, 4), report.primitive_bytes); try std.testing.expectEqual(@as(u64, 3), report.padding_bytes); try std.testing.expectEqual(@as(u64, 11), report.executable_bytes);}test "coverage fails closed on foreign and excessive gaps" { var bytes: [32]u8 = @splat(0xcc); const loads = [_]binary.strict.Load{.{ .offset = 4, .address = 0x1000, .file_bytes = 16, .content_bytes = 16, .memory_bytes = 16, .readable = true, .writable = false, .executable = true, }}; const image = binary.strict.Report{ .kind = .elf64, .loads = &loads, .relocations = &.{}, .executable_bytes = 16, .initialized_bytes = 0, .memory_bytes = 16, .stack_bytes = 0, .timestamp = 0, .image_base = 0, .work = 1, }; const digest: [32]u8 = @splat(1); const input = [_]Attribution{.{ .kind = .root, .offset = 4, .address = 0x1000, .length = 1, .source_sha256 = digest, }}; var ordered: [1]Attribution = undefined; var padding: [1]Padding = undefined; try std.testing.expectError( error.PaddingInvalid, verify( &bytes, image, &input, .{ .padding_bytes_max = 14 }, .{ .ordered = &ordered, .padding = &padding }, ), ); bytes[5] = 0x90; const full = [_]Attribution{.{ .kind = .root, .offset = 6, .address = 0x1002, .length = 14, .source_sha256 = digest, }}; try std.testing.expectError( error.PaddingInvalid, verify( &bytes, image, &full, .{}, .{ .ordered = &ordered, .padding = &padding }, ), );}Source: lib/closure/src/link/root.zig:1
zig
pub const cover = @import("cover.zig");Audit
| Definitions | 11 |
|---|---|
| Public names | 20 |
| Members | 24 |
| Version | 26.7.0 |
| Revision | daab053ee433 |