Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zlib.inflate: detect truncated input instead of looping #47

Merged
merged 2 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -671,8 +673,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 @@ -709,8 +712,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 @@ -775,14 +779,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"))
Comment on lines +127 to +132
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: it would probably suffice to report an error as soon as used_out is 0 but finished is false. Here, we're retrying a few times before failing, just in case.

in
uncompr 0 0;
inflate_end zs