Skip to content

Commit fdd17d6

Browse files
authored
Prove correctness of frame delimiter encoding. (#7)
This strengthens the postcondition of the Encode procedure to prove that the encoder only outputs a frame delimiter (zero byte) for the last byte, and that all preceding bytes have a non-zero value.
1 parent 8c8363c commit fdd17d6

File tree

3 files changed

+45
-7
lines changed

3 files changed

+45
-7
lines changed

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,12 @@ end Example;
6767
This project takes a "hybrid verification" approach combining formal
6868
verification and testing.
6969

70-
GNATprove is used to prove absence of run-time errors. At the time of writing
71-
there are 139 checks that are automatically proved.
70+
GNATprove is used to prove absence of run-time errors and some limited
71+
functional properties:
72+
* the encoder only emits a frame delimiter (zero byte) at the end
73+
of the encoded frame.
74+
75+
At the time of writing there are 163 checks that are automatically proved.
7276

7377
The unit tests are used to check that the encoder and decoder produce the
7478
correct results for a variety of inputs, with 100% MC/DC source coverage.

src/generic_cobs.adb

+23-1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ is
105105
Output'First + Index'Base (Length - 1) =>
106106
Output (I)'Initialized);
107107

108+
pragma Loop_Invariant
109+
(for all I in Output'First ..
110+
Output'First + Index'Base (Length - 1) =>
111+
Output (I) /= Frame_Delimiter);
112+
108113
Encode_Block
109114
(Input (Input'First + Index'Base (Offset) .. Input'Last),
110115
Output (Output'First + Index'Base (Length) .. Output'Last),
@@ -117,6 +122,11 @@ is
117122
Remaining := Remaining - (Block_Length - 1);
118123
end loop;
119124

125+
pragma Assert
126+
(for all I in Output'First ..
127+
Output'First + Index'Base (Length - 1) =>
128+
Output (I) /= Frame_Delimiter);
129+
120130
Output (Output'First + Index'Base (Length)) := Frame_Delimiter;
121131

122132
Length := Length + 1;
@@ -148,7 +158,11 @@ is
148158

149159
if Input'Length > 0 then
150160
for I in Byte_Count range 0 .. Input'Length - 1 loop
151-
pragma Loop_Invariant (Code <= Maximum_Run_Length + 1);
161+
pragma Warnings
162+
(Off, """Output"" may be referenced before it has a value",
163+
Reason => "Initialization of Output is guaranteed via proof");
164+
165+
pragma Loop_Invariant (Code in 1 .. Maximum_Run_Length + 1);
152166

153167
pragma Loop_Invariant
154168
(Code = Length - Byte_Count (Code_Pos - Output'First));
@@ -166,6 +180,14 @@ is
166180
Output'First + Index'Base (Length) - 1 =>
167181
(if I /= Code_Pos then Output (I)'Initialized));
168182

183+
-- The frame delimiter is never written to the output.
184+
pragma Loop_Invariant
185+
(for all I in Output'First ..
186+
Output'First + Index'Base (Length) - 1 =>
187+
(if I /= Code_Pos then Output (I) /= Frame_Delimiter));
188+
189+
pragma Warnings (On);
190+
169191
-- Stop when a complete block is reached.
170192
exit when Code = Maximum_Run_Length + 1;
171193

src/generic_cobs.ads

+16-4
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,16 @@ is
117117
-- Only the first 'Length' bytes of the Output are initialized.
118118
and then
119119
Output (Output'First ..
120-
Output'First + Index'Base (Length - 1))'Initialized),
120+
Output'First + Index'Base (Length - 1))'Initialized
121+
122+
-- The last byte in the output is a frame delimiter.
123+
-- All other bytes before the frame delimiter are non-zero.
124+
and then
125+
(for all I in Output'First ..
126+
Output'First + Index'Base (Length - 1) =>
127+
(if I < Output'First + Index'Base (Length - 1)
128+
then Output (I) /= Frame_Delimiter
129+
else Output (I) = Frame_Delimiter))),
121130
Annotate => (GNATProve, Terminating);
122131
-- Encode a byte array.
123132
--
@@ -129,8 +138,7 @@ is
129138
-- @param Input The bytes to encode.
130139
--
131140
-- @param Output The COBS-encoded data is written to the first "Length"
132-
-- bytes of this array. Any unused bytes at the end of the
133-
-- array are initialised to the frame delimiter value (zero).
141+
-- bytes of this array. The last byte is a frame delimiter.
134142
--
135143
-- @param Length The length of the encoded frame is written here.
136144

@@ -201,7 +209,11 @@ private
201209
then Length >= Maximum_Run_Length + 1)
202210
and then
203211
Output (Output'First ..
204-
Output'First + Index'Base (Length - 1))'Initialized),
212+
Output'First + Index'Base (Length - 1))'Initialized
213+
and then
214+
(for all I in Output'First ..
215+
Output'First + Index'Base (Length - 1) =>
216+
Output (I) /= Frame_Delimiter)),
205217
Annotate => (GNATProve, Terminating);
206218
-- Encodes a single block of bytes.
207219
--

0 commit comments

Comments
 (0)