1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! FJ-038: Kani bounded-model proofs for the `nas_archive` resource.
//!
//! Gated behind `#[cfg(kani)]`; normal builds ignore them.
//! Run with: `cargo kani --harness proof_archive_dest_never_inside_source`.
//!
//! These prove the containment and admission algebra over the whole input space
//! a unit test can only sample. The operationally decisive property — does a
//! mismatched destination actually leave the source alive? — is proved by
//! EXECUTION instead, in `src/resources/nas_archive/tests.rs`, because a bounded
//! model cannot say anything useful about what `rsync` and the filesystem do.
//!
//! Both harnesses target allocation-free predicates on purpose.
//! `kani_proofs_backup_sync` records the measurement: driving the constructor
//! instead ran one CBMC process for 117 minutes at 6.5 GB on an idle box, not
//! because the input space was large (216 cases) but because CBMC had to model
//! the allocator and `core::fmt` across it. `classify_declaration` and
//! `contains_path` are those same decisions with the message rendering lifted
//! out, so the model contains no allocation at all.
// ── NAS Archive Proofs (FJ-038 / nas-archive-v1) ──────────────────────
/// Contract `destination_cannot_be_the_source`: containment is component-wise
/// and symmetric-safe.
///
/// Two properties at once, because they constrain each other:
///
/// * Containment must be *reflexive* — a destination equal to the source is
/// the move-onto-itself shape and must be caught.
/// * Containment must NOT be a raw string prefix — `/mnt/unas-old` beside
/// `/mnt/unas` is a valid declaration, and refusing it trains operators to
/// work around the type.
///
/// Proved over every short path built from an alphabet containing the separator,
/// so the boundary between "next byte is `/`" and "next byte is anything else"
/// is exercised exhaustively rather than sampled.
///
/// STATED OVER NORMALISED PATHS, and that is the whole content of this
/// harness's history. It first said, of the RAW arguments,
///
/// assert!(inner.len() > outer.len());
/// assert_eq!(inner.as_bytes()[outer.len()], b'/');
///
/// and `cargo kani` refused it: `1 of 557 failed — assertion failed:
/// inner.len() > outer.len()`. The refusal was correct and the function was
/// not at fault. `contains_path` decides on `norm_path(..)` of each argument,
/// so arithmetic on the raw lengths describes a different pair of strings than
/// the one the decision was made about. `outer = "///"` normalises to the root
/// and is 3 bytes long while meaning 1; every one of the eight `inner` values
/// that is not `outer` itself falsifies the raw form.
///
/// The root is also why the boundary index is not simply `o.len()`: `/` is the
/// one path that already ENDS in its own separator, so what follows it starts
/// at index 0, not at index 1. `boundary` below is that distinction and
/// nothing else. Enumerated over a wider space than Kani's (alphabet `/abc`,
/// paths to length 4, 85 paths, 7225 ordered pairs): 0 counterexamples to the
/// form below, and 207 to the same form if `contains_path` is mutated into the
/// raw string-prefix test this proof exists to exclude — so restating it did
/// not cost the property its teeth.
/// Contract `destination_cannot_be_the_source`: an accepted declaration can
/// never be a move onto itself, and can never name a path where a directory
/// name is required.
///
/// This is the safety property the type exists for: whatever the operator
/// writes, an accepted `NasArchive` cannot delete a source into itself, and
/// cannot reach outside the source root.
/// Contract `cifs_hostile_trees_are_refused`: the small-byte budget admits a
/// directory exactly when its small-file bytes fit, with no overflow.
///
/// The comparison runs in POSIX shell integer arithmetic, and the reported
/// figure is divided down to MB for the operator. This pins that the admission
/// decision is a plain total order — a directory at exactly the budget is
/// admitted, one byte over is refused — and that the MB rendering never claims
/// a refused tree was smaller than the budget.