@@ -19,6 +19,8 @@ module Verisimiser.ABI.Layout
1919import Verisimiser.ABI.Types
2020import Data.Vect
2121import Data.So
22+ import Data.Nat
23+ import Decidable.Equality
2224
2325%default total
2426
@@ -32,24 +34,40 @@ paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
3234paddingFor offset alignment =
3335 if offset `mod` alignment == 0
3436 then 0
35- else alignment - (offset `mod` alignment)
37+ else minus alignment (offset `mod` alignment)
3638
37- ||| Proof that alignment divides aligned size.
39+ ||| Proof that alignment divides aligned size: `m = k * n` .
3840public export
3941data Divides : Nat -> Nat -> Type where
4042 DivideBy : (k : Nat ) -> {n : Nat } -> {m : Nat } -> (m = k * n) -> Divides n m
4143
44+ ||| Sound decision procedure for divisibility. Returns a genuine
45+ ||| `Divides n m` witness when `n` evenly divides `m`, otherwise Nothing.
46+ ||| Division by zero is undecidable here and yields Nothing.
47+ public export
48+ decDivides : (n : Nat ) -> (m : Nat ) -> Maybe (Divides n m)
49+ decDivides Z _ = Nothing
50+ decDivides (S k) m =
51+ let q = m `div` (S k) in
52+ case decEq m (q * (S k)) of
53+ Yes prf => Just (DivideBy q prf)
54+ No _ => Nothing
55+
4256||| Round up to next alignment boundary.
4357public export
4458alignUp : (size : Nat ) -> (alignment : Nat ) -> Nat
4559alignUp size alignment =
4660 size + paddingFor size alignment
4761
48- ||| Proof that alignUp produces aligned result.
62+ ||| Sound divisibility check for an aligned size. The general theorem
63+ ||| "alignUp size align is always divisible by align" needs div/mod lemmas
64+ ||| and is tracked as residual proof work; here we *decide* it via
65+ ||| `decDivides`, which returns a genuine witness when it holds. For the
66+ ||| concrete ABI layouts below, divisibility is proven outright (`DivideBy`).
4967public export
50- alignUpCorrect : (size : Nat ) -> (align : Nat ) -> (align > 0) -> Divides align (alignUp size align)
51- alignUpCorrect size align prf =
52- DivideBy (( size + paddingFor size align) `div` align) Refl
68+ alignUpDivides : (size : Nat ) -> (align : Nat ) ->
69+ Maybe (Divides align (alignUp size align))
70+ alignUpDivides size align = decDivides align (alignUp size align)
5371
5472-- ------------------------------------------------------------------------------
5573-- Struct Field Layout
@@ -81,7 +99,7 @@ record StructLayout where
8199
82100||| Calculate total struct size with padding.
83101public export
84- calcStructSize : Vect n Field -> Nat -> Nat
102+ calcStructSize : Vect k Field -> Nat -> Nat
85103calcStructSize [] align = 0
86104calcStructSize (f :: fs) align =
87105 let lastOffset = foldl (\ acc, field => nextFieldOffset field) f. offset fs
@@ -90,23 +108,26 @@ calcStructSize (f :: fs) align =
90108
91109||| Proof that field offsets are correctly aligned.
92110public export
93- data FieldsAligned : Vect n Field -> Type where
111+ data FieldsAligned : Vect k Field -> Type where
94112 NoFields : FieldsAligned []
95113 ConsField :
96114 (f : Field) ->
97- (rest : Vect n Field) ->
115+ (rest : Vect k Field) ->
98116 Divides f.alignment f.offset ->
99117 FieldsAligned rest ->
100118 FieldsAligned (f :: rest)
101119
102- ||| Verify a struct layout is valid.
120+ ||| Decide field alignment for every field, building a real `FieldsAligned`
121+ ||| witness from per-field divisibility proofs.
103122public export
104- verifyLayout : (fields : Vect n Field) -> (align : Nat ) -> Either String StructLayout
105- verifyLayout fields align =
106- let size = calcStructSize fields align
107- in case decSo (size >= sum (map (\ f => f. size) fields)) of
108- Yes prf => Right (MkStructLayout fields size align)
109- No _ => Left " Invalid struct size"
123+ decFieldsAligned : (fs : Vect k Field) -> Maybe (FieldsAligned fs)
124+ decFieldsAligned [] = Just NoFields
125+ decFieldsAligned (f :: fs) =
126+ case decDivides f. alignment f. offset of
127+ Nothing => Nothing
128+ Just dvd => case decFieldsAligned fs of
129+ Nothing => Nothing
130+ Just rest => Just (ConsField f fs dvd rest)
110131
111132-- ------------------------------------------------------------------------------
112133-- Platform-Specific Layouts
@@ -137,11 +158,15 @@ data CABICompliant : StructLayout -> Type where
137158 FieldsAligned layout.fields ->
138159 CABICompliant layout
139160
140- ||| Check if layout follows C ABI.
161+ ||| Verify a layout against the C ABI alignment rules, returning a genuine
162+ ||| `CABICompliant` proof (built from real per-field divisibility witnesses)
163+ ||| or an error when some field offset is misaligned.
141164public export
142165checkCABI : (layout : StructLayout) -> Either String (CABICompliant layout)
143166checkCABI layout =
144- Right (CABIOk layout ? fieldsAlignedProof)
167+ case decFieldsAligned layout. fields of
168+ Just prf => Right (CABIOk layout prf)
169+ Nothing => Left " Field offsets are not correctly aligned for the C ABI"
145170
146171-- ------------------------------------------------------------------------------
147172-- OctadRecord Layout
@@ -183,11 +208,26 @@ octadRecordLayout =
183208 ]
184209 80 -- Total size: 80 bytes
185210 8 -- Alignment: 8 bytes
211+ {sizeCorrect = Oh }
212+ {aligned = DivideBy 10 Refl }
186213
187214||| Proof that the OctadRecord layout is C-ABI compliant.
188215export
189- octadRecordValid : CABICompliant octadRecordLayout
190- octadRecordValid = CABIOk octadRecordLayout ? octadFieldsAligned
216+ octadRecordValid : CABICompliant Layout.octadRecordLayout
217+ octadRecordValid =
218+ CABIOk octadRecordLayout
219+ (ConsField _ _ (DivideBy 0 Refl ) -- entity_id 0 / 8
220+ (ConsField _ _ (DivideBy 2 Refl ) -- backend 8 / 4
221+ (ConsField _ _ (DivideBy 3 Refl ) -- active_dims 12 / 4
222+ (ConsField _ _ (DivideBy 2 Refl ) -- provenance 16 / 8
223+ (ConsField _ _ (DivideBy 3 Refl ) -- temporal 24 / 8
224+ (ConsField _ _ (DivideBy 4 Refl ) -- drift_score 32 / 8
225+ (ConsField _ _ (DivideBy 5 Refl ) -- lineage 40 / 8
226+ (ConsField _ _ (DivideBy 6 Refl ) -- constraints 48 / 8
227+ (ConsField _ _ (DivideBy 7 Refl ) -- acl 56 / 8
228+ (ConsField _ _ (DivideBy 8 Refl ) -- simulation 64 / 8
229+ (ConsField _ _ (DivideBy 9 Refl ) -- metadata 72 / 8
230+ NoFields )))))))))))
191231
192232-- ------------------------------------------------------------------------------
193233-- ProvenanceEntry Layout
@@ -218,11 +258,21 @@ provenanceEntryLayout =
218258 ]
219259 88 -- Total size: 88 bytes
220260 8 -- Alignment: 8 bytes
261+ {sizeCorrect = Oh }
262+ {aligned = DivideBy 11 Refl }
221263
222264||| Proof that the ProvenanceEntry layout is C-ABI compliant.
223265export
224- provenanceEntryValid : CABICompliant provenanceEntryLayout
225- provenanceEntryValid = CABIOk provenanceEntryLayout ? provenanceFieldsAligned
266+ provenanceEntryValid : CABICompliant Layout.provenanceEntryLayout
267+ provenanceEntryValid =
268+ CABIOk provenanceEntryLayout
269+ (ConsField _ _ (DivideBy 0 Refl ) -- hash 0 / 1
270+ (ConsField _ _ (DivideBy 32 Refl ) -- previous_hash 32 / 1
271+ (ConsField _ _ (DivideBy 8 Refl ) -- entity_id 64 / 8
272+ (ConsField _ _ (DivideBy 18 Refl ) -- operation 72 / 4
273+ (ConsField _ _ (DivideBy 19 Refl ) -- _padding 76 / 4
274+ (ConsField _ _ (DivideBy 10 Refl ) -- timestamp 80 / 8
275+ NoFields ))))))
226276
227277-- ------------------------------------------------------------------------------
228278-- DriftMeasurement Layout
@@ -248,11 +298,19 @@ driftMeasurementLayout =
248298 ]
249299 88 -- Total size: 88 bytes
250300 8 -- Alignment: 8 bytes
301+ {sizeCorrect = Oh }
302+ {aligned = DivideBy 11 Refl }
251303
252304||| Proof that the DriftMeasurement layout is C-ABI compliant.
253305export
254- driftMeasurementValid : CABICompliant driftMeasurementLayout
255- driftMeasurementValid = CABIOk driftMeasurementLayout ? driftFieldsAligned
306+ driftMeasurementValid : CABICompliant Layout.driftMeasurementLayout
307+ driftMeasurementValid =
308+ CABIOk driftMeasurementLayout
309+ (ConsField _ _ (DivideBy 0 Refl ) -- entity_id 0 / 8
310+ (ConsField _ _ (DivideBy 1 Refl ) -- overall_score 8 / 8
311+ (ConsField _ _ (DivideBy 2 Refl ) -- scores 16 / 8
312+ (ConsField _ _ (DivideBy 10 Refl ) -- measured_at 80 / 8
313+ NoFields ))))
256314
257315-- ------------------------------------------------------------------------------
258316-- TemporalSnapshot Layout
@@ -284,25 +342,43 @@ temporalSnapshotLayout =
284342 ]
285343 48 -- Total size: 48 bytes
286344 8 -- Alignment: 8 bytes
345+ {sizeCorrect = Oh }
346+ {aligned = DivideBy 6 Refl }
287347
288348||| Proof that the TemporalSnapshot layout is C-ABI compliant.
289349export
290- temporalSnapshotValid : CABICompliant temporalSnapshotLayout
291- temporalSnapshotValid = CABIOk temporalSnapshotLayout ? temporalFieldsAligned
350+ temporalSnapshotValid : CABICompliant Layout.temporalSnapshotLayout
351+ temporalSnapshotValid =
352+ CABIOk temporalSnapshotLayout
353+ (ConsField _ _ (DivideBy 0 Refl ) -- entity_id 0 / 8
354+ (ConsField _ _ (DivideBy 1 Refl ) -- version 8 / 8
355+ (ConsField _ _ (DivideBy 2 Refl ) -- valid_from 16 / 8
356+ (ConsField _ _ (DivideBy 3 Refl ) -- valid_to 24 / 8
357+ (ConsField _ _ (DivideBy 4 Refl ) -- snapshot_ptr 32 / 8
358+ (ConsField _ _ (DivideBy 10 Refl ) -- snapshot_len 40 / 4
359+ (ConsField _ _ (DivideBy 11 Refl ) -- operation 44 / 4
360+ NoFields )))))))
292361
293362-- ------------------------------------------------------------------------------
294363-- Offset Calculation
295364-- ------------------------------------------------------------------------------
296365
297366||| Calculate field offset with proof of correctness.
298367public export
299- fieldOffset : (layout : StructLayout) -> (fieldName : String) -> Maybe (n : Nat ** Field)
368+ fieldOffset : (layout : StructLayout) -> (fieldName : String) -> Maybe (Nat , Field)
300369fieldOffset layout name =
301370 case findIndex (\ f => f. name == name) layout. fields of
302- Just idx => Just (finToNat idx ** index idx layout. fields)
371+ Just idx => Just (finToNat idx, index idx layout. fields)
303372 Nothing => Nothing
304373
305- ||| Proof that field offset is within struct bounds.
374+ ||| Decide whether a field lies within a struct's byte bounds, returning a
375+ ||| genuine proof when `offset + size <= totalSize`. The previous signature
376+ ||| asserted this for *every* field unconditionally, which is false (a field
377+ ||| need not belong to the layout); this honest version decides it.
306378public export
307- offsetInBounds : (layout : StructLayout) -> (f : Field) -> So (f.offset + f.size <= layout.totalSize)
308- offsetInBounds layout f = ? offsetInBoundsProof
379+ offsetInBounds : (layout : StructLayout) -> (f : Field) ->
380+ Maybe (So (f.offset + f.size <= layout.totalSize))
381+ offsetInBounds layout f =
382+ case choose (f. offset + f. size <= layout. totalSize) of
383+ Left ok => Just ok
384+ Right _ => Nothing
0 commit comments