emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] trunk r113804: * decompress.c: Fix bugs with large buffers


From: Paul Eggert
Subject: [Emacs-diffs] trunk r113804: * decompress.c: Fix bugs with large buffers and weird inputs.
Date: Sun, 11 Aug 2013 23:09:34 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 113804
revision-id: address@hidden
parent: address@hidden
committer: Paul Eggert <address@hidden>
branch nick: trunk
timestamp: Sun 2013-08-11 16:09:26 -0700
message:
  * decompress.c: Fix bugs with large buffers and weird inputs.
  
  Tune a bit.  Reindent as per usual Emacs style.
  (BUFFER_SIZE): Remove.
  (Fdecompress_gzipped_region): Do not mishandle input buffers with
  more than UINT_MAX bytes.  Decompress into the gap instead of into
  an auto buffer, as this should avoid copying.  Return nil if
  'inflate' returns Z_NEED_DICT, as we have no dictionary.  Do not
  set immediate_quit; we shouldn't trust zlib code that much.
modified:
  src/ChangeLog                  changelog-20091113204419-o5vbwnq5f7feedwu-1438
  src/decompress.c               decompress.c-20130811194033-wfhl0tqmmc36jfmu-1
=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2013-08-11 20:51:47 +0000
+++ b/src/ChangeLog     2013-08-11 23:09:26 +0000
@@ -1,3 +1,14 @@
+2013-08-11  Paul Eggert  <address@hidden>
+
+       * decompress.c: Fix bugs with large buffers and weird inputs.
+       Tune a bit.  Reindent as per usual Emacs style.
+       (BUFFER_SIZE): Remove.
+       (Fdecompress_gzipped_region): Do not mishandle input buffers with
+       more than UINT_MAX bytes.  Decompress into the gap instead of into
+       an auto buffer, as this should avoid copying.  Return nil if
+       'inflate' returns Z_NEED_DICT, as we have no dictionary.  Do not
+       set immediate_quit; we shouldn't trust zlib code that much.
+
 2013-08-11  Lars Magne Ingebrigtsen  <address@hidden>
 
        * decompress.c (Fdecompress_gzipped_region): Respect all zlib

=== modified file 'src/decompress.c'
--- a/src/decompress.c  2013-08-11 20:51:47 +0000
+++ b/src/decompress.c  2013-08-11 23:09:26 +0000
@@ -27,48 +27,48 @@
 #include "buffer.h"
 
 
-#define BUFFER_SIZE 16384
-
-struct decompress_unwind_data {
+struct decompress_unwind_data
+{
   ptrdiff_t old_point, start;
   z_stream *stream;
 };
 
 static void
-unwind_decompress (void *ddata) {
+unwind_decompress (void *ddata)
+{
   struct decompress_unwind_data *data = ddata;
   inflateEnd (data->stream);
-  /* Delete any uncompressed data already inserted and restore
-     point. */
-  if (data->start) {
-    del_range (data->start, PT);
-    SET_PT (data->old_point);
-  }
+
+  /* Delete any uncompressed data already inserted and restore point.  */
+  if (data->start)
+    {
+      del_range (data->start, PT);
+      SET_PT (data->old_point);
+    }
 }
 
 DEFUN ("decompress-gzipped-region", Fdecompress_gzipped_region,
        Sdecompress_gzipped_region,
        2, 2, 0,
        doc: /* Decompress a gzip-compressed region.
-The text in the region will be replaced by the decompressed data.
-On failure, nil is returned and the data is left in place.
-This function can only be called in unibyte buffers.*/)
+Replace the text in the region by the decompressed data.
+On failure, return nil and leave the data in place.
+This function can be called only in unibyte buffers.  */)
   (Lisp_Object start, Lisp_Object end)
 {
-  ptrdiff_t istart, iend, point = PT;
+  ptrdiff_t istart, iend, pos_byte;
   z_stream stream;
-  int decompressed;
-  char out[16384];
+  int inflate_status;
   struct decompress_unwind_data unwind_data;
   ptrdiff_t count = SPECPDL_INDEX ();
 
   validate_region (&start, &end);
 
   if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
-    error ("This function can only be called in unibyte buffers");
+    error ("This function can be called only in unibyte buffers");
 
   /* This is a unibyte buffer, so character positions and bytes are
-     the same. */
+     the same.  */
   istart = XINT (start);
   iend = XINT (end);
   move_gap_both (iend, iend);
@@ -79,49 +79,55 @@
   stream.avail_in = 0;
   stream.next_in = Z_NULL;
 
-  /* This magic number apparently means "this is gzip". */
+  /* This magic number apparently means "this is gzip".  */
   if (inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK)
     return Qnil;
 
-  /* We're inserting the decompressed data at the end of the
-     compressed data. */
-  SET_PT (iend);
-
-  stream.avail_in = iend - istart;
-  stream.next_in = (char *) BYTE_POS_ADDR (istart);
-
   unwind_data.start = iend;
   unwind_data.stream = &stream;
-  unwind_data.old_point = point;
+  unwind_data.old_point = PT;
+
   record_unwind_protect_ptr (unwind_decompress, &unwind_data);
 
-  immediate_quit = 1;
-
-  /* Run inflate() on input until the output buffer isn't full. */
-  do {
-    int result;
-    stream.avail_out = BUFFER_SIZE;
-    stream.next_out = out;
-    result = inflate (&stream, Z_NO_FLUSH);
-    if (result < 0) {
-      unbind_to (count, Qnil);
-      return Qnil;
+  /* Insert the decompressed data at the end of the compressed data.  */
+  SET_PT (iend);
+
+  pos_byte = istart;
+
+  /* Keep calling 'inflate' until it reports an error or end-of-input.  */
+  do
+    {
+      /* Maximum number of bytes that one 'inflate' call should read and write.
+        zlib requires that these values not exceed UINT_MAX.
+        Do not make avail_out too large, as that might unduly delay C-g.  */
+      ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
+      ptrdiff_t avail_out = min (1 << 14, UINT_MAX);
+
+      ptrdiff_t decompressed;
+
+      if (GAP_SIZE < avail_out)
+       make_gap (avail_out - GAP_SIZE);
+      stream.next_in = BYTE_POS_ADDR (pos_byte);
+      stream.avail_in = avail_in;
+      stream.next_out = GPT_ADDR;
+      stream.avail_out = avail_out;
+      inflate_status = inflate (&stream, Z_NO_FLUSH);
+      pos_byte += avail_in - stream.avail_in;
+      decompressed = avail_out - stream.avail_out;
+      insert_from_gap (decompressed, decompressed, 0);
+      QUIT;
     }
-
-    decompressed = BUFFER_SIZE - stream.avail_out;
-    insert_1_both (out, decompressed, decompressed, 0, 0, 0);
-    QUIT;
-  } while (stream.avail_out == 0);
-
-  immediate_quit = 0;
+  while (inflate_status == Z_OK);
+
+  if (inflate_status != Z_STREAM_END)
+    return unbind_to (count, Qnil);
 
   unwind_data.start = 0;
-  unbind_to (count, Qnil);
 
-  /* Delete the compressed data. */
+  /* Delete the compressed data.  */
   del_range (istart, iend);
 
-  return Qt;
+  return unbind_to (count, Qt);
 }
 
 


reply via email to

[Prev in Thread] Current Thread [Next in Thread]