Skip to content

Commit

Permalink
Merge pull request #47 from xavierleroy/fix-short-inflate
Browse files Browse the repository at this point in the history
`Zlib.inflate`: detect truncated input instead of looping
  • Loading branch information
xavierleroy authored Oct 4, 2024
2 parents 5afa6cf + 4d4a0d5 commit 1bce445
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 16 deletions.
30 changes: 18 additions & 12 deletions zip.ml
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,9 @@ let read_entry ifile e =
"wrong size for deflated entry (too much data)"));
Bytes.blit buf 0 res !out_pos len;
out_pos := !out_pos + len)
with Zlib.Error(_, _) ->
raise (Error(ifile.if_filename, e.filename, "decompression error"))
with Zlib.Error(_, msg) ->
raise (Error(ifile.if_filename, e.filename,
"decompression error: " ^ msg))
end;
if !out_pos <> Bytes.length res then
raise (Error(ifile.if_filename, e.filename,
Expand Down Expand Up @@ -463,8 +464,9 @@ let copy_entry_to_channel ifile e oc =
(fun buf len ->
output oc buf 0 len;
crc := Zlib.update_crc !crc buf 0 len)
with Zlib.Error(_, _) ->
raise (Error(ifile.if_filename, e.filename, "decompression error"))
with Zlib.Error(_, msg) ->
raise (Error(ifile.if_filename, e.filename,
"decompression error: " ^ msg))
end;
if !crc <> e.crc then
raise (Error(ifile.if_filename, e.filename, "CRC mismatch"))
Expand Down Expand Up @@ -677,8 +679,9 @@ let add_entry data ofile ?(comment = "")
output ofile.of_channel buf 0 n;
out_pos := !out_pos + n);
!out_pos
with Zlib.Error(_, _) ->
raise (Error(ofile.of_filename, name, "compression error")) in
with Zlib.Error(_, msg) ->
raise (Error(ofile.of_filename, name,
"compression error: " ^ msg)) in
let e' = update_entry ofile crc compr_size (String.length data) e in
ofile.of_entries <- e' :: ofile.of_entries

Expand Down Expand Up @@ -715,8 +718,9 @@ let copy_channel_to_entry ic ofile ?(comment = "")
output ofile.of_channel buf 0 n;
out_pos := !out_pos + n);
(!out_pos, !in_pos)
with Zlib.Error(_, _) ->
raise (Error(ofile.of_filename, name, "compression error")) in
with Zlib.Error(_, msg) ->
raise (Error(ofile.of_filename, name,
"compression error: " ^ msg)) in
let e' = update_entry ofile !crc compr_size uncompr_size e in
ofile.of_entries <- e' :: ofile.of_entries

Expand Down Expand Up @@ -781,14 +785,16 @@ let add_entry_generator ofile ?(comment = "")
send buf pos len;
uncompr_size := !uncompr_size + len;
crc := Zlib.update_crc !crc buf pos len
with Zlib.Error(_, _) ->
raise (Error(ofile.of_filename, name, "compression error"))
with Zlib.Error(_, msg) ->
raise (Error(ofile.of_filename, name,
"compression error: " ^ msg))
),
(fun () ->
check ();
try
flush ();
finish ()
with Zlib.Error(_, _) ->
raise (Error(ofile.of_filename, name, "compression error"))
with Zlib.Error(_, msg) ->
raise (Error(ofile.of_filename, name,
"compression error: " ^ msg))
)
16 changes: 12 additions & 4 deletions zlib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -106,22 +106,30 @@ let uncompress ?(header = true) refill flush =
let rec uncompr inpos inavail =
if inavail = 0 then begin
let incount = refill inbuf in
if incount = 0 then uncompr_finish true else uncompr 0 incount
if incount = 0 then uncompr_finish 0 else uncompr 0 incount
end else begin
let (finished, used_in, used_out) =
inflate zs inbuf inpos inavail outbuf 0 buffer_size Z_SYNC_FLUSH in
flush outbuf used_out;
if not finished then uncompr (inpos + used_in) (inavail - used_in)
end
and uncompr_finish first_finish =
and uncompr_finish num_round =
(* Gotcha: if there is no header, inflate requires an extra "dummy" byte
after the compressed stream in order to complete decompression
and return finished = true. *)
let dummy_byte = if first_finish && not header then 1 else 0 in
let dummy_byte = if num_round = 0 && not header then 1 else 0 in
let (finished, _, used_out) =
inflate zs inbuf 0 dummy_byte outbuf 0 buffer_size Z_SYNC_FLUSH in
flush outbuf used_out;
if not finished then uncompr_finish false
if finished then ()
else if used_out > 0 then uncompr_finish 1
else if num_round < 10 then uncompr_finish (num_round + 1)
else
(* Gotcha: truncated input can cause an infinite loop where
[inflate] doesn't produce output and never returns "finished".
Raise an error after too many calls to [inflate] that produced
no output. *)
raise(Error("Zlib.uncompress", "truncated input data"))
in
uncompr 0 0;
inflate_end zs

0 comments on commit 1bce445

Please sign in to comment.