|
| 1 | +/- Copyright 2026 The Fuchsia Authors |
| 2 | +
|
| 3 | +Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 |
| 4 | +<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT |
| 5 | +license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. |
| 6 | +This file may not be copied, modified, or distributed except according to |
| 7 | +those terms. -/ |
| 8 | + |
| 9 | +module |
| 10 | +public import Specs |
| 11 | +public import Proofs.SafetyChecks |
| 12 | +public import Proofs.Byteorder |
| 13 | +@[expose] public section |
| 14 | + |
| 15 | +/-! |
| 16 | +These functions are the size selection and byte-copy implementation called by |
| 17 | +IntoBytes's three public write methods. The proofs include unsuccessful writes |
| 18 | +and the unaffected destination region, rather than merely checking copied bytes. |
| 19 | +The source-bound copy interpretations retain the reference premises documented |
| 20 | +in ADMISSION_DESIGN.md; their pointer implementations are not extracted. |
| 21 | +-/ |
| 22 | +open Aeneas Aeneas.Std AeneasSpecs |
| 23 | +namespace Zerocopy.Proofs |
| 24 | + |
| 25 | +private theorem decode_write (out : Bool × Slice U8) : |
| 26 | + (inferInstance : RustModel (Bool × Slice U8)).decode out = |
| 27 | + some (out.1, byteSlice out.2) := by |
| 28 | + have decoded := decode_byte_slice out.2 |
| 29 | + simp only [RustModel.decode] at decoded ⊢ |
| 30 | + rw [decoded] |
| 31 | + |
| 32 | +theorem write_exact_raw (src dst : Slice U8) : |
| 33 | + util.bytewrite.exact src dst ⦃ out => |
| 34 | + out.1 = decide (src.val.length = dst.val.length) ∧ |
| 35 | + out.2.val = if src.val.length = dst.val.length then src.val else dst.val ⦄ := by |
| 36 | + by_cases same : src.val.length = dst.val.length |
| 37 | + · simp [util.bytewrite.exact, Slice.len, |
| 38 | + util.copy_unchecked, same, bind_ok] |
| 39 | + · simp [util.bytewrite.exact, Slice.len, same, Ne.symm same] |
| 40 | + |
| 41 | +theorem write_prefix_raw (src dst : Slice U8) : |
| 42 | + util.bytewrite.prefix src dst ⦃ out => |
| 43 | + out.1 = decide (src.val.length ≤ dst.val.length) ∧ |
| 44 | + out.2.val = if src.val.length ≤ dst.val.length |
| 45 | + then src.val ++ dst.val.drop src.val.length else dst.val ⦄ := by |
| 46 | + by_cases fits : src.val.length ≤ dst.val.length |
| 47 | + · simp [util.bytewrite.prefix, Slice.len, UScalar.le_equiv, |
| 48 | + util.copy_unchecked, fits, bind_ok] |
| 49 | + · simp [util.bytewrite.prefix, Slice.len, UScalar.le_equiv, fits] |
| 50 | + |
| 51 | +theorem write_suffix_raw (src dst : Slice U8) : |
| 52 | + util.bytewrite.suffix src dst ⦃ out => |
| 53 | + out.1 = decide (src.val.length ≤ dst.val.length) ∧ |
| 54 | + out.2.val = if src.val.length ≤ dst.val.length |
| 55 | + then dst.val.take (dst.val.length - src.val.length) ++ src.val else dst.val ⦄ := by |
| 56 | + unfold util.bytewrite.suffix |
| 57 | + step as ⟨start, facts⟩ |
| 58 | + cases start with |
| 59 | + | none => |
| 60 | + have unfit : ¬ src.val.length ≤ dst.val.length := by simpa [Slice.len] using facts |
| 61 | + simp [unfit] |
| 62 | + | some start => |
| 63 | + have bounds : start.val = dst.val.length - src.val.length ∧ |
| 64 | + src.val.length ≤ dst.val.length := by |
| 65 | + simp only [Slice.len] at facts |
| 66 | + exact ⟨facts.2.1, facts.1⟩ |
| 67 | + have offset : start.val ≤ dst.val.length := by omega |
| 68 | + have fits : src.val.length ≤ dst.val.length - start.val := by omega |
| 69 | + have finish : start.val + src.val.length = dst.val.length := by omega |
| 70 | + simp only [util.copy_unchecked_at, if_pos offset, dif_pos fits, bind_ok, WP.spec_ok] |
| 71 | + simp [bounds.1, bounds.2] |
| 72 | + |
| 73 | +-- The concrete consumer uses the public byteorder operations, then the same |
| 74 | +-- prefix writer as IntoBytes. Reuse the proved encoder rather than recalculate |
| 75 | +-- its bitvector arithmetic inside this caller proof. |
| 76 | +theorem write_be_word_prefix_raw (n : U16) (dst : Slice U8) : |
| 77 | + util.bytewrite.be_word_prefix n dst ⦃ out => |
| 78 | + out.1 = decide (2 ≤ dst.val.length) ∧ |
| 79 | + out.2.val.map U8.bv = if 2 ≤ dst.val.length |
| 80 | + then Rust.Bytes.encodeBE 2 n.val ++ (dst.val.drop 2).map U8.bv |
| 81 | + else dst.val.map U8.bv ⦄ := by |
| 82 | + have encoding_eq : (do |
| 83 | + let value ← byteorder.U16.new byteorder.BigEndian.Insts.ZerocopyByteorderByteOrder n |
| 84 | + byteorder.U16.to_bytes value) = byteorder.verification.write_u16_be n := by |
| 85 | + simp only [byteorder.verification.write_u16_be, core.convert.IntoFrom.into, |
| 86 | + ArrayU82.Insts.CoreConvertFromU16.from, |
| 87 | + byteorder.U16.to_bytes] |
| 88 | + unfold util.bytewrite.be_word_prefix |
| 89 | + rw [← Aeneas.Std.bind_assoc, encoding_eq] |
| 90 | + step with Raw.write_u16_be n as ⟨bytes, encoded⟩ |
| 91 | + simp only [lift, bind_ok] |
| 92 | + apply WP.spec_mono (write_prefix_raw (Array.to_slice bytes) dst) |
| 93 | + intro out facts |
| 94 | + have length : bytes.val.length = 2 := by simp |
| 95 | + simp only [Array.to_slice, Slice.from_val, length] at facts |
| 96 | + refine ⟨facts.1, ?_⟩ |
| 97 | + by_cases fits : 2 ≤ dst.val.length |
| 98 | + · simp only [if_pos fits] at facts ⊢ |
| 99 | + rw [facts.2, List.map_append, encoded] |
| 100 | + · simp only [if_neg fits] at facts ⊢ |
| 101 | + rw [facts.2] |
| 102 | + |
| 103 | +theorem write_exact_spec : Specs.write_exact_spec := by |
| 104 | + intro src dst _ _ _ _ |
| 105 | + apply WP.spec_mono (write_exact_raw src dst) |
| 106 | + intro out facts |
| 107 | + exact ⟨(out.1, byteSlice out.2), decode_write out, facts⟩ |
| 108 | + |
| 109 | +theorem write_prefix_spec : Specs.write_prefix_spec := by |
| 110 | + intro src dst _ _ _ _ |
| 111 | + apply WP.spec_mono (write_prefix_raw src dst) |
| 112 | + intro out facts |
| 113 | + exact ⟨(out.1, byteSlice out.2), decode_write out, facts⟩ |
| 114 | + |
| 115 | +theorem write_suffix_spec : Specs.write_suffix_spec := by |
| 116 | + intro src dst _ _ _ _ |
| 117 | + apply WP.spec_mono (write_suffix_raw src dst) |
| 118 | + intro out facts |
| 119 | + exact ⟨(out.1, byteSlice out.2), decode_write out, facts⟩ |
| 120 | + |
| 121 | +theorem write_be_word_prefix_spec : Specs.write_be_word_prefix_spec := by |
| 122 | + intro n dst _ _ _ _ |
| 123 | + apply WP.spec_mono (write_be_word_prefix_raw n dst) |
| 124 | + intro out facts |
| 125 | + exact ⟨(out.1, byteSlice out.2), decode_write out, facts⟩ |
| 126 | + |
| 127 | +end Zerocopy.Proofs |
0 commit comments