Skip to content
REFERENCE / ZLIB × LEAN

gzlib.c.seek.lean

Source snapshot · 24f87a0732715551 · Source at the matching commit ↗

Lean source with line numbers. Follow a line number to locate a statement, or an import to read its dependency.

1import ZlibF.Proof.«gzlib.c.rewind»
2
3/-!
4# gzlib.c: `gzseek64`, `gzseek`
5
6`t` is the target position: `0` for `SEEK_SET`, `gztell64` for `SEEK_CUR`, plus `offset`. The function refuses
7(`-1`, nothing changes) when `t` is not a `z_off64_t` that is not negative; otherwise it returns `t`.
8
9The unpatched C faults in two places that the earlier repository patched (R-1, `docs/design/external-io.md`):
10`SEEK_CUR` while `gztell64 < 0` (the check `offset > GZ_OFF_MAX - pos` overflows), and `offset = pos - x.pos` while
11`x.pos < 0` (after `gzungetc` at the start). The theorems below that say what the function returns carry the
12hypothesis `0 ≤ x.pos`, which excludes both; the `*_ub` theorems show the fault in the excluded states.
13-/
14
15namespace Zlib.Gzlib.Spec
16open C Zlib.GzFs Zlib.Gzguts
17
18/-- `GZ_OFF_MAX` as a number. -/
19def OffMax : Int := 9223372036854775807
20
21/-- The position `gzseek64` starts from: 0 for `SEEK_SET`, the current position for `SEEK_CUR`. -/
22def seekBase (s : GzState) (whence : Int32) : Int := if whence = SEEK_SET then 0 else tellPos s
23
24/-- The position `gzseek64` goes to. -/
25def seekT (s : GzState) (offset : Int64) (whence : Int32) : Int := seekBase s whence + offset.toInt
26
27/-- A position `gzseek64` accepts. -/
28def SeekOk (t : Int) : Prop := 0 ≤ t ∧ t ≤ OffMax
29
30instance (t : Int) : Decidable (SeekOk t) := by unfold SeekOk; infer_instance
31
32theorem OffMax_val : OffMax = 9223372036854775807 := rfl
33
34
35/-- The fields a seek does not change. -/
36def SameCfg (s s' : GzState) : Prop :=
37  s'.mode = s.mode ∧ s'.fd = s.fd ∧ s'.path = s.path ∧ s'.size = s.size ∧ s'.want = s.want ∧ s'.in = s.in ∧
38  s'.out = s.out ∧ s'.direct = s.direct ∧ s'.start = s.start ∧ s'.level = s.level ∧ s'.strategy = s.strategy
39
40/-- What every outcome of `gzseek64` has in common: the handle still satisfies the invariants, and either nothing changed
41(result `-1`) or the result is the target `t` and the position fields say so: `gztell64` is `t`, except for a reader that
42has read past the end (`past`), whose `gztell64` is `x.pos` (the position of the end; the returned `t` is not what
43`gztell64` then reports, see the migration note). -/
44def SeekPost (h : Heap) (gs : Nat) (s : GzState) (t : Int) (r : Int64) (h' : Heap) (s' : GzState) : Prop :=
45  GzGood h' gs s' ∧ PosInv s' ∧ NextOk h' s' ∧ SameCfg s s' ∧
46  ((r = -1 ∧ h' = h ∧ s' = s) ∨
47   (SeekOk t ∧ r.toInt = t ∧ (s'.past = 0 → tellPos s' = t) ∧
48     (s'.past ≠ 0 → tellPos s' = s'.x.pos.toInt ∧ s'.x.pos.toInt ≤ t)))
49
50/-- The record after a seek inside the raw area (`how == COPY`): nothing buffered, the position is `t`, no pending skip,
51no error and no message, the stream has no input. -/
52def copyRec (s : GzState) (t : Int) : GzState :=
53  { s with x := { s.x with «have» := 0, pos := Int64.ofInt t }, eof := 0, past := 0, skip := 0, msg := .null, err := Z_OK,
54           strm := { s.strm with avail_in := 0 } }
55
56/-- How many buffered bytes a forward seek skips by advancing `x.next`: all of the distance if the buffer is longer than
57the distance, else the whole buffer. -/
58def fwdN (s : GzState) (off : Int) : Nat := if off < s.x.«have».toNat then off.toNat else s.x.«have».toNat
59
60/-- The record after a forward seek of a reader (target `t`, not before `x.pos`): the buffered bytes before the target
61are consumed, the rest of the distance becomes `skip`. -/
62def skipFwd (s : GzState) (t : Int) : GzState :=
63  let off := t - s.x.pos.toInt
64  let n := fwdN s off
65  { s with x := { «have» := s.x.«have» - UInt32.ofNat n, next := padv s.x.next n, pos := Int64.ofInt (s.x.pos.toInt + n) },
66           skip := Int64.ofInt (off - n) }
67
68/-- The code of `gzseek64` after `pos += offset` (`s` is the record read at the start). Written as in
69`gzlib.c.lean`; `gzseek64_head` shows that `gzseek64` is the checks followed by this. -/
70def seekTail (file : GzFilePtr) (s : GzState) (pos : Int64) : G Int64 := do
71  let state := castState file
72  -- if (state->mode == GZ_READ && state->how == COPY) { ... }
73  if s.mode == GZ_READ && s.how == GZ_COPY then
74    let d ← i64sub (← i64sub pos s.x.pos) (u32ToI64 s.x.«have»)
75    let ret ← fsLseek s.fd d SEEK_CUR
76    if ret == -1 then return -1
77    updX state fun x => { x with «have» := 0 }
78    updS state fun s => { s with eof := 0, past := 0, skip := 0 }
79    gz_error state Z_OK .null
80    updZ (strmOf state) fun z => { z with avail_in := 0 }
81    updX state fun x => { x with pos := pos }
82    return pos
83  -- if (pos < state->x.pos) { if (state->mode != GZ_READ) return -1; if (gzrewind(file) == -1) return -1; }
84  let s ← ldS state
85  if pos < s.x.pos then
86    if s.mode != GZ_READ then return -1
87    if (← gzrewind file) == -1 then return -1
88  -- offset = pos - state->x.pos;
89  let s ← ldS state
90  let mut offset ← i64sub pos s.x.pos
91  -- if (state->mode == GZ_READ) { ... }
92  if s.mode == GZ_READ then
93    let s ← ldS state
94    let n : UInt32 ← if (← GT_OFF s.x.«have») || u32ToI64 s.x.«have» > offset then pure offset.toUInt64.toUInt32
95      else pure s.x.«have»
96    updX state fun x => { x with «have» := x.«have» - n }
97    let s ← ldS state
98    let nx ← s.x.next.add n.toNat
99    updX state fun x => { x with next := nx }
100    let s ← ldS state
101    let np ← i64add s.x.pos (u32ToI64 n)
102    updX state fun x => { x with pos := np }
103    offset ← i64sub offset (u32ToI64 n)
104  -- state->skip = offset; return pos;
105  updS state fun s => { s with skip := offset }
106  return pos
107
108end Zlib.Gzlib.Spec
109
110namespace Zlib.Gzlib.Proofs
111open C Zlib.GzFs Zlib.Gzguts Zlib.Gzlib Zlib.Gzlib.Spec
112
113theorem GZ_OFF_MAX_toInt : GZ_OFF_MAX.toInt = OffMax := by
114  unfold GZ_OFF_MAX OffMax
115  exact i64_toInt_ofInt (by omega) (by omega)
116
117theorem int64_toInt_ge (x : Int64) : -9223372036854775808 ≤ x.toInt := x.le_toInt
118theorem int64_toInt_lt (x : Int64) : x.toInt < 9223372036854775808 := x.toInt_lt
119
120theorem i64_gt_iff (a b : Int64) : a > b ↔ b.toInt < a.toInt := by
121  show b < a ↔ _
122  exact Int64.lt_iff_toInt_lt
123
124theorem i64_lt_iff (a b : Int64) : a < b ↔ a.toInt < b.toInt := Int64.lt_iff_toInt_lt
125
126/-- The addition at the start of `gzseek64` for `SEEK_CUR`. -/
127theorem tell_add_run {s : GzState} (hP : PosInv s) (h : Heap) :
128    i64add s.x.pos (if s.past != 0 then 0 else s.skip) h = .ok (Int64.ofInt (tellPos s), h) := by
129  have e := i64add_run s.x.pos (tellSkip s) (by have := hP.lo; unfold tellPos at this; omega)
130    (by have := hP.hi; unfold tellPos at this; omega) h
131  simpa [tellSkip, tellPos] using e
132
133
134theorem tellPos_nonneg {s : GzState} (hP : PosInv s) (hnn : 0 ≤ s.x.pos.toInt) : 0 ≤ tellPos s := by
135  have := hP.skip_nonneg
136  unfold tellPos tellSkip
137  by_cases hp : s.past != 0
138  · simp [hp]; omega
139  · simp [hp]; omega
140
141theorem seekBase_nonneg {s : GzState} (hP : PosInv s) (hnn : 0 ≤ s.x.pos.toInt) (whence : Int32) :
142    0 ≤ seekBase s whence := by
143  unfold seekBase; split
144  · exact Int.le_refl 0
145  · exact tellPos_nonneg hP hnn
146
147theorem seekBase_le {s : GzState} (hP : PosInv s) (whence : Int32) : seekBase s whence ≤ OffMax := by
148  have := hP.hi
149  have hOM := OffMax_val
150  unfold seekBase; split
151  · omega
152  · omega
153
154/-- The three checks of `gzseek64` (`offset > GZ_OFF_MAX - pos`, `offset < -pos`, `pos += offset`) for `pos = p0`
155with `0 ≤ p0`: what they evaluate to. -/
156theorem seek_checks {p0 : Int64} (offset : Int64) (hp0 : 0 ≤ p0.toInt) (h : Heap) :
157    ∃ q1 q3 : Int64, i64sub GZ_OFF_MAX p0 h = .ok (q1, h) ∧ i64neg p0 h = .ok (q3, h) ∧
158      (q1 < offset ↔ OffMax < p0.toInt + offset.toInt) ∧ (offset < q3 ↔ p0.toInt + offset.toInt < 0) ∧
159      ((¬ OffMax < p0.toInt + offset.toInt) → ¬ (p0.toInt + offset.toInt < 0) →
160        ∃ q5 : Int64, i64add p0 offset h = .ok (q5, h) ∧ q5 = Int64.ofInt (p0.toInt + offset.toInt) ∧
161          q5.toInt = p0.toInt + offset.toInt) := by
162  have hOM := OffMax_val
163  have hg := GZ_OFF_MAX_toInt
164  have hlt := p0.toInt_lt
165  have f1 := i64sub_run GZ_OFF_MAX p0 (by omega) (by omega) h
166  have f3 := i64neg_run p0 (by omega) (by omega) h
167  have hq1 : (Int64.ofInt (GZ_OFF_MAX.toInt - p0.toInt)).toInt = OffMax - p0.toInt := by
168    rw [hg]; exact i64_toInt_ofInt (by omega) (by omega)
169  have hq3 : (Int64.ofInt (-p0.toInt)).toInt = -p0.toInt := i64_toInt_ofInt (by omega) (by omega)
170  refine ⟨_, _, f1, f3, ?_, ?_, ?_⟩
171  · rw [i64_lt_iff, hq1]; omega
172  · rw [i64_lt_iff, hq3]; omega
173  · intro h1 h2
174    exact ⟨_, i64add_run p0 offset (by omega) (by omega) h, rfl, i64_toInt_ofInt (by omega) (by omega)⟩
175
176/-- `gzseek64` on a handle with `GzGood` and `PosInv`, `x.pos >= 0`, no serious error and `whence` in {SEEK_SET, SEEK_CUR}: it is the checks followed by `seekTail`. The result is -1 with the heap unchanged when the target is outside `[0, GZ_OFF_MAX]`, otherwise `seekTail` at the target. -/
177theorem gzseek64_head {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
178    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
179    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (offset : Int64) (fs : Fs) :
180    gzseek64 (hnd gs) offset whence fs h =
181      if SeekOk (seekT s offset whence) then seekTail (hnd gs) s (Int64.ofInt (seekT s offset whence)) fs h
182      else Except.ok ((-1, fs), h) := by
183  have hs := hG.hs
184  have hOM := OffMax_val
185  have hb0 := seekBase_nonneg hP hnn whence
186  have hb1 := seekBase_le hP whence
187  have hne : ¬(¬ s.err = Z_OK ∧ ¬ s.err = Z_BUF_ERROR) := by rcases he with h1 | h1 <;> simp [h1]
188  have hmo : ¬ (s.mode ≠ GZ_READ ∧ s.mode ≠ GZ_WRITE) := by rcases hG.mode with h1 | h1 <;> simp [h1]
189  have hSC : SEEK_CUR ≠ SEEK_SET := by decide
190  have eT : i64add s.x.pos (if s.past = 0 then s.skip else 0) h = .ok (Int64.ofInt (tellPos s), h) := by
191    have := tell_add_run hP h
192    by_cases hh : s.past = 0
193    · simpa [hh] using this
194    · simpa [hh] using this
195  obtain ⟨p0, hp0⟩ : ∃ p0 : Int64, p0 = Int64.ofInt (seekBase s whence) := ⟨_, rfl⟩
196  have hp0i : p0.toInt = seekBase s whence := by rw [hp0]; exact i64_toInt_ofInt (by omega) (by omega)
197  obtain ⟨q1, q3, f1, f3, c1, c2, c5⟩ := seek_checks (p0 := p0) offset (by omega) h
198  rw [hp0i] at c1 c2 c5
199  have hbt : seekBase s whence + offset.toInt = seekT s offset whence := rfl
200  rw [hbt] at c1 c2 c5
201  have hpp : p0 = if whence = SEEK_SET then 0 else Int64.ofInt (tellPos s) := by
202    rw [hp0]; unfold seekBase; split
203    · rfl
204    · rfl
205  unfold gzseek64
206  by_cases r1 : OffMax < seekT s offset whence
207  · have hrhs : ¬ SeekOk (seekT s offset whence) := fun hh => by have := hh.2; omega
208    rw [ite_eq_right hrhs]
209    rcases hw with hw | hw <;> subst hw
210    · rw [ite_eq_left rfl] at hpp; subst hpp
211      ggsimp [hs, hmo, hne, f1, f3, c1, c2, r1]
212    · rw [ite_eq_right (by decide)] at hpp; subst hpp
213      ggsimp [hs, hmo, hne, f1, f3, c1, c2, r1, eT, hSC]
214  · by_cases r2 : seekT s offset whence < 0
215    · have hrhs : ¬ SeekOk (seekT s offset whence) := fun hh => by have := hh.1; omega
216      rw [ite_eq_right hrhs]
217      rcases hw with hw | hw <;> subst hw
218      · rw [ite_eq_left rfl] at hpp; subst hpp
219        ggsimp [hs, hmo, hne, f1, f3, c1, c2, r1, r2]
220      · rw [ite_eq_right (by decide)] at hpp; subst hpp
221        ggsimp [hs, hmo, hne, f1, f3, c1, c2, r1, r2, eT, hSC]
222    · obtain ⟨q5, f5, hq5, hq5i⟩ := c5 r1 r2
223      have hrhs : SeekOk (seekT s offset whence) := ⟨by omega, by omega⟩
224      rw [ite_eq_left hrhs, ← hq5]
225      rcases hw with hw | hw <;> subst hw
226      · rw [ite_eq_left rfl] at hpp; subst hpp
227        ggsimp [hs, hmo, hne, f1, f3, c1, c2, r1, r2, f5, seekTail]
228      · rw [ite_eq_right (by decide)] at hpp; subst hpp
229        ggsimp [hs, hmo, hne, f1, f3, c1, c2, r1, r2, eT, hSC, f5, seekTail]
230
231/-- `gzseek64` on a writer (preconditions as in `gzseek64_head`): the result is the target `t` and `skip` becomes `t - x.pos` when `t` is in range and not before `x.pos`; otherwise -1 and nothing changes. The world is unchanged. -/
232theorem gzseek64_write_run {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
233    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
234    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (hm : s.mode = GZ_WRITE) (offset : Int64) (fs : Fs) :
235    gzseek64 (hnd gs) offset whence fs h =
236      if SeekOk (seekT s offset whence) ∧ s.x.pos.toInt ≤ seekT s offset whence then
237        Except.ok ((Int64.ofInt (seekT s offset whence), fs),
238          setS h gs { s with skip := Int64.ofInt (seekT s offset whence - s.x.pos.toInt) })
239      else Except.ok ((-1, fs), h) := by
240  have hs := hG.hs
241  have hOM := OffMax_val
242  have hb0 := seekBase_nonneg hP hnn whence
243  have hb1 := seekBase_le hP whence
244  have hne : ¬(¬ s.err = Z_OK ∧ ¬ s.err = Z_BUF_ERROR) := by rcases he with h1 | h1 <;> simp [h1]
245  have hWR : GZ_WRITE ≠ GZ_READ := by decide
246  have hSC : SEEK_CUR ≠ SEEK_SET := by decide
247  have eT : i64add s.x.pos (if s.past = 0 then s.skip else 0) h = .ok (Int64.ofInt (tellPos s), h) := by
248    have := tell_add_run hP h
249    by_cases hh : s.past = 0
250    · simpa [hh] using this
251    · simpa [hh] using this
252  obtain ⟨p0, hp0⟩ : ∃ p0 : Int64, p0 = Int64.ofInt (seekBase s whence) := ⟨_, rfl⟩
253  have hp0i : p0.toInt = seekBase s whence := by rw [hp0]; exact i64_toInt_ofInt (by omega) (by omega)
254  obtain ⟨q1, q3, f1, f3, c1, c2, c5⟩ := seek_checks (p0 := p0) offset (by omega) h
255  rw [hp0i] at c1 c2 c5
256  have hbt : seekBase s whence + offset.toInt = seekT s offset whence := rfl
257  rw [hbt] at c1 c2 c5
258  have hpf : True ∧ (whence = SEEK_CUR → i64add s.x.pos (if s.past != 0 then 0 else s.skip) h =
259      .ok (Int64.ofInt (tellPos s), h)) := ⟨trivial, fun _ => tell_add_run hP h⟩
260  have hpp : p0 = if whence = SEEK_SET then 0 else Int64.ofInt (tellPos s) := by
261    rw [hp0]; unfold seekBase; split
262    · rfl
263    · rfl
264  unfold gzseek64
265  have hcases : (whence = SEEK_SET ∨ whence = SEEK_CUR) := hw
266  by_cases r1 : OffMax < seekT s offset whence
267  · have hrhs : ¬ (SeekOk (seekT s offset whence) ∧ s.x.pos.toInt ≤ seekT s offset whence) := fun hh => by
268      have := hh.1.2; omega
269    rw [ite_eq_right hrhs]
270    rcases hw with hw | hw <;> subst hw
271    · rw [ite_eq_left rfl] at hpp; subst hpp
272      ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1]
273    · rw [ite_eq_right (by decide)] at hpp; subst hpp
274      ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1, eT, hSC]
275  · by_cases r2 : seekT s offset whence < 0
276    · have hrhs : ¬ (SeekOk (seekT s offset whence) ∧ s.x.pos.toInt ≤ seekT s offset whence) := fun hh => by
277        have := hh.1.1; omega
278      rw [ite_eq_right hrhs]
279      rcases hw with hw | hw <;> subst hw
280      · rw [ite_eq_left rfl] at hpp; subst hpp
281        ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1, r2]
282      · rw [ite_eq_right (by decide)] at hpp; subst hpp
283        ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1, r2, eT, hSC]
284    · obtain ⟨q5, f5, hq5, hq5i⟩ := c5 r1 r2
285      have c6 : q5 < s.x.pos ↔ seekT s offset whence < s.x.pos.toInt := by rw [i64_lt_iff, hq5i]
286      have hlt := s.x.pos.toInt_lt
287      have f7 := i64sub_run q5 s.x.pos (by rw [hq5i]; omega) (by rw [hq5i]; omega) h
288      rw [hq5i] at f7
289      by_cases r3 : seekT s offset whence < s.x.pos.toInt
290      · have hrhs : ¬ (SeekOk (seekT s offset whence) ∧ s.x.pos.toInt ≤ seekT s offset whence) := fun hh => by
291          have := hh.2; omega
292        rw [ite_eq_right hrhs]
293        rcases hw with hw | hw <;> subst hw
294        · rw [ite_eq_left rfl] at hpp; subst hpp
295          ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1, r2, f5, c6, r3]
296        · rw [ite_eq_right (by decide)] at hpp; subst hpp
297          ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1, r2, eT, hSC, f5, c6, r3]
298      · have hrhs : SeekOk (seekT s offset whence) ∧ s.x.pos.toInt ≤ seekT s offset whence :=
299          ⟨⟨by omega, by omega⟩, by omega⟩
300        rw [ite_eq_left hrhs, ← hq5]
301        rcases hw with hw | hw <;> subst hw
302        · rw [ite_eq_left rfl] at hpp; subst hpp
303          ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1, r2, f5, c6, r3, f7]
304        · rw [ite_eq_right (by decide)] at hpp; subst hpp
305          ggsimp [hs, hm, hne, hWR, f1, f3, c1, c2, r1, r2, eT, hSC, f5, c6, r3, f7]
306
307
308theorem OffMax_lt : OffMax < 9223372036854775808 := by decide
309
310/-- **Seek inside the raw area** (`how == COPY`, a reader that passes the file through). The descriptor moves by
311`t - x.pos - have` (the bytes read ahead are given back); if `lseek` fails nothing changes but the world; otherwise
312the buffer is dropped (`have = 0`), the error is cleared (the message block is freed), `x.pos = t`. The hypothesis
313`hraw` is the signed-overflow condition of the C expression `pos - x.pos - have` (it fails only for positions within
314`2^32` of `2^63`). -/
315theorem seekTail_copy {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hm : s.mode = GZ_READ)
316    (hcopy : s.how = GZ_COPY) (hnn : 0 ≤ s.x.pos.toInt) {t : Int} (ht : SeekOk t)
317    (hraw : -9223372036854775808 ≤ t - s.x.pos.toInt - s.x.«have».toNat) (fs : Fs) :
318    seekTail (hnd gs) s (Int64.ofInt t) fs h =
319      if (lseekRes s.fd (Int64.ofInt (t - s.x.pos.toInt - s.x.«have».toNat)) SEEK_CUR fs).1 = -1 then
320        Except.ok ((-1, (lseekRes s.fd (Int64.ofInt (t - s.x.pos.toInt - s.x.«have».toNat)) SEEK_CUR fs).2), h)
321      else
322        Except.ok ((Int64.ofInt t, (lseekRes s.fd (Int64.ofInt (t - s.x.pos.toInt - s.x.«have».toNat)) SEEK_CUR fs).2),
323          setS (freeStr h s.msg) gs (copyRec s t)) := by
324  have hs := hG.hs
325  have hOM := OffMax_val
326  have hhv := s.x.«have».toNat_lt
327  have hlt := s.x.pos.toInt_lt
328  have hqi : (Int64.ofInt t).toInt = t := i64_toInt_ofInt (by have := ht.1; omega) (by have := ht.2; omega)
329  obtain ⟨q2, hq2d⟩ : ∃ q2 : Int64, q2 = Int64.ofInt (t - s.x.pos.toInt) := ⟨_, rfl⟩
330  have hq2 : q2.toInt = t - s.x.pos.toInt := by
331    rw [hq2d]; exact i64_toInt_ofInt (by have := ht.1; omega) (by have := ht.2; omega)
332  have fa : i64sub (Int64.ofInt t) s.x.pos h = .ok (q2, h) := by
333    have := i64sub_run (Int64.ofInt t) s.x.pos (by rw [hqi]; have := ht.1; omega) (by rw [hqi]; have := ht.2; omega) h
334    rw [hqi, ← hq2d] at this; exact this
335  obtain ⟨q3, hq3d⟩ : ∃ q3 : Int64, q3 = Int64.ofInt (t - s.x.pos.toInt - s.x.«have».toNat) := ⟨_, rfl⟩
336  have fb : i64sub q2 (u32ToI64 s.x.«have») h = .ok (q3, h) := by
337    have := i64sub_run q2 (u32ToI64 s.x.«have») (by rw [hq2, u32ToI64_toInt]; omega)
338      (by rw [hq2, u32ToI64_toInt]; have := ht.2; omega) h
339    rw [hq2, u32ToI64_toInt, ← hq3d] at this; exact this
340  obtain ⟨q, hqd⟩ : ∃ q : Int64, q = Int64.ofInt t := ⟨_, rfl⟩
341  rw [← hqd] at fa ⊢
342  rw [← hq3d]
343  unfold seekTail
344  by_cases hl : (lseekRes s.fd q3 SEEK_CUR fs).1 = -1
345  · rw [ite_eq_left hl]
346    ggsimp [hs, hm, hcopy, fa, fb, hl]
347  · rw [ite_eq_right hl]
348    rcases hG.msg with hn | ⟨mb, hmm, a, ha, hro, k, hz⟩
349    · ggsimp [hs, hm, hcopy, fa, fb, hl, hn, gz_error, copyRec, ← hqd]
350    · have hne : s.err ≠ Z_MEM_ERROR := fun h' => by have := hG.memerr h'; rw [hmm] at this; cases this
351      ggsimp [hs, hm, hcopy, fa, fb, hl, hmm, hne, freeB_blk_setS ha hro, freeB_blk_run ha hro, gz_error, copyRec, ← hqd]
352
353
354theorem space_get_setS (h : Heap) (gs : Nat) (s' : GzState) (r : BRef) : Space.get (setS h gs s') r = Space.get h r := by
355  cases r <;> rfl
356
357theorem i64_toU32_toNat (x : Int64) (hx : 0 ≤ x.toInt) : x.toUInt64.toUInt32.toNat = x.toInt.toNat % 2 ^ 32 := by
358  have h0 : (0 : Int64) ≤ x := by rw [Int64.le_iff_toInt_le]; simpa using hx
359  rw [UInt64.toNat_toUInt32, Int64.toNat_toUInt64_of_le h0, Int64.toNat_toInt]
360
361/-- `x.next + n` for `n ≤ x.have`: the pointer advanced by `n`. -/
362theorem next_add_run {h : Heap} {s : GzState} (hN : NextOk h s) {n : Nat} (hn : n ≤ s.x.«have».toNat) (gs : Nat) (s' : GzState) :
363    Ptr.add s.x.next (n : Int) (setS h gs s') = .ok (padv s.x.next n, setS h gs s') := by
364  rcases hN.1 with ⟨hnl, hh0⟩ | ⟨r, a, off, hr, hg, hle⟩
365  · have : n = 0 := by rw [hh0] at hn; simpa using hn
366    subst this
367    rw [hnl]
368    simp [Ptr.add, padv]
369  · rw [hr]
370    have hg' : Space.get (setS h gs s') r = some a := by rw [space_get_setS]; exact hg
371    have := Ptr.add_nat_ok (R := BRef) (h := setS h gs s') hg' (n := n) (off := off) (by omega)
372    exact this
373
374
375/-- The part of `gzseek64` after the checks, for a reader that is not in the raw area, with the target not before `x.pos`, `x.pos >= 0` and `NextOk`: the buffered bytes before the target are consumed (`have`, `next`, `pos` advance by `n`), the rest of the distance becomes `skip`; the result is the target; no fault. -/
376theorem seekTail_fwd {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hm : s.mode = GZ_READ)
377    (hhow : s.how ≠ GZ_COPY) (hnn : 0 ≤ s.x.pos.toInt) {t : Int} (ht : SeekOk t) (hfw : s.x.pos.toInt ≤ t)
378    (hN : NextOk h s) (fs : Fs) :
379    seekTail (hnd gs) s (Int64.ofInt t) fs h = Except.ok ((Int64.ofInt t, fs), setS h gs (skipFwd s t)) := by
380  have hs := hG.hs
381  have hOM := OffMax_val
382  have ht1 := ht.1
383  have ht2 := ht.2
384  have hhv := s.x.«have».toNat_lt
385  have hlt := s.x.pos.toInt_lt
386  have hqi : (Int64.ofInt t).toInt = t := i64_toInt_ofInt (by have := ht.1; omega) (by have := ht.2; omega)
387  obtain ⟨q, hqd⟩ : ∃ q : Int64, q = Int64.ofInt t := ⟨_, rfl⟩
388  have hqi' : q.toInt = t := by rw [hqd]; exact hqi
389  obtain ⟨q2, hq2d⟩ : ∃ q2 : Int64, q2 = Int64.ofInt (t - s.x.pos.toInt) := ⟨_, rfl⟩
390  have hq2 : q2.toInt = t - s.x.pos.toInt := by
391    rw [hq2d]; exact i64_toInt_ofInt (by have := ht.1; omega) (by have := ht.2; omega)
392  have fa : ∀ h', i64sub q s.x.pos h' = .ok (q2, h') := fun h' => by
393    have := i64sub_run q s.x.pos (by rw [hqi']; have := ht.1; omega) (by rw [hqi']; have := ht.2; omega) h'
394    rw [hqi', ← hq2d] at this; exact this
395  have c6 : ¬ (q < s.x.pos) := by rw [i64_lt_iff, hqi']; omega
396  have c : q2 < u32ToI64 s.x.«have» ↔ t - s.x.pos.toInt < s.x.«have».toNat := by
397    rw [i64_lt_iff, hq2, u32ToI64_toInt]
398  obtain ⟨nU, hA, hB, hnU, hn1, hn2⟩ : ∃ nU : UInt32, (q2 < u32ToI64 s.x.«have» → nU = q2.toUInt64.toUInt32) ∧
399      (¬ q2 < u32ToI64 s.x.«have» → nU = s.x.«have») ∧ nU.toNat = fwdN s (t - s.x.pos.toInt) ∧
400      nU.toNat ≤ s.x.«have».toNat ∧ (nU.toNat : Int) ≤ t - s.x.pos.toInt := by
401    by_cases hc : q2 < u32ToI64 s.x.«have»
402    · have hc' := c.mp hc
403      have h0 : 0 ≤ q2.toInt := by rw [hq2]; have := ht.1; omega
404      have e := i64_toU32_toNat q2 h0
405      rw [hq2] at e
406      have hl : (t - s.x.pos.toInt).toNat < 2 ^ 32 := by omega
407      rw [Nat.mod_eq_of_lt hl] at e
408      refine ⟨q2.toUInt64.toUInt32, fun _ => rfl, fun hh => absurd hc hh, ?_, ?_, ?_⟩
409      · rw [e]; unfold fwdN; rw [ite_eq_left hc']
410      · rw [e]; omega
411      · rw [e]; omega
412    · have hc' : ¬ (t - s.x.pos.toInt < s.x.«have».toNat) := fun hh => hc (c.mpr hh)
413      refine ⟨s.x.«have», fun hh => absurd hh hc, fun _ => rfl, ?_, Nat.le_refl _, ?_⟩
414      · unfold fwdN; rw [ite_eq_right hc']
415      · omega
416  have fn := next_add_run hN hn1 gs
417  have fpos : ∀ h', i64add s.x.pos (u32ToI64 nU) h' =
418      .ok (Int64.ofInt (s.x.pos.toInt + nU.toNat), h') := fun h' => by
419    have := i64add_run s.x.pos (u32ToI64 nU) (by rw [u32ToI64_toInt]; omega) (by rw [u32ToI64_toInt]; have := ht.2; omega) h'
420    rw [u32ToI64_toInt] at this; exact this
421  have fsk : ∀ h', i64sub q2 (u32ToI64 nU) h' = .ok (Int64.ofInt ((t - s.x.pos.toInt) - nU.toNat), h') := fun h' => by
422    have := i64sub_run q2 (u32ToI64 nU) (by rw [hq2, u32ToI64_toInt]; omega) (by rw [hq2, u32ToI64_toInt]; omega) h'
423    rw [hq2, u32ToI64_toInt] at this; exact this
424  rw [← hqd]
425  unfold seekTail
426  ggsimp [hs, hm, hhow, fa, c6, GT_OFF_run]
427  by_cases hc : q2 < u32ToI64 s.x.«have»
428  · rw [ite_eq_left hc, ← hA hc]
429    ggsimp [hs, fn, fpos, fsk, skipFwd, ← hnU]
430  · rw [ite_eq_right hc, ← hB hc]
431    ggsimp [hs, fn, fpos, fsk, skipFwd, ← hnU]
432
433
434theorem freeStr_get_other (h : Heap) (p : BPtr) (r : BRef) (hr : ∀ b, r ≠ .blk b) :
435    Space.get (freeStr h p) r = Space.get h r := by
436  cases r with
437  | blk b => exact absurd rfl (hr b)
438  | _ => cases p with
439    | null => rfl
440    | ref q o => cases q <;> rfl
441
442/-- `x.next + n` after `gz_reset` freed the message block: the pointer does not point into it. -/
443theorem next_add_run_reset {h : Heap} {s : GzState} (hN : NextOk h s) {n : Nat} (hn : n ≤ s.x.«have».toNat) (gs : Nat)
444    (s' : GzState) :
445    Ptr.add s.x.next (n : Int) (setS (freeStr h s.msg) gs s') = .ok (padv s.x.next n, setS (freeStr h s.msg) gs s') := by
446  rcases hN.1 with ⟨hnl, hh0⟩ | ⟨r, a, off, hr, hg, hle⟩
447  · have : n = 0 := by rw [hh0] at hn; simpa using hn
448    subst this
449    rw [hnl]
450    simp [Ptr.add, padv]
451  · rw [hr]
452    have hg' : Space.get (setS (freeStr h s.msg) gs s') r = some a := by
453      rw [space_get_setS]
454      cases r with
455      | blk b =>
456        have hb := hN.2 b (by rw [hr]; rfl)
457        cases hm : s.msg with
458        | null => simpa [hm, freeStr] using hg
459        | ref q o =>
460          cases q with
461          | blk mb =>
462            have : mb ≠ b := fun e => hb (by rw [hm, e]; rfl)
463            simpa [hm, freeStr, upd_ne _ _ _ _ this.symm] using hg
464          | _ => simpa [hm, freeStr] using hg
465      | str i => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
466      | dstate_depth i => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
467      | st_dist_code => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
468      | st_length_code => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
469    exact Ptr.add_nat_ok (R := BRef) (h := setS (freeStr h s.msg) gs s') hg' (n := n) (off := off) (by omega)
470
471
472theorem padv_zero (p : BPtr) : padv p 0 = p := by cases p <;> rfl
473
474/-- The part of `gzseek64` after the checks, for a reader that is not in the raw area, with the target before `x.pos` and `NextOk`: `gzrewind`, which fails with -1 and no change of the heap if `lseek` fails, otherwise the state is reset and the whole distance becomes `skip`; no fault. -/
475theorem seekTail_back {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hm : s.mode = GZ_READ)
476    (hhow : s.how ≠ GZ_COPY) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) (hnn : 0 ≤ s.x.pos.toInt) {t : Int}
477    (ht : SeekOk t) (hlt : t < s.x.pos.toInt) (hN : NextOk h s) (fs : Fs) :
478    seekTail (hnd gs) s (Int64.ofInt t) fs h =
479      if (lseekRes s.fd s.start SEEK_SET fs).1 = -1 then Except.ok ((-1, (lseekRes s.fd s.start SEEK_SET fs).2), h)
480      else Except.ok ((Int64.ofInt t, (lseekRes s.fd s.start SEEK_SET fs).2),
481        setS (freeStr h s.msg) gs (skipFwd (resetRec s) t)) := by
482  have hs := hG.hs
483  have hOM := OffMax_val
484  have ht1 := ht.1
485  have ht2 := ht.2
486  have hqi : (Int64.ofInt t).toInt = t := i64_toInt_ofInt (by omega) (by omega)
487  obtain ⟨q, hqd⟩ : ∃ q : Int64, q = Int64.ofInt t := ⟨_, rfl⟩
488  have hqi' : q.toInt = t := by rw [hqd]; exact hqi
489  have c6 : q < s.x.pos := by rw [i64_lt_iff, hqi']; omega
490  have hrw := gzrewind_run hG fs
491  have hcond : ¬ (s.mode ≠ GZ_READ ∨ (s.err ≠ Z_OK ∧ s.err ≠ Z_BUF_ERROR)) := by
492    rintro (h1 | ⟨h1, h2⟩)
493    · exact h1 hm
494    · rcases he with e | e <;> simp_all
495  rw [ite_eq_right hcond] at hrw
496  rw [← hqd]
497  unfold seekTail
498  by_cases hl : (lseekRes s.fd s.start SEEK_SET fs).1 = -1
499  · rw [ite_eq_left hl]
500    rw [ite_eq_left hl] at hrw
501    ggsimp [hs, hm, hhow, c6, hrw, hl]
502  · rw [ite_eq_right hl]
503    rw [ite_eq_right hl] at hrw
504    have z0 : (0 : Int64).toInt = 0 := rfl
505    have hq0 : ¬ q < 0 := by rw [i64_lt_iff, hqi', z0]; omega
506    have fa0 : ∀ h', i64sub q 0 h' = .ok (q, h') := fun h' => by
507      have := i64sub_run q 0 (by rw [hqi', z0]; omega) (by rw [hqi', z0]; omega) h'
508      simpa [hqi', ← hqd] using this
509    have fb0 : ∀ h', i64sub q (u32ToI64 0) h' = .ok (q, h') := fun h' => by
510      have := fa0 h'
511      simpa [u32ToI64] using this
512    have fc0 : ∀ h', i64add 0 (u32ToI64 0) h' = .ok (0, h') := fun h' => by
513      simp [i64add, i64chk, u32ToI64]
514    have fn0 : ∀ s', Ptr.add s.x.next 0 (setS (freeStr h s.msg) gs s') = .ok (s.x.next, setS (freeStr h s.msg) gs s') := fun s' => by
515      have := next_add_run_reset hN (n := 0) (Nat.zero_le _) gs s'
516      simpa [padv_zero] using this
517    have hGT : ¬ (u32ToI64 0 > q) := by rw [i64_gt_iff, u32ToI64_toInt, hqi']; simp; omega
518    have hnt : ¬ t < 0 := by omega
519    ggsimp [hs, hm, hhow, c6, hrw, hl, fa0, fb0, fc0, fn0, hGT, skipFwd, fwdN, resetRec, padv_zero, GT_OFF_run, hnt, ← hqd]
520
521
522/-! ### The invariants after a seek -/
523
524theorem fwdN_le {s : GzState} (off : Int) : fwdN s off ≤ s.x.«have».toNat := by
525  unfold fwdN; split <;> omega
526
527theorem fwdN_le_off {s : GzState} {off : Int} (h0 : 0 ≤ off) : (fwdN s off : Int) ≤ off := by
528  unfold fwdN; split <;> omega
529
530theorem GzGood.skipFwd {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (t : Int) :
531    GzGood (setS h gs (skipFwd s t)) gs (skipFwd s t) :=
532  GzGood.withRec hG (Or.inl rfl) rfl rfl rfl rfl (fun hh => hG.memerr hh)
533
534theorem PosInv_skipFwd {s : GzState} (hnn : 0 ≤ s.x.pos.toInt) {t : Int} (ht : SeekOk t) (hfw : s.x.pos.toInt ≤ t) :
535    PosInv (skipFwd s t) ∧ (s.past = 0 → tellPos (skipFwd s t) = t) ∧
536      (s.past ≠ 0 → tellPos (skipFwd s t) = s.x.pos.toInt + fwdN s (t - s.x.pos.toInt)) := by
537  have hOM := OffMax_val
538  have ht1 := ht.1
539  have ht2 := ht.2
540  have h1 := fwdN_le_off (s := s) (off := t - s.x.pos.toInt) (by omega)
541  have hlt := s.x.pos.toInt_lt
542  obtain ⟨P, hPd⟩ : ∃ P : Int64, P = Int64.ofInt (s.x.pos.toInt + (fwdN s (t - s.x.pos.toInt) : Int)) := ⟨_, rfl⟩
543  obtain ⟨K, hKd⟩ : ∃ K : Int64, K = Int64.ofInt (t - s.x.pos.toInt - (fwdN s (t - s.x.pos.toInt) : Int)) := ⟨_, rfl⟩
544  have e1 : P.toInt = s.x.pos.toInt + fwdN s (t - s.x.pos.toInt) := by
545    rw [hPd]; exact i64_toInt_ofInt (by omega) (by omega)
546  have e2 : K.toInt = t - s.x.pos.toInt - fwdN s (t - s.x.pos.toInt) := by
547    rw [hKd]; exact i64_toInt_ofInt (by omega) (by omega)
548  have hskip : (skipFwd s t).skip = K := by rw [hKd]; rfl
549  have hpos : (skipFwd s t).x.pos = P := by rw [hPd]; rfl
550  have hpast : (skipFwd s t).past = s.past := rfl
551  refine ⟨⟨?_, ?_, ?_⟩, ?_, ?_⟩
552  · rw [hskip, e2]; omega
553  · unfold tellPos tellSkip; rw [hpast, hpos]
554    by_cases hp : s.past != 0 <;> simp [hp, e1, hskip, e2] <;> omega
555  · unfold tellPos tellSkip; rw [hpast, hpos]
556    by_cases hp : s.past != 0 <;> simp [hp, e1, hskip, e2] <;> omega
557  · intro hp0
558    unfold tellPos tellSkip; rw [hpast, hpos]
559    simp [hp0, e1, hskip, e2]; omega
560  · intro hp0
561    unfold tellPos tellSkip; rw [hpast, hpos]
562    have : (s.past != 0) = true := by simpa using hp0
563    simp [this, e1]
564
565theorem NextOk_skipFwd {h : Heap} {s : GzState} (hN : NextOk h s) (t : Int) : NextOk h (skipFwd s t) := by
566  have hle := fwdN_le (s := s) (t - s.x.pos.toInt)
567  refine ⟨?_, ?_⟩
568  · rcases hN.1 with ⟨hnl, hh0⟩ | ⟨r, a, off, hr, hg, hl⟩
569    · have : fwdN s (t - s.x.pos.toInt) = 0 := by rw [hh0] at hle; simpa using hle
570      left; refine ⟨?_, ?_⟩
571      · show padv s.x.next _ = _; rw [hnl, this]; rfl
572      · show s.x.«have» - UInt32.ofNat _ = 0; rw [hh0, this]; rfl
573    · right
574      refine ⟨r, a, off + fwdN s (t - s.x.pos.toInt), ?_, hg, ?_⟩
575      · show padv s.x.next _ = _; rw [hr]; rfl
576      · show _ + (s.x.«have» - UInt32.ofNat _).toNat ≤ a.size
577        have hh := s.x.«have».toNat_lt
578        have hn32 : (UInt32.ofNat (fwdN s (t - s.x.pos.toInt))).toNat = fwdN s (t - s.x.pos.toInt) :=
579          UInt32.toNat_ofNat_of_lt' (by have hsz : UInt32.size = 4294967296 := rfl; omega)
580        have : (s.x.«have» - UInt32.ofNat (fwdN s (t - s.x.pos.toInt))).toNat = s.x.«have».toNat - fwdN s (t - s.x.pos.toInt) := by
581          rw [UInt32.toNat_sub_of_le _ _ (by rw [UInt32.le_iff_toNat_le, hn32]; omega), hn32]
582        rw [this]; omega
583  · intro b hb
584    have : (skipFwd s t).x.next = padv s.x.next (fwdN s (t - s.x.pos.toInt)) := rfl
585    rw [this] at hb
586    have hb' : ptrBlk s.x.next = some b := by
587      cases hp : s.x.next with
588      | null => rw [hp] at hb; simp [padv, ptrBlk] at hb
589      | ref r o => rw [hp] at hb; cases r <;> simp [padv, ptrBlk] at hb ⊢ <;> exact hb
590    have := hN.2 b hb'
591    exact this
592
593
594theorem get_after_free {h : Heap} {s : GzState} (hn : ∀ b, ptrBlk s.x.next = some b → ptrBlk s.msg ≠ some b)
595    {r : BRef} {off : Nat} (hr : s.x.next = .ref r off) {a : Array UInt8} (hg : Space.get h r = some a) (gs : Nat)
596    (z : GzState) : Space.get (setS (freeStr h s.msg) gs z) r = some a := by
597  rw [space_get_setS]
598  cases r with
599  | blk b =>
600    have hb := hn b (by rw [hr]; rfl)
601    cases hm : s.msg with
602    | null => simpa [hm, freeStr] using hg
603    | ref q o =>
604      cases q with
605      | blk mb =>
606        have : mb ≠ b := fun e => hb (by rw [hm, e]; rfl)
607        simpa [hm, freeStr, upd_ne _ _ _ _ this.symm] using hg
608      | _ => simpa [hm, freeStr] using hg
609  | str i => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
610  | dstate_depth i => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
611  | st_dist_code => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
612  | st_length_code => rw [freeStr_get_other _ _ _ (by intro b; simp)]; exact hg
613
614/-- After a call that frees the message block and leaves `x.next` alone, `x.next` is still usable. -/
615theorem NextOk_free {h : Heap} {s s' : GzState} (hN : NextOk h s) (gs : Nat) (z : GzState)
616    (hnx : s'.x.next = s.x.next) (hhv : s'.x.«have».toNat ≤ s.x.«have».toNat)
617    (hnull : s'.x.next = .null → s'.x.«have» = 0) (hm : s'.msg = .null) :
618    NextOk (setS (freeStr h s.msg) gs z) s' := by
619  refine ⟨?_, ?_⟩
620  · rcases hN.1 with ⟨hnl, hh0⟩ | ⟨r, a, off, hr, hg, hl⟩
621    · exact Or.inl ⟨by rw [hnx]; exact hnl, hnull (by rw [hnx]; exact hnl)⟩
622    · exact Or.inr ⟨r, a, off, by rw [hnx]; exact hr, get_after_free hN.2 hr hg gs z, by omega⟩
623  · intro b hb; rw [hm]; simp [ptrBlk]
624
625theorem PosInv_resetRec (s : GzState) : PosInv (resetRec s) ∧ tellPos (resetRec s) = 0 := by
626  have e1 : (resetRec s).skip = 0 := by unfold resetRec; split <;> rfl
627  have e2 : (resetRec s).x.pos = 0 := by unfold resetRec; split <;> rfl
628  have e3 : (resetRec s).past = 0 := by unfold resetRec; split <;> rfl
629  have z0 : (0 : Int64).toInt = 0 := rfl
630  have ht : tellPos (resetRec s) = 0 := by
631    unfold tellPos tellSkip; rw [e2, e3]; simp [z0, e1]
632  exact ⟨⟨by rw [e1, z0]; omega, by rw [ht]; omega, by rw [ht]; omega⟩, ht⟩
633
634theorem GzGood.afterCopy {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (t : Int) :
635    GzGood (setS (freeStr h s.msg) gs (copyRec s t)) gs (copyRec s t) := by
636  have h1 := GzGood.withRec (GzGood.afterErr hG Z_OK) (s' := copyRec s t) (Or.inl rfl) rfl rfl rfl rfl (fun _ => rfl)
637  simpa using h1
638
639theorem PosInv_copyRec (s : GzState) {t : Int} (ht : SeekOk t) : PosInv (copyRec s t) ∧ tellPos (copyRec s t) = t := by
640  have hOM := OffMax_val
641  have ht1 := ht.1
642  have ht2 := ht.2
643  have e1 : (copyRec s t).skip = 0 := rfl
644  have e2 : (copyRec s t).x.pos = Int64.ofInt t := rfl
645  have e3 : (copyRec s t).past = 0 := rfl
646  have z0 : (0 : Int64).toInt = 0 := rfl
647  have hq : (Int64.ofInt t).toInt = t := i64_toInt_ofInt (by omega) (by omega)
648  have htt : tellPos (copyRec s t) = t := by
649    unfold tellPos tellSkip; rw [e2, e3]; simp [z0, e1, hq, -Int64.toInt_ofInt]
650  exact ⟨⟨by rw [e1, z0]; omega, by rw [htt]; omega, by rw [htt]; omega⟩, htt⟩
651
652
653theorem sameCfg_refl (s : GzState) : SameCfg s s := ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩
654
655theorem SameCfg.trans {a b c : GzState} (h1 : SameCfg a b) (h2 : SameCfg b c) : SameCfg a c := by
656  obtain ⟨a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11⟩ := h1
657  obtain ⟨b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11⟩ := h2
658  exact ⟨b1.trans a1, b2.trans a2, b3.trans a3, b4.trans a4, b5.trans a5, b6.trans a6, b7.trans a7, b8.trans a8,
659    b9.trans a9, b10.trans a10, b11.trans a11⟩
660
661theorem sameCfg_resetRec (s : GzState) : SameCfg s (resetRec s) := by
662  unfold resetRec; split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩
663
664theorem sameCfg_skipFwd (s : GzState) (t : Int) : SameCfg s (skipFwd s t) :=
665  ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩
666
667theorem sameCfg_copyRec (s : GzState) (t : Int) : SameCfg s (copyRec s t) :=
668  ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩
669
670theorem NextOk_setS {h : Heap} {s' : GzState} (hN : NextOk h s') (gs : Nat) (z : GzState) : NextOk (setS h gs z) s' := by
671  refine ⟨?_, hN.2⟩
672  rcases hN.1 with hn | ⟨r, a, off, hr, hg, hl⟩
673  · exact Or.inl hn
674  · exact Or.inr ⟨r, a, off, hr, by rw [space_get_setS]; exact hg, hl⟩
675
676theorem seek_write_safe {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
677    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
678    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (hm : s.mode = GZ_WRITE) (hN : NextOk h s) (offset : Int64) (fs : Fs) :
679    ∃ r h' s', gzseek64 (hnd gs) offset whence fs h = .ok ((r, fs), h') ∧ SeekPost h gs s (seekT s offset whence) r h' s' := by
680  have hrun := gzseek64_write_run hG hP hnn he hw hm offset fs
681  have hOM := OffMax_val
682  by_cases hc : SeekOk (seekT s offset whence) ∧ s.x.pos.toInt ≤ seekT s offset whence
683  · rw [ite_eq_left hc] at hrun
684    obtain ⟨⟨ht1, ht2⟩, hfw⟩ := hc
685    have hlt := s.x.pos.toInt_lt
686    have eK : (Int64.ofInt (seekT s offset whence - s.x.pos.toInt)).toInt = seekT s offset whence - s.x.pos.toInt :=
687      i64_toInt_ofInt (by omega) (by omega)
688    obtain ⟨K, hKd⟩ : ∃ K : Int64, K = Int64.ofInt (seekT s offset whence - s.x.pos.toInt) := ⟨_, rfl⟩
689    rw [← hKd] at hrun
690    have eK' : K.toInt = seekT s offset whence - s.x.pos.toInt := by rw [hKd]; exact eK
691    have hG' := GzGood.withRec hG (s' := { s with skip := K }) (Or.inl rfl) rfl rfl rfl rfl (fun hh => hG.memerr hh)
692    have hts : ∀ pp : Int, tellPos { s with skip := K } = (if s.past != 0 then s.x.pos.toInt else s.x.pos.toInt + K.toInt) := by
693      intro _; unfold tellPos tellSkip; by_cases hp : s.past != 0 <;> simp [hp]
694    refine ⟨_, _, { s with skip := K }, hrun, hG', ⟨?_, ?_, ?_⟩, NextOk_setS hN gs _, ?_, Or.inr ⟨⟨ht1, ht2⟩, ?_, ?_, ?_⟩⟩
695    · exact eK' ▸ (by omega)
696    · rw [hts 0]; split <;> omega
697    · rw [hts 0]; split <;> omega
698    · exact ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩
699    · exact i64_toInt_ofInt (by omega) (by omega)
700    · intro hp0; rw [hts 0]; have : ¬ (s.past != 0) = true := by simpa using hp0
701      simp [this]; omega
702    · intro hp0; have : (s.past != 0) = true := by simpa using hp0
703      rw [hts 0]; simp [this]; omega
704  · rw [ite_eq_right hc] at hrun
705    exact ⟨_, _, s, hrun, hG, hP, hN, sameCfg_refl s, Or.inl ⟨rfl, rfl, rfl⟩⟩
706
707
708theorem skipFwd_pos {s : GzState} (hnn : 0 ≤ s.x.pos.toInt) {t : Int} (ht : SeekOk t) (hfw : s.x.pos.toInt ≤ t) :
709    (skipFwd s t).x.pos.toInt = s.x.pos.toInt + fwdN s (t - s.x.pos.toInt) ∧ (skipFwd s t).x.pos.toInt ≤ t := by
710  have hOM := OffMax_val
711  have ht1 := ht.1
712  have ht2 := ht.2
713  have h1 := fwdN_le_off (s := s) (off := t - s.x.pos.toInt) (by omega)
714  have hlt := s.x.pos.toInt_lt
715  have e : (skipFwd s t).x.pos.toInt = s.x.pos.toInt + fwdN s (t - s.x.pos.toInt) :=
716    i64_toInt_ofInt (by omega) (by omega)
717  exact ⟨e, by omega⟩
718
719theorem tell_past {s : GzState} (hp : s.past ≠ 0) : tellPos s = s.x.pos.toInt := by
720  have : (s.past != 0) = true := by simpa using hp
721  unfold tellPos tellSkip; simp [this]
722
723theorem seek_fwd_safe {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
724    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
725    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (hm : s.mode = GZ_READ) (hhow : s.how ≠ GZ_COPY) (hN : NextOk h s)
726    (offset : Int64) (fs : Fs) (ht : SeekOk (seekT s offset whence)) (hfw : s.x.pos.toInt ≤ seekT s offset whence) :
727    ∃ r h' s', gzseek64 (hnd gs) offset whence fs h = .ok ((r, fs), h') ∧ SeekPost h gs s (seekT s offset whence) r h' s' := by
728  have hhead := gzseek64_head hG hP hnn he hw offset fs
729  rw [ite_eq_left ht, seekTail_fwd hG hm hhow hnn ht hfw hN fs] at hhead
730  have hpp := PosInv_skipFwd hnn ht hfw
731  have hps := skipFwd_pos hnn ht hfw
732  have hq : (Int64.ofInt (seekT s offset whence)).toInt = seekT s offset whence :=
733    i64_toInt_ofInt (by have := ht.1; omega) (by have := ht.2; have := OffMax_val; omega)
734  refine ⟨_, _, skipFwd s (seekT s offset whence), hhead, GzGood.skipFwd hG _, hpp.1,
735    NextOk_setS (NextOk_skipFwd hN _) gs _, sameCfg_skipFwd _ _, Or.inr ⟨ht, hq, hpp.2.1, ?_⟩⟩
736  intro hp0
737  have hpast : (skipFwd s (seekT s offset whence)).past ≠ 0 := hp0
738  exact ⟨tell_past hpast, hps.2⟩
739
740
741theorem seek_copy_safe {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
742    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
743    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (hm : s.mode = GZ_READ) (hcopy : s.how = GZ_COPY) (hN : NextOk h s)
744    (offset : Int64) (fs : Fs) (ht : SeekOk (seekT s offset whence))
745    (hraw : -9223372036854775808 ≤ seekT s offset whence - s.x.pos.toInt - s.x.«have».toNat) :
746    ∃ r fs' h', gzseek64 (hnd gs) offset whence fs h = .ok ((r, fs'), h') ∧
747      (∃ s', SeekPost h gs s (seekT s offset whence) r h' s') := by
748  have hhead := gzseek64_head hG hP hnn he hw offset fs
749  rw [ite_eq_left ht, seekTail_copy hG hm hcopy hnn ht hraw fs] at hhead
750  have hq : (Int64.ofInt (seekT s offset whence)).toInt = seekT s offset whence :=
751    i64_toInt_ofInt (by have := ht.1; omega) (by have := ht.2; have := OffMax_val; omega)
752  by_cases hl : (lseekRes s.fd (Int64.ofInt (seekT s offset whence - s.x.pos.toInt - s.x.«have».toNat)) SEEK_CUR fs).1 = -1
753  · rw [ite_eq_left hl] at hhead
754    exact ⟨_, _, _, hhead, s, hG, hP, hN, sameCfg_refl s, Or.inl ⟨rfl, rfl, rfl⟩⟩
755  · rw [ite_eq_right hl] at hhead
756    have hpc := PosInv_copyRec s ht
757    refine ⟨_, _, _, hhead, copyRec s (seekT s offset whence), GzGood.afterCopy hG _, hpc.1,
758      NextOk_free hN gs _ rfl (by show (0 : UInt32).toNat ≤ _; simp) (fun _ => rfl) rfl, sameCfg_copyRec _ _,
759      Or.inr ⟨ht, hq, fun _ => hpc.2, fun hp0 => absurd rfl hp0⟩⟩
760
761theorem fwdN_reset (s : GzState) {t : Int} (ht : 0 ≤ t) : fwdN (resetRec s) (t - (resetRec s).x.pos.toInt) = 0 := by
762  have e1 : (resetRec s).x.«have» = 0 := by unfold resetRec; split <;> rfl
763  have e2 : (resetRec s).x.pos = 0 := by unfold resetRec; split <;> rfl
764  have z0 : (0 : Int64).toInt = 0 := rfl
765  unfold fwdN; rw [e1, e2, z0]; simp; omega
766
767theorem seek_back_safe {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
768    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
769    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (hm : s.mode = GZ_READ) (hhow : s.how ≠ GZ_COPY) (hN : NextOk h s)
770    (offset : Int64) (fs : Fs) (ht : SeekOk (seekT s offset whence)) (hlt : seekT s offset whence < s.x.pos.toInt) :
771    ∃ r fs' h', gzseek64 (hnd gs) offset whence fs h = .ok ((r, fs'), h') ∧
772      (∃ s', SeekPost h gs s (seekT s offset whence) r h' s') := by
773  have hhead := gzseek64_head hG hP hnn he hw offset fs
774  rw [ite_eq_left ht, seekTail_back hG hm hhow he hnn ht hlt hN fs] at hhead
775  have hq : (Int64.ofInt (seekT s offset whence)).toInt = seekT s offset whence :=
776    i64_toInt_ofInt (by have := ht.1; omega) (by have := ht.2; have := OffMax_val; omega)
777  by_cases hl : (lseekRes s.fd s.start SEEK_SET fs).1 = -1
778  · rw [ite_eq_left hl] at hhead
779    exact ⟨_, _, _, hhead, s, hG, hP, hN, sameCfg_refl s, Or.inl ⟨rfl, rfl, rfl⟩⟩
780  · rw [ite_eq_right hl] at hhead
781    have hz : (resetRec s).x.pos.toInt = 0 := by
782      have e2 : (resetRec s).x.pos = 0 := by unfold resetRec; split <;> rfl
783      rw [e2]; rfl
784    have hfw : (resetRec s).x.pos.toInt ≤ seekT s offset whence := by rw [hz]; exact ht.1
785    have hpp := PosInv_skipFwd (s := resetRec s) (by rw [hz]; exact Int.le_refl 0) ht hfw
786    have hF := fwdN_reset s ht.1
787    have hGr := GzGood.skipFwd (GzGood.afterReset hG) (seekT s offset whence)
788    simp only [setS_setS] at hGr
789    have hpast : (resetRec s).past = 0 := by unfold resetRec; split <;> rfl
790    refine ⟨_, _, _, hhead, skipFwd (resetRec s) (seekT s offset whence), hGr, hpp.1, ?_,
791      SameCfg.trans (sameCfg_resetRec s) (sameCfg_skipFwd _ _), Or.inr ⟨ht, hq, fun _ => hpp.2.1 hpast, fun hp0 => absurd hpast hp0⟩⟩
792    refine NextOk_free hN gs _ ?_ ?_ ?_ rfl
793    · show padv (resetRec s).x.next _ = _
794      rw [hF]; unfold resetRec; split <;> simp [padv_zero]
795    · show ((resetRec s).x.«have» - _).toNat ≤ _
796      have e1 : (resetRec s).x.«have» = 0 := by unfold resetRec; split <;> rfl
797      rw [e1]; rw [hF]; simp
798    · intro _
799      show (resetRec s).x.«have» - _ = 0
800      have e1 : (resetRec s).x.«have» = 0 := by unfold resetRec; split <;> rfl
801      rw [e1, hF]; rfl
802
803
804/-- `gzseek64(NULL, offset, whence)` returns -1; the world and the heap are unchanged. -/
805theorem gzseek64_null (offset : Int64) (whence : Int32) (fs : Fs) (h : Heap) :
806    gzseek64 none offset whence fs h = .ok ((-1, fs), h) := rfl
807
808/-- **gzseek64, safety and effect** (reachable states with `x.pos ≥ 0`: no `gzungetc` right after `gzopen`, the case
809the unpatched C faults in; `NextOk`: `x.next` is not dangling; `hraw`: positions within `2^32` of `2^63`).
810The call does not fault; on success the handle satisfies the invariants again; the result is `-1` and nothing but the
811world changed, or it is the target `t` (the position `gztell64` reports is `t`, except after a read past the end). -/
812theorem gzseek64_safe {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
813    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
814    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (hN : NextOk h s) (offset : Int64) (fs : Fs)
815    (hraw : s.mode = GZ_READ → s.how = GZ_COPY → SeekOk (seekT s offset whence) →
816      -9223372036854775808 ≤ seekT s offset whence - s.x.pos.toInt - s.x.«have».toNat) :
817    ∃ r fs' h' s', gzseek64 (hnd gs) offset whence fs h = .ok ((r, fs'), h') ∧
818      SeekPost h gs s (seekT s offset whence) r h' s' := by
819  by_cases ht : SeekOk (seekT s offset whence)
820  · rcases hG.mode with hm | hm
821    · by_cases hc : s.how = GZ_COPY
822      · obtain ⟨r, fs', h', e, s', hp⟩ := seek_copy_safe hG hP hnn he hw hm hc hN offset fs ht (hraw hm hc ht)
823        exact ⟨r, fs', h', s', e, hp⟩
824      · by_cases hb : seekT s offset whence < s.x.pos.toInt
825        · obtain ⟨r, fs', h', e, s', hp⟩ := seek_back_safe hG hP hnn he hw hm hc hN offset fs ht hb
826          exact ⟨r, fs', h', s', e, hp⟩
827        · obtain ⟨r, h', s', e, hp⟩ := seek_fwd_safe hG hP hnn he hw hm hc hN offset fs ht (by omega)
828          exact ⟨r, fs, h', s', e, hp⟩
829    · obtain ⟨r, h', s', e, hp⟩ := seek_write_safe hG hP hnn he hw hm hN offset fs
830      exact ⟨r, fs, h', s', e, hp⟩
831  · have hhead := gzseek64_head hG hP hnn he hw offset fs
832    rw [ite_eq_right ht] at hhead
833    exact ⟨_, _, _, s, hhead, hG, hP, hN, sameCfg_refl s, Or.inl ⟨rfl, rfl, rfl⟩⟩
834
835/-- `gzseek64` refuses a handle with a serious error, and a `whence` other than `SEEK_SET` and `SEEK_CUR`: `-1`,
836nothing changes. -/
837theorem gzseek64_refuse {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) {whence : Int32} (offset : Int64) (fs : Fs)
838    (hb : (s.err ≠ Z_OK ∧ s.err ≠ Z_BUF_ERROR) ∨ (whence ≠ SEEK_SET ∧ whence ≠ SEEK_CUR)) :
839    gzseek64 (hnd gs) offset whence fs h = .ok ((-1, fs), h) := by
840  have hs := hG.hs
841  have hmo : ¬ (s.mode ≠ GZ_READ ∧ s.mode ≠ GZ_WRITE) := by rcases hG.mode with h1 | h1 <;> simp [h1]
842  unfold gzseek64
843  rcases hb with hb | hb
844  · ggsimp [hs, hmo, hb]
845  · by_cases he : ¬ s.err = Z_OK ∧ ¬ s.err = Z_BUF_ERROR
846    · ggsimp [hs, hmo, he]
847    · ggsimp [hs, hmo, he, hb]
848
849/-- `gzseek` is `gzseek64` (`z_off_t` is `long`: the range check `ret == (z_off_t)ret` always holds). -/
850theorem gzseek_eq (file : GzFilePtr) (offset : Int64) (whence : Int32) (fs : Fs) (h : Heap) :
851    gzseek file offset whence fs h = gzseek64 file offset whence fs h := by
852  unfold gzseek
853  simp only [G_bind_app]
854  cases hh : gzseek64 file offset whence fs h with
855  | error e => rfl
856  | ok v => obtain ⟨⟨a, fs1⟩, h1⟩ := v; simp [G_pure_app]
857
858
859/-! ### The undefined behaviour of the unpatched C (R-1)
860
861`gzseek(file, offset, SEEK_CUR)` while `gztell` is negative (right after `gzungetc` at the start of a file): the check
862`offset > GZ_OFF_MAX - pos` computes `GZ_OFF_MAX - pos` with `pos < 0`, which overflows. And `gzseek(file, offset,
863SEEK_SET)` with a negative `x.pos` and `offset - x.pos >= 2^63`: the subtraction `pos - x.pos` overflows. Both are
864`Fault.ub` in the translation. -/
865
866/-- The undefined behaviour of the unpatched C (the negative-position seek report in `docs/findings.md`): `SEEK_CUR` while `gztell` is negative (after `gzungetc` at the start of a file) makes `GZ_OFF_MAX - pos` overflow: `Fault.ub`, for every `offset`. -/
867theorem gzseek64_cur_negpos_ub {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s)
868    (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) (hneg : tellPos s < 0) (hlo : -9223372036854775808 ≤ tellPos s)
869    (offset : Int64) (fs : Fs) :
870    gzseek64 (hnd gs) offset SEEK_CUR fs h = .error (.ub "signed overflow (long)") := by
871  have hs := hG.hs
872  have hOM := OffMax_val
873  have hne : ¬(¬ s.err = Z_OK ∧ ¬ s.err = Z_BUF_ERROR) := by rcases he with h1 | h1 <;> simp [h1]
874  have hmo : ¬ (s.mode ≠ GZ_READ ∧ s.mode ≠ GZ_WRITE) := by rcases hG.mode with h1 | h1 <;> simp [h1]
875  have hSC : SEEK_CUR ≠ SEEK_SET := by decide
876  have eT : i64add s.x.pos (if s.past = 0 then s.skip else 0) h = .ok (Int64.ofInt (tellPos s), h) := by
877    have := i64add_run s.x.pos (tellSkip s) (by unfold tellPos at hlo; omega) (by unfold tellPos at hneg; omega) h
878    by_cases hh : s.past = 0
879    · simpa [hh, tellSkip, tellPos] using this
880    · simpa [hh, tellSkip, tellPos] using this
881  have hp0 : (Int64.ofInt (tellPos s)).toInt = tellPos s := i64_toInt_ofInt hlo (by omega)
882  have hg := GZ_OFF_MAX_toInt
883  have fo : i64sub GZ_OFF_MAX (Int64.ofInt (tellPos s)) h = .error (.ub "signed overflow (long)") :=
884    i64sub_ub _ _ h (by rw [hp0, hg]; omega)
885  unfold gzseek64
886  ggsimp [hs, hmo, hne, hSC, eT, fo]
887
888/-- The undefined behaviour of the unpatched C: `SEEK_SET` at a negative `x.pos` with `offset - x.pos >= 2^63` makes `pos - x.pos` overflow: `Fault.ub`. -/
889theorem gzseek64_set_negpos_ub {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s)
890    (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) (hneg : s.x.pos.toInt < 0) (offset : Int64) (hoff : 0 ≤ offset.toInt)
891    (hbig : 9223372036854775808 ≤ offset.toInt - s.x.pos.toInt) (fs : Fs) :
892    gzseek64 (hnd gs) offset SEEK_SET fs h = .error (.ub "signed overflow (long)") := by
893  have hs := hG.hs
894  have hOM := OffMax_val
895  have hne : ¬(¬ s.err = Z_OK ∧ ¬ s.err = Z_BUF_ERROR) := by rcases he with h1 | h1 <;> simp [h1]
896  have hmo : ¬ (s.mode ≠ GZ_READ ∧ s.mode ≠ GZ_WRITE) := by rcases hG.mode with h1 | h1 <;> simp [h1]
897  have hSC : SEEK_SET ≠ SEEK_CUR := by decide
898  have z0 : (0 : Int64).toInt = 0 := rfl
899  have hlt := offset.toInt_lt
900  have hgt := s.x.pos.le_toInt
901  obtain ⟨q1, q3, f1, f3, c1, c2, c5⟩ := seek_checks (p0 := 0) offset (by rw [z0]; exact Int.le_refl 0) h
902  rw [z0] at c1 c2 c5
903  obtain ⟨q5, f5, hq5, hq5i⟩ := c5 (by omega) (by omega)
904  have c6 : ¬ (q5 < s.x.pos) := by rw [i64_lt_iff, hq5i]; omega
905  have fo : ∀ h', i64sub q5 s.x.pos h' = .error (.ub "signed overflow (long)") := fun h' =>
906    i64sub_ub _ _ h' (by rw [hq5i]; omega)
907  unfold gzseek64
908  have r1 : ¬ OffMax < offset.toInt := by omega
909  have r2 : ¬ offset.toInt < 0 := by omega
910  by_cases hc : s.mode = GZ_READ ∧ s.how = GZ_COPY
911  · ggsimp [hs, hmo, hne, hSC, f1, f3, f5, c1, c2, c6, fo, r1, r2, hc]
912  · ggsimp [hs, hmo, hne, hSC, f1, f3, f5, c1, c2, c6, fo, r1, r2, hc]
913
914
915/-- **The dangling `x.next` (R-4).** A reader that is not in the raw area executes `x.next += n` on every forward seek;
916when `x.next` points to an object that no longer exists (the caller's buffer after a direct decompression, freed by the
917caller), the translation faults, although `n` is 0 and nothing is read through the pointer. Every other fault of the
918translation is excluded by the hypotheses. -/
919theorem seekTail_dangling_ub {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hm : s.mode = GZ_READ)
920    (hhow : s.how ≠ GZ_COPY) (hnn : 0 ≤ s.x.pos.toInt) {t : Int} (ht : SeekOk t) (hfw : s.x.pos.toInt ≤ t)
921    {r : BRef} {off : Nat} (hr : s.x.next = .ref r off) (hd : Space.get h r = none) (fs : Fs) :
922    seekTail (hnd gs) s (Int64.ofInt t) fs h = .error (.ub "arithmetic on a dangling pointer") := by
923  have hs := hG.hs
924  have hOM := OffMax_val
925  have ht1 := ht.1
926  have ht2 := ht.2
927  have hqi : (Int64.ofInt t).toInt = t := i64_toInt_ofInt (by omega) (by omega)
928  obtain ⟨q, hqd⟩ : ∃ q : Int64, q = Int64.ofInt t := ⟨_, rfl⟩
929  have hqi' : q.toInt = t := by rw [hqd]; exact hqi
930  obtain ⟨q2, hq2d⟩ : ∃ q2 : Int64, q2 = Int64.ofInt (t - s.x.pos.toInt) := ⟨_, rfl⟩
931  have hlt := s.x.pos.toInt_lt
932  have hq2 : q2.toInt = t - s.x.pos.toInt := by
933    rw [hq2d]; exact i64_toInt_ofInt (by omega) (by omega)
934  have fa : ∀ h', i64sub q s.x.pos h' = .ok (q2, h') := fun h' => by
935    have := i64sub_run q s.x.pos (by rw [hqi']; omega) (by rw [hqi']; omega) h'
936    rw [hqi', ← hq2d] at this; exact this
937  have c6 : ¬ (q < s.x.pos) := by rw [i64_lt_iff, hqi']; omega
938  have fn : ∀ (k : Int) (z : GzState), Ptr.add s.x.next k (setS h gs z) = .error (.ub "arithmetic on a dangling pointer") := by
939    intro k z
940    rw [hr]
941    have : Space.get (setS h gs z) r = none := by rw [space_get_setS]; exact hd
942    simp [Ptr.add, this]
943  rw [← hqd]
944  unfold seekTail
945  ggsimp [hs, hm, hhow, fa, c6, GT_OFF_run, fn]
946  by_cases hc : q2 < u32ToI64 s.x.«have»
947  · ggsimp [hs, hc, fn]
948  · ggsimp [hs, hc, fn]
949
950
951/-- The dangling `x.next` (the borrowed seek-pointer report in `docs/findings.md`): a forward seek of a reader outside the raw area executes `x.next += n` on a pointer to an object that no longer exists: `Fault.ub`, although nothing is read through it. -/
952theorem gzseek64_dangling_next_ub {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hP : PosInv s)
953    (hnn : 0 ≤ s.x.pos.toInt) (he : s.err = Z_OK ∨ s.err = Z_BUF_ERROR) {whence : Int32}
954    (hw : whence = SEEK_SET ∨ whence = SEEK_CUR) (hm : s.mode = GZ_READ) (hhow : s.how ≠ GZ_COPY)
955    (offset : Int64) (fs : Fs) (ht : SeekOk (seekT s offset whence)) (hfw : s.x.pos.toInt ≤ seekT s offset whence)
956    {r : BRef} {off : Nat} (hr : s.x.next = .ref r off) (hd : Space.get h r = none) :
957    gzseek64 (hnd gs) offset whence fs h = .error (.ub "arithmetic on a dangling pointer") := by
958  rw [gzseek64_head hG hP hnn he hw offset fs, ite_eq_left ht, seekTail_dangling_ub hG hm hhow hnn ht hfw hr hd fs]
959
960/-- A seek inside the raw area faults exactly when `hraw` of `seekTail_copy` fails: the C expression
961`pos - x.pos - have` overflows (positions within `2^32` of `2^63`). -/
962theorem seekTail_copy_ub {h : Heap} {gs : Nat} {s : GzState} (hG : GzGood h gs s) (hm : s.mode = GZ_READ)
963    (hcopy : s.how = GZ_COPY) (hnn : 0 ≤ s.x.pos.toInt) {t : Int} (ht : SeekOk t)
964    (hraw : t - s.x.pos.toInt - s.x.«have».toNat < -9223372036854775808) (fs : Fs) :
965    seekTail (hnd gs) s (Int64.ofInt t) fs h = .error (.ub "signed overflow (long)") := by
966  have hs := hG.hs
967  have hOM := OffMax_val
968  have ht1 := ht.1
969  have ht2 := ht.2
970  have hhv := s.x.«have».toNat_lt
971  have hlt := s.x.pos.toInt_lt
972  have hqi : (Int64.ofInt t).toInt = t := i64_toInt_ofInt (by omega) (by omega)
973  obtain ⟨q2, hq2d⟩ : ∃ q2 : Int64, q2 = Int64.ofInt (t - s.x.pos.toInt) := ⟨_, rfl⟩
974  have hq2 : q2.toInt = t - s.x.pos.toInt := by
975    rw [hq2d]; exact i64_toInt_ofInt (by omega) (by omega)
976  have fa : i64sub (Int64.ofInt t) s.x.pos h = .ok (q2, h) := by
977    have := i64sub_run (Int64.ofInt t) s.x.pos (by rw [hqi]; omega) (by rw [hqi]; omega) h
978    rw [hqi, ← hq2d] at this; exact this
979  have fb : i64sub q2 (u32ToI64 s.x.«have») h = .error (.ub "signed overflow (long)") :=
980    i64sub_ub _ _ h (by rw [hq2, u32ToI64_toInt]; omega)
981  unfold seekTail
982  ggsimp [hs, hm, hcopy, fa, fb]
983
984end Zlib.Gzlib.Proofs
ZLIB × LEAN

Global contents

READING NOWgzlib.c.seek.lean